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 // This can happen if the client timeouts somehow
1380 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1382 <<" has no associated player"<<std::endl;*/
1385 v3s16 pos = floatToInt(player->getPosition(), BS);
1387 core::map<u16, bool> removed_objects;
1388 core::map<u16, bool> added_objects;
1389 m_env.getRemovedActiveObjects(pos, radius,
1390 client->m_known_objects, removed_objects);
1391 m_env.getAddedActiveObjects(pos, radius,
1392 client->m_known_objects, added_objects);
1394 // Ignore if nothing happened
1395 if(removed_objects.size() == 0 && added_objects.size() == 0)
1397 //dstream<<"INFO: active objects: none changed"<<std::endl;
1401 std::string data_buffer;
1405 // Handle removed objects
1406 writeU16((u8*)buf, removed_objects.size());
1407 data_buffer.append(buf, 2);
1408 for(core::map<u16, bool>::Iterator
1409 i = removed_objects.getIterator();
1410 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 ServerActiveObject* obj = m_env.getActiveObject(id);
1416 // Add to data buffer for sending
1417 writeU16((u8*)buf, i.getNode()->getKey());
1418 data_buffer.append(buf, 2);
1420 // Remove from known objects
1421 client->m_known_objects.remove(i.getNode()->getKey());
1423 if(obj && obj->m_known_by_count > 0)
1424 obj->m_known_by_count--;
1427 // Handle added objects
1428 writeU16((u8*)buf, added_objects.size());
1429 data_buffer.append(buf, 2);
1430 for(core::map<u16, bool>::Iterator
1431 i = added_objects.getIterator();
1432 i.atEnd()==false; i++)
1435 u16 id = i.getNode()->getKey();
1436 ServerActiveObject* obj = m_env.getActiveObject(id);
1439 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1441 dstream<<"WARNING: "<<__FUNCTION_NAME
1442 <<": NULL object"<<std::endl;
1444 type = obj->getType();
1446 // Add to data buffer for sending
1447 writeU16((u8*)buf, id);
1448 data_buffer.append(buf, 2);
1449 writeU8((u8*)buf, type);
1450 data_buffer.append(buf, 1);
1453 data_buffer.append(serializeLongString(
1454 obj->getClientInitializationData()));
1456 data_buffer.append(serializeLongString(""));
1458 // Add to known objects
1459 client->m_known_objects.insert(i.getNode()->getKey(), false);
1462 obj->m_known_by_count++;
1466 SharedBuffer<u8> reply(2 + data_buffer.size());
1467 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1468 memcpy((char*)&reply[2], data_buffer.c_str(),
1469 data_buffer.size());
1471 m_con.Send(client->peer_id, 0, reply, true);
1473 dstream<<"INFO: Server: Sent object remove/add: "
1474 <<removed_objects.size()<<" removed, "
1475 <<added_objects.size()<<" added, "
1476 <<"packet size is "<<reply.getSize()<<std::endl;
1481 Collect a list of all the objects known by the clients
1482 and report it back to the environment.
1485 core::map<u16, bool> all_known_objects;
1487 for(core::map<u16, RemoteClient*>::Iterator
1488 i = m_clients.getIterator();
1489 i.atEnd() == false; i++)
1491 RemoteClient *client = i.getNode()->getValue();
1492 // Go through all known objects of client
1493 for(core::map<u16, bool>::Iterator
1494 i = client->m_known_objects.getIterator();
1495 i.atEnd()==false; i++)
1497 u16 id = i.getNode()->getKey();
1498 all_known_objects[id] = true;
1502 m_env.setKnownActiveObjects(whatever);
1508 Send object messages
1511 JMutexAutoLock envlock(m_env_mutex);
1512 JMutexAutoLock conlock(m_con_mutex);
1514 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1517 // Value = data sent by object
1518 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1520 // Get active object messages from environment
1523 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1527 core::list<ActiveObjectMessage>* message_list = NULL;
1528 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1529 n = buffered_messages.find(aom.id);
1532 message_list = new core::list<ActiveObjectMessage>;
1533 buffered_messages.insert(aom.id, message_list);
1537 message_list = n->getValue();
1539 message_list->push_back(aom);
1542 // Route data to every client
1543 for(core::map<u16, RemoteClient*>::Iterator
1544 i = m_clients.getIterator();
1545 i.atEnd()==false; i++)
1547 RemoteClient *client = i.getNode()->getValue();
1548 std::string reliable_data;
1549 std::string unreliable_data;
1550 // Go through all objects in message buffer
1551 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1552 j = buffered_messages.getIterator();
1553 j.atEnd()==false; j++)
1555 // If object is not known by client, skip it
1556 u16 id = j.getNode()->getKey();
1557 if(client->m_known_objects.find(id) == NULL)
1559 // Get message list of object
1560 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1561 // Go through every message
1562 for(core::list<ActiveObjectMessage>::Iterator
1563 k = list->begin(); k != list->end(); k++)
1565 // Compose the full new data with header
1566 ActiveObjectMessage aom = *k;
1567 std::string new_data;
1570 writeU16((u8*)&buf[0], aom.id);
1571 new_data.append(buf, 2);
1573 new_data += serializeString(aom.datastring);
1574 // Add data to buffer
1576 reliable_data += new_data;
1578 unreliable_data += new_data;
1582 reliable_data and unreliable_data are now ready.
1585 if(reliable_data.size() > 0)
1587 SharedBuffer<u8> reply(2 + reliable_data.size());
1588 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1589 memcpy((char*)&reply[2], reliable_data.c_str(),
1590 reliable_data.size());
1592 m_con.Send(client->peer_id, 0, reply, true);
1594 if(unreliable_data.size() > 0)
1596 SharedBuffer<u8> reply(2 + unreliable_data.size());
1597 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1598 memcpy((char*)&reply[2], unreliable_data.c_str(),
1599 unreliable_data.size());
1600 // Send as unreliable
1601 m_con.Send(client->peer_id, 0, reply, false);
1604 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1606 dstream<<"INFO: Server: Size of object message data: "
1607 <<"reliable: "<<reliable_data.size()
1608 <<", unreliable: "<<unreliable_data.size()
1613 // Clear buffered_messages
1614 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1615 i = buffered_messages.getIterator();
1616 i.atEnd()==false; i++)
1618 delete i.getNode()->getValue();
1622 } // enable_experimental
1625 Send queued-for-sending map edit events.
1628 while(m_unsent_map_edit_queue.size() != 0)
1630 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1632 if(event->type == MEET_ADDNODE)
1634 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1635 sendAddNode(event->p, event->n, event->already_known_by_peer);
1637 else if(event->type == MEET_REMOVENODE)
1639 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1640 sendRemoveNode(event->p, event->already_known_by_peer);
1642 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1644 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1645 setBlockNotSent(event->p);
1647 else if(event->type == MEET_OTHER)
1649 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1654 dstream<<"WARNING: Server: Unknown MapEditEvent "
1655 <<((u32)event->type)<<std::endl;
1663 Send object positions
1664 TODO: Get rid of MapBlockObjects
1667 float &counter = m_objectdata_timer;
1669 if(counter >= g_settings.getFloat("objectdata_interval"))
1671 JMutexAutoLock lock1(m_env_mutex);
1672 JMutexAutoLock lock2(m_con_mutex);
1674 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1676 SendObjectData(counter);
1684 TODO: Move to ServerEnvironment and utilize active block stuff
1687 //TimeTaker timer("Step node metadata");
1689 JMutexAutoLock envlock(m_env_mutex);
1690 JMutexAutoLock conlock(m_con_mutex);
1692 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1694 core::map<v3s16, MapBlock*> changed_blocks;
1695 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1697 // Use setBlockNotSent
1699 for(core::map<v3s16, MapBlock*>::Iterator
1700 i = changed_blocks.getIterator();
1701 i.atEnd() == false; i++)
1703 MapBlock *block = i.getNode()->getValue();
1705 for(core::map<u16, RemoteClient*>::Iterator
1706 i = m_clients.getIterator();
1707 i.atEnd()==false; i++)
1709 RemoteClient *client = i.getNode()->getValue();
1710 client->SetBlockNotSent(block->getPos());
1716 Trigger emergethread (it somehow gets to a non-triggered but
1717 bysy state sometimes)
1720 float &counter = m_emergethread_trigger_timer;
1726 m_emergethread.trigger();
1730 // Save map, players and auth stuff
1732 float &counter = m_savemap_timer;
1734 if(counter >= g_settings.getFloat("server_map_save_interval"))
1738 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1741 if(m_authmanager.isModified())
1742 m_authmanager.save();
1745 JMutexAutoLock lock(m_env_mutex);
1746 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1748 // Save only changed parts
1749 m_env.getMap().save(true);
1751 // Delete unused sectors
1752 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1753 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1754 if(deleted_count > 0)
1756 dout_server<<"Server: Unloaded "<<deleted_count
1757 <<" sectors from memory"<<std::endl;
1761 m_env.serializePlayers(m_mapsavedir);
1763 // Save environment metadata
1764 m_env.saveMeta(m_mapsavedir);
1770 void Server::Receive()
1772 DSTACK(__FUNCTION_NAME);
1773 u32 data_maxsize = 10000;
1774 Buffer<u8> data(data_maxsize);
1779 JMutexAutoLock conlock(m_con_mutex);
1780 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1783 // This has to be called so that the client list gets synced
1784 // with the peer list of the connection
1785 handlePeerChanges();
1787 ProcessData(*data, datasize, peer_id);
1789 catch(con::InvalidIncomingDataException &e)
1791 derr_server<<"Server::Receive(): "
1792 "InvalidIncomingDataException: what()="
1793 <<e.what()<<std::endl;
1795 catch(con::PeerNotFoundException &e)
1797 //NOTE: This is not needed anymore
1799 // The peer has been disconnected.
1800 // Find the associated player and remove it.
1802 /*JMutexAutoLock envlock(m_env_mutex);
1804 dout_server<<"ServerThread: peer_id="<<peer_id
1805 <<" has apparently closed connection. "
1806 <<"Removing player."<<std::endl;
1808 m_env.removePlayer(peer_id);*/
1812 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1814 DSTACK(__FUNCTION_NAME);
1815 // Environment is locked first.
1816 JMutexAutoLock envlock(m_env_mutex);
1817 JMutexAutoLock conlock(m_con_mutex);
1821 peer = m_con.GetPeer(peer_id);
1823 catch(con::PeerNotFoundException &e)
1825 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1826 <<peer_id<<" not found"<<std::endl;
1830 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1838 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1840 if(command == TOSERVER_INIT)
1842 // [0] u16 TOSERVER_INIT
1843 // [2] u8 SER_FMT_VER_HIGHEST
1844 // [3] u8[20] player_name
1845 // [23] u8[28] password <--- can be sent without this, from old versions
1847 if(datasize < 2+1+PLAYERNAME_SIZE)
1850 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1851 <<peer->id<<std::endl;
1853 // First byte after command is maximum supported
1854 // serialization version
1855 u8 client_max = data[2];
1856 u8 our_max = SER_FMT_VER_HIGHEST;
1857 // Use the highest version supported by both
1858 u8 deployed = core::min_(client_max, our_max);
1859 // If it's lower than the lowest supported, give up.
1860 if(deployed < SER_FMT_VER_LOWEST)
1861 deployed = SER_FMT_VER_INVALID;
1863 //peer->serialization_version = deployed;
1864 getClient(peer->id)->pending_serialization_version = deployed;
1866 if(deployed == SER_FMT_VER_INVALID)
1868 derr_server<<DTIME<<"Server: Cannot negotiate "
1869 "serialization version with peer "
1870 <<peer_id<<std::endl;
1879 char playername[PLAYERNAME_SIZE];
1880 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1882 playername[i] = data[3+i];
1884 playername[PLAYERNAME_SIZE-1] = 0;
1886 if(playername[0]=='\0')
1888 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1889 SendAccessDenied(m_con, peer_id,
1894 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1896 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1897 SendAccessDenied(m_con, peer_id,
1898 L"Name contains unallowed characters");
1903 char password[PASSWORD_SIZE];
1904 if(datasize == 2+1+PLAYERNAME_SIZE)
1906 // old version - assume blank password
1911 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1913 password[i] = data[23+i];
1915 password[PASSWORD_SIZE-1] = 0;
1918 std::string checkpwd;
1919 if(m_authmanager.exists(playername))
1921 checkpwd = m_authmanager.getPassword(playername);
1925 checkpwd = g_settings.get("default_password");
1928 if(password != checkpwd && checkpwd != "")
1930 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1931 <<": supplied invalid password for "
1932 <<playername<<std::endl;
1933 SendAccessDenied(m_con, peer_id, L"Invalid password");
1937 // Add player to auth manager
1938 if(m_authmanager.exists(playername) == false)
1940 derr_server<<DTIME<<"Server: adding player "<<playername
1941 <<" to auth manager"<<std::endl;
1942 m_authmanager.add(playername);
1943 m_authmanager.setPassword(playername, checkpwd);
1944 m_authmanager.setPrivs(playername,
1945 stringToPrivs(g_settings.get("default_privs")));
1946 m_authmanager.save();
1950 Player *player = emergePlayer(playername, password, peer_id);
1954 // DEBUG: Test serialization
1955 std::ostringstream test_os;
1956 player->serialize(test_os);
1957 dstream<<"Player serialization test: \""<<test_os.str()
1959 std::istringstream test_is(test_os.str());
1960 player->deSerialize(test_is);
1963 // If failed, cancel
1966 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1967 <<": failed to emerge player"<<std::endl;
1972 // If a client is already connected to the player, cancel
1973 if(player->peer_id != 0)
1975 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1976 <<" tried to connect to "
1977 "an already connected player (peer_id="
1978 <<player->peer_id<<")"<<std::endl;
1981 // Set client of player
1982 player->peer_id = peer_id;
1985 // Check if player doesn't exist
1987 throw con::InvalidIncomingDataException
1988 ("Server::ProcessData(): INIT: Player doesn't exist");
1990 /*// update name if it was supplied
1991 if(datasize >= 20+3)
1994 player->updateName((const char*)&data[3]);
1998 Answer with a TOCLIENT_INIT
2001 SharedBuffer<u8> reply(2+1+6+8);
2002 writeU16(&reply[0], TOCLIENT_INIT);
2003 writeU8(&reply[2], deployed);
2004 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2005 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2008 m_con.Send(peer_id, 0, reply, true);
2012 Send complete position information
2014 SendMovePlayer(player);
2019 if(command == TOSERVER_INIT2)
2021 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2022 <<peer->id<<std::endl;
2025 getClient(peer->id)->serialization_version
2026 = getClient(peer->id)->pending_serialization_version;
2029 Send some initialization data
2032 // Send player info to all players
2035 // Send inventory to player
2036 UpdateCrafting(peer->id);
2037 SendInventory(peer->id);
2041 Player *player = m_env.getPlayer(peer_id);
2042 SendPlayerHP(player);
2047 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2048 m_env.getTimeOfDay());
2049 m_con.Send(peer->id, 0, data, true);
2052 // Send information about server to player in chat
2053 SendChatMessage(peer_id, getStatusString());
2055 // Send information about joining in chat
2057 std::wstring name = L"unknown";
2058 Player *player = m_env.getPlayer(peer_id);
2060 name = narrow_to_wide(player->getName());
2062 std::wstring message;
2065 message += L" joined game";
2066 BroadcastChatMessage(message);
2072 if(peer_ser_ver == SER_FMT_VER_INVALID)
2074 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2075 " serialization format invalid or not initialized."
2076 " Skipping incoming command="<<command<<std::endl;
2080 Player *player = m_env.getPlayer(peer_id);
2083 derr_server<<"Server::ProcessData(): Cancelling: "
2084 "No player for peer_id="<<peer_id
2088 if(command == TOSERVER_PLAYERPOS)
2090 if(datasize < 2+12+12+4+4)
2094 v3s32 ps = readV3S32(&data[start+2]);
2095 v3s32 ss = readV3S32(&data[start+2+12]);
2096 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2097 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2098 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2099 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2100 pitch = wrapDegrees(pitch);
2101 yaw = wrapDegrees(yaw);
2102 player->setPosition(position);
2103 player->setSpeed(speed);
2104 player->setPitch(pitch);
2105 player->setYaw(yaw);
2107 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2108 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2109 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2111 else if(command == TOSERVER_GOTBLOCKS)
2124 u16 count = data[2];
2125 for(u16 i=0; i<count; i++)
2127 if((s16)datasize < 2+1+(i+1)*6)
2128 throw con::InvalidIncomingDataException
2129 ("GOTBLOCKS length is too short");
2130 v3s16 p = readV3S16(&data[2+1+i*6]);
2131 /*dstream<<"Server: GOTBLOCKS ("
2132 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2133 RemoteClient *client = getClient(peer_id);
2134 client->GotBlock(p);
2137 else if(command == TOSERVER_DELETEDBLOCKS)
2150 u16 count = data[2];
2151 for(u16 i=0; i<count; i++)
2153 if((s16)datasize < 2+1+(i+1)*6)
2154 throw con::InvalidIncomingDataException
2155 ("DELETEDBLOCKS length is too short");
2156 v3s16 p = readV3S16(&data[2+1+i*6]);
2157 /*dstream<<"Server: DELETEDBLOCKS ("
2158 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2159 RemoteClient *client = getClient(peer_id);
2160 client->SetBlockNotSent(p);
2163 else if(command == TOSERVER_CLICK_OBJECT)
2168 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2173 [2] u8 button (0=left, 1=right)
2178 u8 button = readU8(&data[2]);
2180 p.X = readS16(&data[3]);
2181 p.Y = readS16(&data[5]);
2182 p.Z = readS16(&data[7]);
2183 s16 id = readS16(&data[9]);
2184 //u16 item_i = readU16(&data[11]);
2186 MapBlock *block = NULL;
2189 block = m_env.getMap().getBlockNoCreate(p);
2191 catch(InvalidPositionException &e)
2193 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2197 MapBlockObject *obj = block->getObject(id);
2201 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2205 //TODO: Check that object is reasonably close
2210 InventoryList *ilist = player->inventory.getList("main");
2211 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2214 // Skip if inventory has no free space
2215 if(ilist->getUsedSlots() == ilist->getSize())
2217 dout_server<<"Player inventory has no free space"<<std::endl;
2222 Create the inventory item
2224 InventoryItem *item = NULL;
2225 // If it is an item-object, take the item from it
2226 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2228 item = ((ItemObject*)obj)->createInventoryItem();
2230 // Else create an item of the object
2233 item = new MapBlockObjectItem
2234 (obj->getInventoryString());
2237 // Add to inventory and send inventory
2238 ilist->addItem(item);
2239 UpdateCrafting(player->peer_id);
2240 SendInventory(player->peer_id);
2243 // Remove from block
2244 block->removeObject(id);
2247 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2252 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2258 [2] u8 button (0=left, 1=right)
2262 u8 button = readU8(&data[2]);
2263 u16 id = readS16(&data[3]);
2264 u16 item_i = readU16(&data[11]);
2266 ServerActiveObject *obj = m_env.getActiveObject(id);
2270 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2275 //TODO: Check that object is reasonably close
2277 // Left click, pick object up (usually)
2280 InventoryList *ilist = player->inventory.getList("main");
2281 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2284 // Skip if inventory has no free space
2285 if(ilist->getUsedSlots() == ilist->getSize())
2287 dout_server<<"Player inventory has no free space"<<std::endl;
2291 // Skip if object has been removed
2296 Create the inventory item
2298 InventoryItem *item = obj->createPickedUpItem();
2302 // Add to inventory and send inventory
2303 ilist->addItem(item);
2304 UpdateCrafting(player->peer_id);
2305 SendInventory(player->peer_id);
2307 // Remove object from environment
2308 obj->m_removed = true;
2313 Item cannot be picked up. Punch it instead.
2316 ToolItem *titem = NULL;
2317 std::string toolname = "";
2319 InventoryList *mlist = player->inventory.getList("main");
2322 InventoryItem *item = mlist->getItem(item_i);
2323 if(item && (std::string)item->getName() == "ToolItem")
2325 titem = (ToolItem*)item;
2326 toolname = titem->getToolName();
2330 u16 wear = obj->punch(toolname);
2334 bool weared_out = titem->addWear(wear);
2336 mlist->deleteItem(item_i);
2337 SendInventory(player->peer_id);
2343 else if(command == TOSERVER_GROUND_ACTION)
2351 [3] v3s16 nodepos_undersurface
2352 [9] v3s16 nodepos_abovesurface
2357 2: stop digging (all parameters ignored)
2358 3: digging completed
2360 u8 action = readU8(&data[2]);
2362 p_under.X = readS16(&data[3]);
2363 p_under.Y = readS16(&data[5]);
2364 p_under.Z = readS16(&data[7]);
2366 p_over.X = readS16(&data[9]);
2367 p_over.Y = readS16(&data[11]);
2368 p_over.Z = readS16(&data[13]);
2369 u16 item_i = readU16(&data[15]);
2371 //TODO: Check that target is reasonably close
2379 NOTE: This can be used in the future to check if
2380 somebody is cheating, by checking the timing.
2387 else if(action == 2)
2390 RemoteClient *client = getClient(peer->id);
2391 JMutexAutoLock digmutex(client->m_dig_mutex);
2392 client->m_dig_tool_item = -1;
2397 3: Digging completed
2399 else if(action == 3)
2401 // Mandatory parameter; actually used for nothing
2402 core::map<v3s16, MapBlock*> modified_blocks;
2404 u8 material = CONTENT_IGNORE;
2405 u8 mineral = MINERAL_NONE;
2407 bool cannot_remove_node = false;
2411 MapNode n = m_env.getMap().getNode(p_under);
2413 mineral = n.getMineral();
2414 // Get material at position
2416 // If not yet cancelled
2417 if(cannot_remove_node == false)
2419 // If it's not diggable, do nothing
2420 if(content_diggable(material) == false)
2422 derr_server<<"Server: Not finishing digging: "
2423 <<"Node not diggable"
2425 cannot_remove_node = true;
2428 // If not yet cancelled
2429 if(cannot_remove_node == false)
2431 // Get node metadata
2432 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2433 if(meta && meta->nodeRemovalDisabled() == true)
2435 derr_server<<"Server: Not finishing digging: "
2436 <<"Node metadata disables removal"
2438 cannot_remove_node = true;
2442 catch(InvalidPositionException &e)
2444 derr_server<<"Server: Not finishing digging: Node not found."
2445 <<" Adding block to emerge queue."
2447 m_emerge_queue.addBlock(peer_id,
2448 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2449 cannot_remove_node = true;
2452 // Make sure the player is allowed to do it
2453 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2455 dstream<<"Player "<<player->getName()<<" cannot remove node"
2456 <<" because privileges are "<<getPlayerPrivs(player)
2458 cannot_remove_node = true;
2462 If node can't be removed, set block to be re-sent to
2465 if(cannot_remove_node)
2467 derr_server<<"Server: Not finishing digging."<<std::endl;
2469 // Client probably has wrong data.
2470 // Set block not sent, so that client will get
2472 dstream<<"Client "<<peer_id<<" tried to dig "
2473 <<"node; but node cannot be removed."
2474 <<" setting MapBlock not sent."<<std::endl;
2475 RemoteClient *client = getClient(peer_id);
2476 v3s16 blockpos = getNodeBlockPos(p_under);
2477 client->SetBlockNotSent(blockpos);
2483 Send the removal to all other clients.
2484 - If other player is close, send REMOVENODE
2485 - Otherwise set blocks not sent
2487 core::list<u16> far_players;
2488 sendRemoveNode(p_under, peer_id, &far_players, 100);
2491 Update and send inventory
2494 if(g_settings.getBool("creative_mode") == false)
2499 InventoryList *mlist = player->inventory.getList("main");
2502 InventoryItem *item = mlist->getItem(item_i);
2503 if(item && (std::string)item->getName() == "ToolItem")
2505 ToolItem *titem = (ToolItem*)item;
2506 std::string toolname = titem->getToolName();
2508 // Get digging properties for material and tool
2509 DiggingProperties prop =
2510 getDiggingProperties(material, toolname);
2512 if(prop.diggable == false)
2514 derr_server<<"Server: WARNING: Player digged"
2515 <<" with impossible material + tool"
2516 <<" combination"<<std::endl;
2519 bool weared_out = titem->addWear(prop.wear);
2523 mlist->deleteItem(item_i);
2529 Add dug item to inventory
2532 InventoryItem *item = NULL;
2534 if(mineral != MINERAL_NONE)
2535 item = getDiggedMineralItem(mineral);
2540 std::string &dug_s = content_features(material).dug_item;
2543 std::istringstream is(dug_s, std::ios::binary);
2544 item = InventoryItem::deSerialize(is);
2550 // Add a item to inventory
2551 player->inventory.addItem("main", item);
2554 UpdateCrafting(player->peer_id);
2555 SendInventory(player->peer_id);
2561 (this takes some time so it is done after the quick stuff)
2563 m_ignore_map_edit_events = true;
2564 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2565 m_ignore_map_edit_events = false;
2568 Set blocks not sent to far players
2570 for(core::list<u16>::Iterator
2571 i = far_players.begin();
2572 i != far_players.end(); i++)
2575 RemoteClient *client = getClient(peer_id);
2578 client->SetBlocksNotSent(modified_blocks);
2585 else if(action == 1)
2588 InventoryList *ilist = player->inventory.getList("main");
2593 InventoryItem *item = ilist->getItem(item_i);
2595 // If there is no item, it is not possible to add it anywhere
2600 Handle material items
2602 if(std::string("MaterialItem") == item->getName())
2605 // Don't add a node if this is not a free space
2606 MapNode n2 = m_env.getMap().getNode(p_over);
2607 bool no_enough_privs =
2608 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2610 dstream<<"Player "<<player->getName()<<" cannot add node"
2611 <<" because privileges are "<<getPlayerPrivs(player)
2614 if(content_buildable_to(n2.d) == false
2617 // Client probably has wrong data.
2618 // Set block not sent, so that client will get
2620 dstream<<"Client "<<peer_id<<" tried to place"
2621 <<" node in invalid position; setting"
2622 <<" MapBlock not sent."<<std::endl;
2623 RemoteClient *client = getClient(peer_id);
2624 v3s16 blockpos = getNodeBlockPos(p_over);
2625 client->SetBlockNotSent(blockpos);
2629 catch(InvalidPositionException &e)
2631 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2632 <<" Adding block to emerge queue."
2634 m_emerge_queue.addBlock(peer_id,
2635 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2639 // Reset build time counter
2640 getClient(peer->id)->m_time_from_building = 0.0;
2643 MaterialItem *mitem = (MaterialItem*)item;
2645 n.d = mitem->getMaterial();
2646 if(content_features(n.d).wall_mounted)
2647 n.dir = packDir(p_under - p_over);
2652 core::list<u16> far_players;
2653 sendAddNode(p_over, n, 0, &far_players, 100);
2658 InventoryList *ilist = player->inventory.getList("main");
2659 if(g_settings.getBool("creative_mode") == false && ilist)
2661 // Remove from inventory and send inventory
2662 if(mitem->getCount() == 1)
2663 ilist->deleteItem(item_i);
2667 UpdateCrafting(peer_id);
2668 SendInventory(peer_id);
2674 This takes some time so it is done after the quick stuff
2676 core::map<v3s16, MapBlock*> modified_blocks;
2677 m_ignore_map_edit_events = true;
2678 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2679 m_ignore_map_edit_events = false;
2682 Set blocks not sent to far players
2684 for(core::list<u16>::Iterator
2685 i = far_players.begin();
2686 i != far_players.end(); i++)
2689 RemoteClient *client = getClient(peer_id);
2692 client->SetBlocksNotSent(modified_blocks);
2696 Calculate special events
2699 /*if(n.d == CONTENT_MESE)
2702 for(s16 z=-1; z<=1; z++)
2703 for(s16 y=-1; y<=1; y++)
2704 for(s16 x=-1; x<=1; x++)
2711 Place other item (not a block)
2715 v3s16 blockpos = getNodeBlockPos(p_over);
2718 Check that the block is loaded so that the item
2719 can properly be added to the static list too
2721 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2724 derr_server<<"Error while placing object: "
2725 "block not found"<<std::endl;
2729 dout_server<<"Placing a miscellaneous item on map"
2732 // Calculate a position for it
2733 v3f pos = intToFloat(p_over, BS);
2735 pos.Y -= BS*0.25; // let it drop a bit
2737 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2738 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2743 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2747 derr_server<<"WARNING: item resulted in NULL object, "
2748 <<"not placing onto map"
2753 // Add the object to the environment
2754 m_env.addActiveObject(obj);
2756 dout_server<<"Placed object"<<std::endl;
2758 if(g_settings.getBool("creative_mode") == false)
2760 // Delete the right amount of items from the slot
2761 u16 dropcount = item->getDropCount();
2763 // Delete item if all gone
2764 if(item->getCount() <= dropcount)
2766 if(item->getCount() < dropcount)
2767 dstream<<"WARNING: Server: dropped more items"
2768 <<" than the slot contains"<<std::endl;
2770 InventoryList *ilist = player->inventory.getList("main");
2772 // Remove from inventory and send inventory
2773 ilist->deleteItem(item_i);
2775 // Else decrement it
2777 item->remove(dropcount);
2780 UpdateCrafting(peer_id);
2781 SendInventory(peer_id);
2789 Catch invalid actions
2793 derr_server<<"WARNING: Server: Invalid action "
2794 <<action<<std::endl;
2798 else if(command == TOSERVER_RELEASE)
2807 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2810 else if(command == TOSERVER_SIGNTEXT)
2812 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2821 std::string datastring((char*)&data[2], datasize-2);
2822 std::istringstream is(datastring, std::ios_base::binary);
2825 is.read((char*)buf, 6);
2826 v3s16 blockpos = readV3S16(buf);
2827 is.read((char*)buf, 2);
2828 s16 id = readS16(buf);
2829 is.read((char*)buf, 2);
2830 u16 textlen = readU16(buf);
2832 for(u16 i=0; i<textlen; i++)
2834 is.read((char*)buf, 1);
2835 text += (char)buf[0];
2838 MapBlock *block = NULL;
2841 block = m_env.getMap().getBlockNoCreate(blockpos);
2843 catch(InvalidPositionException &e)
2845 derr_server<<"Error while setting sign text: "
2846 "block not found"<<std::endl;
2850 MapBlockObject *obj = block->getObject(id);
2853 derr_server<<"Error while setting sign text: "
2854 "object not found"<<std::endl;
2858 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2860 derr_server<<"Error while setting sign text: "
2861 "object is not a sign"<<std::endl;
2865 ((SignObject*)obj)->setText(text);
2867 obj->getBlock()->setChangedFlag();
2869 else if(command == TOSERVER_SIGNNODETEXT)
2871 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2879 std::string datastring((char*)&data[2], datasize-2);
2880 std::istringstream is(datastring, std::ios_base::binary);
2883 is.read((char*)buf, 6);
2884 v3s16 p = readV3S16(buf);
2885 is.read((char*)buf, 2);
2886 u16 textlen = readU16(buf);
2888 for(u16 i=0; i<textlen; i++)
2890 is.read((char*)buf, 1);
2891 text += (char)buf[0];
2894 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2897 if(meta->typeId() != CONTENT_SIGN_WALL)
2899 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2900 signmeta->setText(text);
2902 v3s16 blockpos = getNodeBlockPos(p);
2903 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2906 block->setChangedFlag();
2909 for(core::map<u16, RemoteClient*>::Iterator
2910 i = m_clients.getIterator();
2911 i.atEnd()==false; i++)
2913 RemoteClient *client = i.getNode()->getValue();
2914 client->SetBlockNotSent(blockpos);
2917 else if(command == TOSERVER_INVENTORY_ACTION)
2919 /*// Ignore inventory changes if in creative mode
2920 if(g_settings.getBool("creative_mode") == true)
2922 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2926 // Strip command and create a stream
2927 std::string datastring((char*)&data[2], datasize-2);
2928 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2929 std::istringstream is(datastring, std::ios_base::binary);
2931 InventoryAction *a = InventoryAction::deSerialize(is);
2936 c.current_player = player;
2939 Handle craftresult specially if not in creative mode
2941 bool disable_action = false;
2942 if(a->getType() == IACTION_MOVE
2943 && g_settings.getBool("creative_mode") == false)
2945 IMoveAction *ma = (IMoveAction*)a;
2946 if(ma->to_inv == "current_player" &&
2947 ma->from_inv == "current_player")
2949 InventoryList *rlist = player->inventory.getList("craftresult");
2951 InventoryList *clist = player->inventory.getList("craft");
2953 InventoryList *mlist = player->inventory.getList("main");
2956 Craftresult is no longer preview if something
2959 if(ma->to_list == "craftresult"
2960 && ma->from_list != "craftresult")
2962 // If it currently is a preview, remove
2964 if(player->craftresult_is_preview)
2966 rlist->deleteItem(0);
2968 player->craftresult_is_preview = false;
2971 Crafting takes place if this condition is true.
2973 if(player->craftresult_is_preview &&
2974 ma->from_list == "craftresult")
2976 player->craftresult_is_preview = false;
2977 clist->decrementMaterials(1);
2980 If the craftresult is placed on itself, move it to
2981 main inventory instead of doing the action
2983 if(ma->to_list == "craftresult"
2984 && ma->from_list == "craftresult")
2986 disable_action = true;
2988 InventoryItem *item1 = rlist->changeItem(0, NULL);
2989 mlist->addItem(item1);
2994 if(disable_action == false)
2996 // Feed action to player inventory
3004 UpdateCrafting(player->peer_id);
3005 SendInventory(player->peer_id);
3010 dstream<<"TOSERVER_INVENTORY_ACTION: "
3011 <<"InventoryAction::deSerialize() returned NULL"
3015 else if(command == TOSERVER_CHAT_MESSAGE)
3023 std::string datastring((char*)&data[2], datasize-2);
3024 std::istringstream is(datastring, std::ios_base::binary);
3027 is.read((char*)buf, 2);
3028 u16 len = readU16(buf);
3030 std::wstring message;
3031 for(u16 i=0; i<len; i++)
3033 is.read((char*)buf, 2);
3034 message += (wchar_t)readU16(buf);
3037 // Get player name of this client
3038 std::wstring name = narrow_to_wide(player->getName());
3040 // Line to send to players
3042 // Whether to send to the player that sent the line
3043 bool send_to_sender = false;
3044 // Whether to send to other players
3045 bool send_to_others = false;
3047 // Local player gets all privileges regardless of
3048 // what's set on their account.
3049 u64 privs = getPlayerPrivs(player);
3052 std::wstring commandprefix = L"/#";
3053 if(message.substr(0, commandprefix.size()) == commandprefix)
3055 line += L"Server: ";
3057 message = message.substr(commandprefix.size());
3059 ServerCommandContext *ctx = new ServerCommandContext(
3060 str_split(message, L' '),
3066 line += processServerCommand(ctx);
3067 send_to_sender = ctx->flags & 1;
3068 send_to_others = ctx->flags & 2;
3074 if(privs & PRIV_SHOUT)
3080 send_to_others = true;
3084 line += L"Server: You are not allowed to shout";
3085 send_to_sender = true;
3091 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3094 Send the message to clients
3096 for(core::map<u16, RemoteClient*>::Iterator
3097 i = m_clients.getIterator();
3098 i.atEnd() == false; i++)
3100 // Get client and check that it is valid
3101 RemoteClient *client = i.getNode()->getValue();
3102 assert(client->peer_id == i.getNode()->getKey());
3103 if(client->serialization_version == SER_FMT_VER_INVALID)
3107 bool sender_selected = (peer_id == client->peer_id);
3108 if(sender_selected == true && send_to_sender == false)
3110 if(sender_selected == false && send_to_others == false)
3113 SendChatMessage(client->peer_id, line);
3117 else if(command == TOSERVER_DAMAGE)
3119 if(g_settings.getBool("enable_damage"))
3121 std::string datastring((char*)&data[2], datasize-2);
3122 std::istringstream is(datastring, std::ios_base::binary);
3123 u8 damage = readU8(is);
3124 if(player->hp > damage)
3126 player->hp -= damage;
3132 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3135 v3f pos = findSpawnPos(m_env.getServerMap());
3136 player->setPosition(pos);
3138 SendMovePlayer(player);
3139 SendPlayerHP(player);
3141 //TODO: Throw items around
3145 SendPlayerHP(player);
3147 else if(command == TOSERVER_PASSWORD)
3150 [0] u16 TOSERVER_PASSWORD
3151 [2] u8[28] old password
3152 [30] u8[28] new password
3155 if(datasize != 2+PASSWORD_SIZE*2)
3157 /*char password[PASSWORD_SIZE];
3158 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3159 password[i] = data[2+i];
3160 password[PASSWORD_SIZE-1] = 0;*/
3162 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3170 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3172 char c = data[2+PASSWORD_SIZE+i];
3178 std::string playername = player->getName();
3180 if(m_authmanager.exists(playername) == false)
3182 dstream<<"Server: playername not found in authmanager"<<std::endl;
3183 // Wrong old password supplied!!
3184 SendChatMessage(peer_id, L"playername not found in authmanager");
3188 std::string checkpwd = m_authmanager.getPassword(playername);
3190 if(oldpwd != checkpwd)
3192 dstream<<"Server: invalid old password"<<std::endl;
3193 // Wrong old password supplied!!
3194 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3198 m_authmanager.setPassword(playername, newpwd);
3200 dstream<<"Server: password change successful for "<<playername
3202 SendChatMessage(peer_id, L"Password change successful");
3206 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3207 "unknown command "<<command<<std::endl;
3211 catch(SendFailedException &e)
3213 derr_server<<"Server::ProcessData(): SendFailedException: "
3219 void Server::onMapEditEvent(MapEditEvent *event)
3221 dstream<<"Server::onMapEditEvent()"<<std::endl;
3222 if(m_ignore_map_edit_events)
3224 MapEditEvent *e = event->clone();
3225 m_unsent_map_edit_queue.push_back(e);
3228 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3230 if(id == "current_player")
3232 assert(c->current_player);
3233 return &(c->current_player->inventory);
3237 std::string id0 = fn.next(":");
3239 if(id0 == "nodemeta")
3242 p.X = stoi(fn.next(","));
3243 p.Y = stoi(fn.next(","));
3244 p.Z = stoi(fn.next(","));
3245 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3247 return meta->getInventory();
3248 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3249 <<"no metadata found"<<std::endl;
3253 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3256 void Server::inventoryModified(InventoryContext *c, std::string id)
3258 if(id == "current_player")
3260 assert(c->current_player);
3262 UpdateCrafting(c->current_player->peer_id);
3263 SendInventory(c->current_player->peer_id);
3268 std::string id0 = fn.next(":");
3270 if(id0 == "nodemeta")
3273 p.X = stoi(fn.next(","));
3274 p.Y = stoi(fn.next(","));
3275 p.Z = stoi(fn.next(","));
3276 v3s16 blockpos = getNodeBlockPos(p);
3278 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3280 meta->inventoryModified();
3282 for(core::map<u16, RemoteClient*>::Iterator
3283 i = m_clients.getIterator();
3284 i.atEnd()==false; i++)
3286 RemoteClient *client = i.getNode()->getValue();
3287 client->SetBlockNotSent(blockpos);
3293 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3296 core::list<PlayerInfo> Server::getPlayerInfo()
3298 DSTACK(__FUNCTION_NAME);
3299 JMutexAutoLock envlock(m_env_mutex);
3300 JMutexAutoLock conlock(m_con_mutex);
3302 core::list<PlayerInfo> list;
3304 core::list<Player*> players = m_env.getPlayers();
3306 core::list<Player*>::Iterator i;
3307 for(i = players.begin();
3308 i != players.end(); i++)
3312 Player *player = *i;
3315 con::Peer *peer = m_con.GetPeer(player->peer_id);
3316 // Copy info from peer to info struct
3318 info.address = peer->address;
3319 info.avg_rtt = peer->avg_rtt;
3321 catch(con::PeerNotFoundException &e)
3323 // Set dummy peer info
3325 info.address = Address(0,0,0,0,0);
3329 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3330 info.position = player->getPosition();
3332 list.push_back(info);
3339 void Server::peerAdded(con::Peer *peer)
3341 DSTACK(__FUNCTION_NAME);
3342 dout_server<<"Server::peerAdded(): peer->id="
3343 <<peer->id<<std::endl;
3346 c.type = PEER_ADDED;
3347 c.peer_id = peer->id;
3349 m_peer_change_queue.push_back(c);
3352 void Server::deletingPeer(con::Peer *peer, bool timeout)
3354 DSTACK(__FUNCTION_NAME);
3355 dout_server<<"Server::deletingPeer(): peer->id="
3356 <<peer->id<<", timeout="<<timeout<<std::endl;
3359 c.type = PEER_REMOVED;
3360 c.peer_id = peer->id;
3361 c.timeout = timeout;
3362 m_peer_change_queue.push_back(c);
3369 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3371 DSTACK(__FUNCTION_NAME);
3372 std::ostringstream os(std::ios_base::binary);
3374 writeU16(os, TOCLIENT_HP);
3378 std::string s = os.str();
3379 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3381 con.Send(peer_id, 0, data, true);
3384 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3385 const std::wstring &reason)
3387 DSTACK(__FUNCTION_NAME);
3388 std::ostringstream os(std::ios_base::binary);
3390 writeU16(os, TOCLIENT_ACCESS_DENIED);
3391 os<<serializeWideString(reason);
3394 std::string s = os.str();
3395 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3397 con.Send(peer_id, 0, data, true);
3401 Non-static send methods
3404 void Server::SendObjectData(float dtime)
3406 DSTACK(__FUNCTION_NAME);
3408 core::map<v3s16, bool> stepped_blocks;
3410 for(core::map<u16, RemoteClient*>::Iterator
3411 i = m_clients.getIterator();
3412 i.atEnd() == false; i++)
3414 u16 peer_id = i.getNode()->getKey();
3415 RemoteClient *client = i.getNode()->getValue();
3416 assert(client->peer_id == peer_id);
3418 if(client->serialization_version == SER_FMT_VER_INVALID)
3421 client->SendObjectData(this, dtime, stepped_blocks);
3425 void Server::SendPlayerInfos()
3427 DSTACK(__FUNCTION_NAME);
3429 //JMutexAutoLock envlock(m_env_mutex);
3431 // Get connected players
3432 core::list<Player*> players = m_env.getPlayers(true);
3434 u32 player_count = players.getSize();
3435 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3437 SharedBuffer<u8> data(datasize);
3438 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3441 core::list<Player*>::Iterator i;
3442 for(i = players.begin();
3443 i != players.end(); i++)
3445 Player *player = *i;
3447 /*dstream<<"Server sending player info for player with "
3448 "peer_id="<<player->peer_id<<std::endl;*/
3450 writeU16(&data[start], player->peer_id);
3451 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3452 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3453 start += 2+PLAYERNAME_SIZE;
3456 //JMutexAutoLock conlock(m_con_mutex);
3459 m_con.SendToAll(0, data, true);
3462 void Server::SendInventory(u16 peer_id)
3464 DSTACK(__FUNCTION_NAME);
3466 Player* player = m_env.getPlayer(peer_id);
3473 std::ostringstream os;
3474 //os.imbue(std::locale("C"));
3476 player->inventory.serialize(os);
3478 std::string s = os.str();
3480 SharedBuffer<u8> data(s.size()+2);
3481 writeU16(&data[0], TOCLIENT_INVENTORY);
3482 memcpy(&data[2], s.c_str(), s.size());
3485 m_con.Send(peer_id, 0, data, true);
3488 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3490 DSTACK(__FUNCTION_NAME);
3492 std::ostringstream os(std::ios_base::binary);
3496 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3497 os.write((char*)buf, 2);
3500 writeU16(buf, message.size());
3501 os.write((char*)buf, 2);
3504 for(u32 i=0; i<message.size(); i++)
3508 os.write((char*)buf, 2);
3512 std::string s = os.str();
3513 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3515 m_con.Send(peer_id, 0, data, true);
3518 void Server::BroadcastChatMessage(const std::wstring &message)
3520 for(core::map<u16, RemoteClient*>::Iterator
3521 i = m_clients.getIterator();
3522 i.atEnd() == false; i++)
3524 // Get client and check that it is valid
3525 RemoteClient *client = i.getNode()->getValue();
3526 assert(client->peer_id == i.getNode()->getKey());
3527 if(client->serialization_version == SER_FMT_VER_INVALID)
3530 SendChatMessage(client->peer_id, message);
3534 void Server::SendPlayerHP(Player *player)
3536 SendHP(m_con, player->peer_id, player->hp);
3539 void Server::SendMovePlayer(Player *player)
3541 DSTACK(__FUNCTION_NAME);
3542 std::ostringstream os(std::ios_base::binary);
3544 writeU16(os, TOCLIENT_MOVE_PLAYER);
3545 writeV3F1000(os, player->getPosition());
3546 writeF1000(os, player->getPitch());
3547 writeF1000(os, player->getYaw());
3550 v3f pos = player->getPosition();
3551 f32 pitch = player->getPitch();
3552 f32 yaw = player->getYaw();
3553 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3554 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3561 std::string s = os.str();
3562 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3564 m_con.Send(player->peer_id, 0, data, true);
3567 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3568 core::list<u16> *far_players, float far_d_nodes)
3570 float maxd = far_d_nodes*BS;
3571 v3f p_f = intToFloat(p, BS);
3575 SharedBuffer<u8> reply(replysize);
3576 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3577 writeS16(&reply[2], p.X);
3578 writeS16(&reply[4], p.Y);
3579 writeS16(&reply[6], p.Z);
3581 for(core::map<u16, RemoteClient*>::Iterator
3582 i = m_clients.getIterator();
3583 i.atEnd() == false; i++)
3585 // Get client and check that it is valid
3586 RemoteClient *client = i.getNode()->getValue();
3587 assert(client->peer_id == i.getNode()->getKey());
3588 if(client->serialization_version == SER_FMT_VER_INVALID)
3591 // Don't send if it's the same one
3592 if(client->peer_id == ignore_id)
3598 Player *player = m_env.getPlayer(client->peer_id);
3601 // If player is far away, only set modified blocks not sent
3602 v3f player_pos = player->getPosition();
3603 if(player_pos.getDistanceFrom(p_f) > maxd)
3605 far_players->push_back(client->peer_id);
3612 m_con.Send(client->peer_id, 0, reply, true);
3616 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3617 core::list<u16> *far_players, float far_d_nodes)
3619 float maxd = far_d_nodes*BS;
3620 v3f p_f = intToFloat(p, BS);
3622 for(core::map<u16, RemoteClient*>::Iterator
3623 i = m_clients.getIterator();
3624 i.atEnd() == false; i++)
3626 // Get client and check that it is valid
3627 RemoteClient *client = i.getNode()->getValue();
3628 assert(client->peer_id == i.getNode()->getKey());
3629 if(client->serialization_version == SER_FMT_VER_INVALID)
3632 // Don't send if it's the same one
3633 if(client->peer_id == ignore_id)
3639 Player *player = m_env.getPlayer(client->peer_id);
3642 // If player is far away, only set modified blocks not sent
3643 v3f player_pos = player->getPosition();
3644 if(player_pos.getDistanceFrom(p_f) > maxd)
3646 far_players->push_back(client->peer_id);
3653 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3654 SharedBuffer<u8> reply(replysize);
3655 writeU16(&reply[0], TOCLIENT_ADDNODE);
3656 writeS16(&reply[2], p.X);
3657 writeS16(&reply[4], p.Y);
3658 writeS16(&reply[6], p.Z);
3659 n.serialize(&reply[8], client->serialization_version);
3662 m_con.Send(client->peer_id, 0, reply, true);
3666 void Server::setBlockNotSent(v3s16 p)
3668 for(core::map<u16, RemoteClient*>::Iterator
3669 i = m_clients.getIterator();
3670 i.atEnd()==false; i++)
3672 RemoteClient *client = i.getNode()->getValue();
3673 client->SetBlockNotSent(p);
3677 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3679 DSTACK(__FUNCTION_NAME);
3681 v3s16 p = block->getPos();
3685 bool completely_air = true;
3686 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3687 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3688 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3690 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3692 completely_air = false;
3693 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3698 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3700 dstream<<"[completely air] ";
3705 Create a packet with the block in the right format
3708 std::ostringstream os(std::ios_base::binary);
3709 block->serialize(os, ver);
3710 std::string s = os.str();
3711 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3713 u32 replysize = 8 + blockdata.getSize();
3714 SharedBuffer<u8> reply(replysize);
3715 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3716 writeS16(&reply[2], p.X);
3717 writeS16(&reply[4], p.Y);
3718 writeS16(&reply[6], p.Z);
3719 memcpy(&reply[8], *blockdata, blockdata.getSize());
3721 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3722 <<": \tpacket size: "<<replysize<<std::endl;*/
3727 m_con.Send(peer_id, 1, reply, true);
3730 void Server::SendBlocks(float dtime)
3732 DSTACK(__FUNCTION_NAME);
3734 JMutexAutoLock envlock(m_env_mutex);
3735 JMutexAutoLock conlock(m_con_mutex);
3737 //TimeTaker timer("Server::SendBlocks");
3739 core::array<PrioritySortedBlockTransfer> queue;
3741 s32 total_sending = 0;
3744 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3746 for(core::map<u16, RemoteClient*>::Iterator
3747 i = m_clients.getIterator();
3748 i.atEnd() == false; i++)
3750 RemoteClient *client = i.getNode()->getValue();
3751 assert(client->peer_id == i.getNode()->getKey());
3753 total_sending += client->SendingCount();
3755 if(client->serialization_version == SER_FMT_VER_INVALID)
3758 client->GetNextBlocks(this, dtime, queue);
3763 // Lowest priority number comes first.
3764 // Lowest is most important.
3767 for(u32 i=0; i<queue.size(); i++)
3769 //TODO: Calculate limit dynamically
3770 if(total_sending >= g_settings.getS32
3771 ("max_simultaneous_block_sends_server_total"))
3774 PrioritySortedBlockTransfer q = queue[i];
3776 MapBlock *block = NULL;
3779 block = m_env.getMap().getBlockNoCreate(q.pos);
3781 catch(InvalidPositionException &e)
3786 RemoteClient *client = getClient(q.peer_id);
3788 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3790 client->SentBlock(q.pos);
3800 void Server::UpdateCrafting(u16 peer_id)
3802 DSTACK(__FUNCTION_NAME);
3804 Player* player = m_env.getPlayer(peer_id);
3808 Calculate crafting stuff
3810 if(g_settings.getBool("creative_mode") == false)
3812 InventoryList *clist = player->inventory.getList("craft");
3813 InventoryList *rlist = player->inventory.getList("craftresult");
3815 if(rlist->getUsedSlots() == 0)
3816 player->craftresult_is_preview = true;
3818 if(rlist && player->craftresult_is_preview)
3820 rlist->clearItems();
3822 if(clist && rlist && player->craftresult_is_preview)
3824 InventoryItem *items[9];
3825 for(u16 i=0; i<9; i++)
3827 items[i] = clist->getItem(i);
3836 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3837 if(checkItemCombination(items, specs))
3839 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3848 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3849 if(checkItemCombination(items, specs))
3851 rlist->addItem(new CraftItem("Stick", 4));
3860 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3861 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3862 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3863 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3864 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3865 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3866 if(checkItemCombination(items, specs))
3868 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3877 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3878 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3879 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3880 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3881 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3882 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3883 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3884 if(checkItemCombination(items, specs))
3886 //rlist->addItem(new MapBlockObjectItem("Sign"));
3887 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3896 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3897 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3898 if(checkItemCombination(items, specs))
3900 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3909 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3910 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3911 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3912 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3913 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3914 if(checkItemCombination(items, specs))
3916 rlist->addItem(new ToolItem("WPick", 0));
3925 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3926 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3927 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3928 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3929 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3930 if(checkItemCombination(items, specs))
3932 rlist->addItem(new ToolItem("STPick", 0));
3941 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3942 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3943 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3944 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3945 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3946 if(checkItemCombination(items, specs))
3948 rlist->addItem(new ToolItem("SteelPick", 0));
3957 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3958 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3959 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3960 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3961 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3962 if(checkItemCombination(items, specs))
3964 rlist->addItem(new ToolItem("MesePick", 0));
3973 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3974 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3975 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3976 if(checkItemCombination(items, specs))
3978 rlist->addItem(new ToolItem("WShovel", 0));
3987 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3988 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3989 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3990 if(checkItemCombination(items, specs))
3992 rlist->addItem(new ToolItem("STShovel", 0));
4001 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4002 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4003 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4004 if(checkItemCombination(items, specs))
4006 rlist->addItem(new ToolItem("SteelShovel", 0));
4015 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4016 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4017 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4018 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4019 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4020 if(checkItemCombination(items, specs))
4022 rlist->addItem(new ToolItem("WAxe", 0));
4031 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4032 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4033 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4034 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4035 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4036 if(checkItemCombination(items, specs))
4038 rlist->addItem(new ToolItem("STAxe", 0));
4047 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4048 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4049 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4050 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4051 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4052 if(checkItemCombination(items, specs))
4054 rlist->addItem(new ToolItem("SteelAxe", 0));
4063 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4064 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4065 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4066 if(checkItemCombination(items, specs))
4068 rlist->addItem(new ToolItem("WSword", 0));
4077 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4078 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4079 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4080 if(checkItemCombination(items, specs))
4082 rlist->addItem(new ToolItem("STSword", 0));
4091 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4092 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4093 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4094 if(checkItemCombination(items, specs))
4096 rlist->addItem(new ToolItem("SteelSword", 0));
4105 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4106 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4107 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4108 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4109 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4110 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4111 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4112 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4113 if(checkItemCombination(items, specs))
4115 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4124 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4125 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4126 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4127 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4128 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4129 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4130 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4131 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4132 if(checkItemCombination(items, specs))
4134 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4143 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4144 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4145 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4146 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4147 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4148 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4149 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4150 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4151 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4152 if(checkItemCombination(items, specs))
4154 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4160 } // if creative_mode == false
4163 RemoteClient* Server::getClient(u16 peer_id)
4165 DSTACK(__FUNCTION_NAME);
4166 //JMutexAutoLock lock(m_con_mutex);
4167 core::map<u16, RemoteClient*>::Node *n;
4168 n = m_clients.find(peer_id);
4169 // A client should exist for all peers
4171 return n->getValue();
4174 std::wstring Server::getStatusString()
4176 std::wostringstream os(std::ios_base::binary);
4179 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4181 os<<L", uptime="<<m_uptime.get();
4182 // Information about clients
4184 for(core::map<u16, RemoteClient*>::Iterator
4185 i = m_clients.getIterator();
4186 i.atEnd() == false; i++)
4188 // Get client and check that it is valid
4189 RemoteClient *client = i.getNode()->getValue();
4190 assert(client->peer_id == i.getNode()->getKey());
4191 if(client->serialization_version == SER_FMT_VER_INVALID)
4194 Player *player = m_env.getPlayer(client->peer_id);
4195 // Get name of player
4196 std::wstring name = L"unknown";
4198 name = narrow_to_wide(player->getName());
4199 // Add name to information string
4203 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4204 os<<" WARNING: Map saving is disabled."<<std::endl;
4209 void setCreativeInventory(Player *player)
4211 player->resetInventory();
4213 // Give some good tools
4215 InventoryItem *item = new ToolItem("MesePick", 0);
4216 void* r = player->inventory.addItem("main", item);
4220 InventoryItem *item = new ToolItem("SteelPick", 0);
4221 void* r = player->inventory.addItem("main", item);
4225 InventoryItem *item = new ToolItem("SteelAxe", 0);
4226 void* r = player->inventory.addItem("main", item);
4230 InventoryItem *item = new ToolItem("SteelShovel", 0);
4231 void* r = player->inventory.addItem("main", item);
4239 // CONTENT_IGNORE-terminated list
4240 u8 material_items[] = {
4251 CONTENT_WATERSOURCE,
4259 u8 *mip = material_items;
4260 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4262 if(*mip == CONTENT_IGNORE)
4265 InventoryItem *item = new MaterialItem(*mip, 1);
4266 player->inventory.addItem("main", item);
4272 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4275 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4276 player->inventory.addItem("main", item);
4279 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4281 // Skip some materials
4282 if(i == CONTENT_WATER || i == CONTENT_TORCH
4283 || i == CONTENT_COALSTONE)
4286 InventoryItem *item = new MaterialItem(i, 1);
4287 player->inventory.addItem("main", item);
4293 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4294 void* r = player->inventory.addItem("main", item);
4299 v3f findSpawnPos(ServerMap &map)
4301 //return v3f(50,50,50)*BS;
4304 s16 groundheight = 0;
4306 // Try to find a good place a few times
4307 for(s32 i=0; i<1000; i++)
4310 // We're going to try to throw the player to this position
4311 nodepos = v2s16(-range + (myrand()%(range*2)),
4312 -range + (myrand()%(range*2)));
4313 v2s16 sectorpos = getNodeSectorPos(nodepos);
4314 // Get sector (NOTE: Don't get because it's slow)
4315 //m_env.getMap().emergeSector(sectorpos);
4316 // Get ground height at point (fallbacks to heightmap function)
4317 groundheight = map.findGroundLevel(nodepos);
4318 // Don't go underwater
4319 if(groundheight < WATER_LEVEL)
4321 //dstream<<"-> Underwater"<<std::endl;
4324 // Don't go to high places
4325 if(groundheight > WATER_LEVEL + 4)
4327 //dstream<<"-> Underwater"<<std::endl;
4331 // Found a good place
4332 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4336 // If no suitable place was not found, go above water at least.
4337 if(groundheight < WATER_LEVEL)
4338 groundheight = WATER_LEVEL;
4340 return intToFloat(v3s16(
4347 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4350 Try to get an existing player
4352 Player *player = m_env.getPlayer(name);
4355 // If player is already connected, cancel
4356 if(player->peer_id != 0)
4358 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4363 player->peer_id = peer_id;
4365 // Reset inventory to creative if in creative mode
4366 if(g_settings.getBool("creative_mode"))
4368 setCreativeInventory(player);
4375 If player with the wanted peer_id already exists, cancel.
4377 if(m_env.getPlayer(peer_id) != NULL)
4379 dstream<<"emergePlayer(): Player with wrong name but same"
4380 " peer_id already exists"<<std::endl;
4388 player = new ServerRemotePlayer();
4389 //player->peer_id = c.peer_id;
4390 //player->peer_id = PEER_ID_INEXISTENT;
4391 player->peer_id = peer_id;
4392 player->updateName(name);
4393 m_authmanager.add(name);
4394 m_authmanager.setPassword(name, password);
4395 m_authmanager.setPrivs(name,
4396 stringToPrivs(g_settings.get("default_privs")));
4402 dstream<<"Server: Finding spawn place for player \""
4403 <<player->getName()<<"\""<<std::endl;
4405 v3f pos = findSpawnPos(m_env.getServerMap());
4407 player->setPosition(pos);
4410 Add player to environment
4413 m_env.addPlayer(player);
4416 Add stuff to inventory
4419 if(g_settings.getBool("creative_mode"))
4421 setCreativeInventory(player);
4423 else if(g_settings.getBool("give_initial_stuff"))
4426 InventoryItem *item = new ToolItem("SteelPick", 0);
4427 void* r = player->inventory.addItem("main", item);
4431 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4432 void* r = player->inventory.addItem("main", item);
4436 InventoryItem *item = new ToolItem("SteelAxe", 0);
4437 void* r = player->inventory.addItem("main", item);
4441 InventoryItem *item = new ToolItem("SteelShovel", 0);
4442 void* r = player->inventory.addItem("main", item);
4446 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4447 void* r = player->inventory.addItem("main", item);
4451 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4452 void* r = player->inventory.addItem("main", item);
4456 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4457 void* r = player->inventory.addItem("main", item);
4461 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4462 void* r = player->inventory.addItem("main", item);
4466 InventoryItem *item = new CraftItem("Stick", 4);
4467 void* r = player->inventory.addItem("main", item);
4471 InventoryItem *item = new ToolItem("WPick", 32000);
4472 void* r = player->inventory.addItem("main", item);
4476 InventoryItem *item = new ToolItem("STPick", 32000);
4477 void* r = player->inventory.addItem("main", item);
4481 for(u16 i=0; i<4; i++)
4483 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4484 bool r = player->inventory.addItem("main", item);
4487 /*// Give some other stuff
4489 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4490 bool r = player->inventory.addItem("main", item);
4497 } // create new player
4500 void Server::handlePeerChange(PeerChange &c)
4502 JMutexAutoLock envlock(m_env_mutex);
4503 JMutexAutoLock conlock(m_con_mutex);
4505 if(c.type == PEER_ADDED)
4512 core::map<u16, RemoteClient*>::Node *n;
4513 n = m_clients.find(c.peer_id);
4514 // The client shouldn't already exist
4518 RemoteClient *client = new RemoteClient();
4519 client->peer_id = c.peer_id;
4520 m_clients.insert(client->peer_id, client);
4523 else if(c.type == PEER_REMOVED)
4530 core::map<u16, RemoteClient*>::Node *n;
4531 n = m_clients.find(c.peer_id);
4532 // The client should exist
4536 Mark objects to be not known by the client
4538 RemoteClient *client = n->getValue();
4540 for(core::map<u16, bool>::Iterator
4541 i = client->m_known_objects.getIterator();
4542 i.atEnd()==false; i++)
4545 u16 id = i.getNode()->getKey();
4546 ServerActiveObject* obj = m_env.getActiveObject(id);
4548 if(obj && obj->m_known_by_count > 0)
4549 obj->m_known_by_count--;
4552 // Collect information about leaving in chat
4553 std::wstring message;
4555 std::wstring name = L"unknown";
4556 Player *player = m_env.getPlayer(c.peer_id);
4558 name = narrow_to_wide(player->getName());
4562 message += L" left game";
4564 message += L" (timed out)";
4569 m_env.removePlayer(c.peer_id);
4572 // Set player client disconnected
4574 Player *player = m_env.getPlayer(c.peer_id);
4576 player->peer_id = 0;
4580 delete m_clients[c.peer_id];
4581 m_clients.remove(c.peer_id);
4583 // Send player info to all remaining clients
4586 // Send leave chat message to all remaining clients
4587 BroadcastChatMessage(message);
4596 void Server::handlePeerChanges()
4598 while(m_peer_change_queue.size() > 0)
4600 PeerChange c = m_peer_change_queue.pop_front();
4602 dout_server<<"Server: Handling peer change: "
4603 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4606 handlePeerChange(c);
4610 u64 Server::getPlayerPrivs(Player *player)
4614 std::string playername = player->getName();
4615 // Local player gets all privileges regardless of
4616 // what's set on their account.
4617 if(g_settings.get("name") == playername)
4623 return getPlayerAuthPrivs(playername);
4627 void dedicated_server_loop(Server &server, bool &kill)
4629 DSTACK(__FUNCTION_NAME);
4631 dstream<<DTIME<<std::endl;
4632 dstream<<"========================"<<std::endl;
4633 dstream<<"Running dedicated server"<<std::endl;
4634 dstream<<"========================"<<std::endl;
4637 IntervalLimiter m_profiler_interval;
4641 // This is kind of a hack but can be done like this
4642 // because server.step() is very light
4644 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4649 if(server.getShutdownRequested() || kill)
4651 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4658 float profiler_print_interval =
4659 g_settings.getFloat("profiler_print_interval");
4660 if(profiler_print_interval != 0)
4662 if(m_profiler_interval.step(0.030, profiler_print_interval))
4664 dstream<<"Profiler:"<<std::endl;
4665 g_profiler.print(dstream);
4673 static int counter = 0;
4679 core::list<PlayerInfo> list = server.getPlayerInfo();
4680 core::list<PlayerInfo>::Iterator i;
4681 static u32 sum_old = 0;
4682 u32 sum = PIChecksum(list);
4685 dstream<<DTIME<<"Player info:"<<std::endl;
4686 for(i=list.begin(); i!=list.end(); i++)
4688 i->PrintLine(&dstream);