3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
37 void * ServerThread::Thread()
41 DSTACK(__FUNCTION_NAME);
43 BEGIN_DEBUG_EXCEPTION_HANDLER
48 //TimeTaker timer("AsyncRunStep() + Receive()");
51 //TimeTaker timer("AsyncRunStep()");
52 m_server->AsyncRunStep();
55 //dout_server<<"Running m_server->Receive()"<<std::endl;
58 catch(con::NoIncomingDataException &e)
61 catch(con::PeerNotFoundException &e)
63 dout_server<<"Server: PeerNotFoundException"<<std::endl;
67 END_DEBUG_EXCEPTION_HANDLER
72 void * EmergeThread::Thread()
76 DSTACK(__FUNCTION_NAME);
80 BEGIN_DEBUG_EXCEPTION_HANDLER
83 Get block info from queue, emerge them and send them
86 After queue is empty, exit.
90 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
94 SharedPtr<QueuedBlockEmerge> q(qptr);
100 Do not generate over-limit
102 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
110 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
112 //TimeTaker timer("block emerge");
115 Try to emerge it from somewhere.
117 If it is only wanted as optional, only loading from disk
122 Check if any peer wants it as non-optional. In that case it
125 Also decrement the emerge queue count in clients.
128 bool optional = true;
131 core::map<u16, u8>::Iterator i;
132 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
134 //u16 peer_id = i.getNode()->getKey();
137 u8 flags = i.getNode()->getValue();
138 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
144 /*dstream<<"EmergeThread: p="
145 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
146 <<"optional="<<optional<<std::endl;*/
148 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
150 core::map<v3s16, MapBlock*> changed_blocks;
151 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
153 MapBlock *block = NULL;
154 bool got_block = true;
155 core::map<v3s16, MapBlock*> modified_blocks;
157 bool only_from_disk = false;
160 only_from_disk = true;
162 v2s16 chunkpos = map.sector_to_chunk(p2d);
164 bool generate_chunk = false;
165 if(only_from_disk == false)
167 JMutexAutoLock envlock(m_server->m_env_mutex);
168 if(map.chunkNonVolatile(chunkpos) == false)
169 generate_chunk = true;
176 JMutexAutoLock envlock(m_server->m_env_mutex);
177 map.initChunkMake(data, chunkpos);
183 JMutexAutoLock envlock(m_server->m_env_mutex);
184 map.finishChunkMake(data, changed_blocks);
189 Fetch block from map or generate a single block
192 JMutexAutoLock envlock(m_server->m_env_mutex);
194 // Load sector if it isn't loaded
195 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196 map.loadSectorFull(p2d);
198 block = map.getBlockNoCreateNoEx(p);
199 if(!block || block->isDummy())
207 // Get, load or create sector
208 ServerMapSector *sector =
209 (ServerMapSector*)map.createSector(p2d);
211 block = map.generateBlock(p, block, sector, changed_blocks,
212 lighting_invalidated_blocks);
219 if(block->getLightingExpired()){
220 lighting_invalidated_blocks[block->getPos()] = block;
224 // TODO: Some additional checking and lighting updating,
229 JMutexAutoLock envlock(m_server->m_env_mutex);
234 Collect a list of blocks that have been modified in
235 addition to the fetched one.
238 if(lighting_invalidated_blocks.size() > 0)
240 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
241 <<" blocks"<<std::endl;*/
243 // 50-100ms for single block generation
244 //TimeTaker timer("** EmergeThread updateLighting");
246 // Update lighting without locking the environment mutex,
247 // add modified blocks to changed blocks
248 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
251 // Add all from changed_blocks to modified_blocks
252 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
253 i.atEnd() == false; i++)
255 MapBlock *block = i.getNode()->getValue();
256 modified_blocks.insert(block->getPos(), block);
259 // If we got no block, there should be no invalidated blocks
262 assert(lighting_invalidated_blocks.size() == 0);
268 Set sent status of modified blocks on clients
271 // NOTE: Server's clients are also behind the connection mutex
272 JMutexAutoLock lock(m_server->m_con_mutex);
275 Add the originally fetched block to the modified list
279 modified_blocks.insert(p, block);
283 Set the modified blocks unsent for all the clients
286 for(core::map<u16, RemoteClient*>::Iterator
287 i = m_server->m_clients.getIterator();
288 i.atEnd() == false; i++)
290 RemoteClient *client = i.getNode()->getValue();
292 if(modified_blocks.size() > 0)
294 // Remove block from sent history
295 client->SetBlocksNotSent(modified_blocks);
301 END_DEBUG_EXCEPTION_HANDLER
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307 core::array<PrioritySortedBlockTransfer> &dest)
309 DSTACK(__FUNCTION_NAME);
312 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
315 m_nothing_to_send_pause_timer -= dtime;
317 if(m_nothing_to_send_pause_timer >= 0)
320 m_nearest_unsent_reset_timer = 0;
324 // Won't send anything if already sending
325 if(m_blocks_sending.size() >= g_settings.getU16
326 ("max_simultaneous_block_sends_per_client"))
328 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
332 //TimeTaker timer("RemoteClient::GetNextBlocks");
334 Player *player = server->m_env.getPlayer(peer_id);
336 assert(player != NULL);
338 v3f playerpos = player->getPosition();
339 v3f playerspeed = player->getSpeed();
340 v3f playerspeeddir(0,0,0);
341 if(playerspeed.getLength() > 1.0*BS)
342 playerspeeddir = playerspeed / playerspeed.getLength();
343 // Predict to next block
344 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
346 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
348 v3s16 center = getNodeBlockPos(center_nodepos);
350 // Camera position and direction
352 playerpos + v3f(0, BS+BS/2, 0);
353 v3f camera_dir = v3f(0,0,1);
354 camera_dir.rotateYZBy(player->getPitch());
355 camera_dir.rotateXZBy(player->getYaw());
357 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
358 <<camera_dir.Z<<")"<<std::endl;*/
361 Get the starting value of the block finder radius.
364 if(m_last_center != center)
366 m_nearest_unsent_d = 0;
367 m_last_center = center;
370 /*dstream<<"m_nearest_unsent_reset_timer="
371 <<m_nearest_unsent_reset_timer<<std::endl;*/
373 // This has to be incremented only when the nothing to send pause
375 m_nearest_unsent_reset_timer += dtime;
377 // Reset periodically to avoid possible bugs or other mishaps
378 if(m_nearest_unsent_reset_timer > 10.0)
380 m_nearest_unsent_reset_timer = 0;
381 m_nearest_unsent_d = 0;
382 /*dstream<<"Resetting m_nearest_unsent_d for "
383 <<server->getPlayerName(peer_id)<<std::endl;*/
386 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
387 s16 d_start = m_nearest_unsent_d;
389 //dstream<<"d_start="<<d_start<<std::endl;
391 u16 max_simul_sends_setting = g_settings.getU16
392 ("max_simultaneous_block_sends_per_client");
393 u16 max_simul_sends_usually = max_simul_sends_setting;
396 Check the time from last addNode/removeNode.
398 Decrease send rate if player is building stuff.
400 m_time_from_building += dtime;
401 if(m_time_from_building < g_settings.getFloat(
402 "full_block_send_enable_min_time_from_building"))
404 max_simul_sends_usually
405 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
409 Number of blocks sending + number of blocks selected for sending
411 u32 num_blocks_selected = m_blocks_sending.size();
414 next time d will be continued from the d from which the nearest
415 unsent block was found this time.
417 This is because not necessarily any of the blocks found this
418 time are actually sent.
420 s32 new_nearest_unsent_d = -1;
422 s16 d_max = g_settings.getS16("max_block_send_distance");
423 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
425 // Don't loop very much at a time
426 if(d_max > d_start+1)
428 /*if(d_max_gen > d_start+2)
429 d_max_gen = d_start+2;*/
431 //dstream<<"Starting from "<<d_start<<std::endl;
433 bool sending_something = false;
435 bool no_blocks_found_for_sending = true;
437 bool queue_is_full = false;
440 for(d = d_start; d <= d_max; d++)
442 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
445 If m_nearest_unsent_d was changed by the EmergeThread
446 (it can change it to 0 through SetBlockNotSent),
448 Else update m_nearest_unsent_d
450 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
452 d = m_nearest_unsent_d;
453 last_nearest_unsent_d = m_nearest_unsent_d;
457 Get the border/face dot coordinates of a "d-radiused"
460 core::list<v3s16> list;
461 getFacePositions(list, d);
463 core::list<v3s16>::Iterator li;
464 for(li=list.begin(); li!=list.end(); li++)
466 v3s16 p = *li + center;
470 - Don't allow too many simultaneous transfers
471 - EXCEPT when the blocks are very close
473 Also, don't send blocks that are already flying.
476 // Start with the usual maximum
477 u16 max_simul_dynamic = max_simul_sends_usually;
479 // If block is very close, allow full maximum
480 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
481 max_simul_dynamic = max_simul_sends_setting;
483 // Don't select too many blocks for sending
484 if(num_blocks_selected >= max_simul_dynamic)
486 queue_is_full = true;
487 goto queue_full_break;
490 // Don't send blocks that are currently being transferred
491 if(m_blocks_sending.find(p) != NULL)
497 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
498 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
499 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
500 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
501 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
502 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
505 // If this is true, inexistent block will be made from scratch
506 bool generate = d <= d_max_gen;
509 /*// Limit the generating area vertically to 2/3
510 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
513 // Limit the send area vertically to 2/3
514 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
520 If block is far away, don't generate it unless it is
526 // Block center y in nodes
527 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
528 // Don't generate if it's very high or very low
529 if(y < -64 || y > 64)
533 v2s16 p2d_nodes_center(
537 // Get ground height in nodes
538 s16 gh = server->m_env.getServerMap().findGroundLevel(
541 // If differs a lot, don't generate
542 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
544 // Actually, don't even send it
550 //dstream<<"d="<<d<<std::endl;
553 Don't generate or send if not in sight
556 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
562 Don't send already sent blocks
565 if(m_blocks_sent.find(p) != NULL)
572 Check if map has this block
574 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
576 bool surely_not_found_on_disk = false;
577 bool block_is_invalid = false;
580 // Block is dummy if data doesn't exist.
581 // It means it has been not found from disk and not generated
584 surely_not_found_on_disk = true;
587 // Block is valid if lighting is up-to-date and data exists
588 if(block->isValid() == false)
590 block_is_invalid = true;
593 /*if(block->isFullyGenerated() == false)
595 block_is_invalid = true;
599 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
600 v2s16 chunkpos = map->sector_to_chunk(p2d);
601 if(map->chunkNonVolatile(chunkpos) == false)
602 block_is_invalid = true;
605 If block is not close, don't send it unless it is near
608 Block is near ground level if night-time mesh
609 differs from day-time mesh.
613 if(block->dayNightDiffed() == false)
620 If block has been marked to not exist on disk (dummy)
621 and generating new ones is not wanted, skip block.
623 if(generate == false && surely_not_found_on_disk == true)
630 Record the lowest d from which a block has been
631 found being not sent and possibly to exist
633 if(no_blocks_found_for_sending)
636 new_nearest_unsent_d = d;
639 no_blocks_found_for_sending = false;
642 Add inexistent block to emerge queue.
644 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
646 //TODO: Get value from somewhere
647 // Allow only one block in emerge queue
648 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
649 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
651 //dstream<<"Adding block to emerge queue"<<std::endl;
653 // Add it to the emerge queue and trigger the thread
656 if(generate == false)
657 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
659 server->m_emerge_queue.addBlock(peer_id, p, flags);
660 server->m_emergethread.trigger();
668 Add block to send queue
671 PrioritySortedBlockTransfer q((float)d, p, peer_id);
675 num_blocks_selected += 1;
676 sending_something = true;
681 //dstream<<"Stopped at "<<d<<std::endl;
683 if(no_blocks_found_for_sending)
685 if(queue_is_full == false)
686 new_nearest_unsent_d = d;
689 if(new_nearest_unsent_d != -1)
690 m_nearest_unsent_d = new_nearest_unsent_d;
692 if(sending_something == false)
694 m_nothing_to_send_counter++;
695 if((s16)m_nothing_to_send_counter >=
696 g_settings.getS16("max_block_send_distance"))
698 // Pause time in seconds
699 m_nothing_to_send_pause_timer = 1.0;
700 /*dstream<<"nothing to send to "
701 <<server->getPlayerName(peer_id)
702 <<" (d="<<d<<")"<<std::endl;*/
707 m_nothing_to_send_counter = 0;
710 /*timer_result = timer.stop(true);
711 if(timer_result != 0)
712 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
715 void RemoteClient::SendObjectData(
718 core::map<v3s16, bool> &stepped_blocks
721 DSTACK(__FUNCTION_NAME);
723 // Can't send anything without knowing version
724 if(serialization_version == SER_FMT_VER_INVALID)
726 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
732 Send a TOCLIENT_OBJECTDATA packet.
736 u16 number of player positions
747 std::ostringstream os(std::ios_base::binary);
751 writeU16(buf, TOCLIENT_OBJECTDATA);
752 os.write((char*)buf, 2);
755 Get and write player data
758 // Get connected players
759 core::list<Player*> players = server->m_env.getPlayers(true);
761 // Write player count
762 u16 playercount = players.size();
763 writeU16(buf, playercount);
764 os.write((char*)buf, 2);
766 core::list<Player*>::Iterator i;
767 for(i = players.begin();
768 i != players.end(); i++)
772 v3f pf = player->getPosition();
773 v3f sf = player->getSpeed();
775 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
776 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
777 s32 pitch_i (player->getPitch() * 100);
778 s32 yaw_i (player->getYaw() * 100);
780 writeU16(buf, player->peer_id);
781 os.write((char*)buf, 2);
782 writeV3S32(buf, position_i);
783 os.write((char*)buf, 12);
784 writeV3S32(buf, speed_i);
785 os.write((char*)buf, 12);
786 writeS32(buf, pitch_i);
787 os.write((char*)buf, 4);
788 writeS32(buf, yaw_i);
789 os.write((char*)buf, 4);
793 Get and write object data
799 For making players to be able to build to their nearby
800 environment (building is not possible on blocks that are not
803 - Add blocks to emerge queue if they are not found
805 SUGGESTION: These could be ignored from the backside of the player
808 Player *player = server->m_env.getPlayer(peer_id);
812 v3f playerpos = player->getPosition();
813 v3f playerspeed = player->getSpeed();
815 v3s16 center_nodepos = floatToInt(playerpos, BS);
816 v3s16 center = getNodeBlockPos(center_nodepos);
818 s16 d_max = g_settings.getS16("active_object_range");
820 // Number of blocks whose objects were written to bos
823 std::ostringstream bos(std::ios_base::binary);
825 for(s16 d = 0; d <= d_max; d++)
827 core::list<v3s16> list;
828 getFacePositions(list, d);
830 core::list<v3s16>::Iterator li;
831 for(li=list.begin(); li!=list.end(); li++)
833 v3s16 p = *li + center;
836 Ignore blocks that haven't been sent to the client
839 if(m_blocks_sent.find(p) == NULL)
843 // Try stepping block and add it to a send queue
848 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
851 Step block if not in stepped_blocks and add to stepped_blocks.
853 if(stepped_blocks.find(p) == NULL)
855 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
856 stepped_blocks.insert(p, true);
857 block->setChangedFlag();
860 // Skip block if there are no objects
861 if(block->getObjectCount() == 0)
870 bos.write((char*)buf, 6);
873 block->serializeObjects(bos, serialization_version);
878 Stop collecting objects if data is already too big
880 // Sum of player and object data sizes
881 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
882 // break out if data too big
883 if(sum > MAX_OBJECTDATA_SIZE)
885 goto skip_subsequent;
889 catch(InvalidPositionException &e)
892 // Add it to the emerge queue and trigger the thread.
893 // Fetch the block only if it is on disk.
895 // Grab and increment counter
896 /*SharedPtr<JMutexAutoLock> lock
897 (m_num_blocks_in_emerge_queue.getLock());
898 m_num_blocks_in_emerge_queue.m_value++;*/
900 // Add to queue as an anonymous fetch from disk
901 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
902 server->m_emerge_queue.addBlock(0, p, flags);
903 server->m_emergethread.trigger();
911 writeU16(buf, blockcount);
912 os.write((char*)buf, 2);
914 // Write block objects
921 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
924 std::string s = os.str();
925 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
926 // Send as unreliable
927 server->m_con.Send(peer_id, 0, data, false);
930 void RemoteClient::GotBlock(v3s16 p)
932 if(m_blocks_sending.find(p) != NULL)
933 m_blocks_sending.remove(p);
936 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
937 " m_blocks_sending"<<std::endl;*/
938 m_excess_gotblocks++;
940 m_blocks_sent.insert(p, true);
943 void RemoteClient::SentBlock(v3s16 p)
945 if(m_blocks_sending.find(p) == NULL)
946 m_blocks_sending.insert(p, 0.0);
948 dstream<<"RemoteClient::SentBlock(): Sent block"
949 " already in m_blocks_sending"<<std::endl;
952 void RemoteClient::SetBlockNotSent(v3s16 p)
954 m_nearest_unsent_d = 0;
956 if(m_blocks_sending.find(p) != NULL)
957 m_blocks_sending.remove(p);
958 if(m_blocks_sent.find(p) != NULL)
959 m_blocks_sent.remove(p);
962 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
964 m_nearest_unsent_d = 0;
966 for(core::map<v3s16, MapBlock*>::Iterator
967 i = blocks.getIterator();
968 i.atEnd()==false; i++)
970 v3s16 p = i.getNode()->getKey();
972 if(m_blocks_sending.find(p) != NULL)
973 m_blocks_sending.remove(p);
974 if(m_blocks_sent.find(p) != NULL)
975 m_blocks_sent.remove(p);
983 PlayerInfo::PlayerInfo()
989 void PlayerInfo::PrintLine(std::ostream *s)
992 (*s)<<"\""<<name<<"\" ("
993 <<(position.X/10)<<","<<(position.Y/10)
994 <<","<<(position.Z/10)<<") ";
996 (*s)<<" avg_rtt="<<avg_rtt;
1000 u32 PIChecksum(core::list<PlayerInfo> &l)
1002 core::list<PlayerInfo>::Iterator i;
1005 for(i=l.begin(); i!=l.end(); i++)
1007 checksum += a * (i->id+1);
1008 checksum ^= 0x435aafcd;
1019 std::string mapsavedir
1021 m_env(new ServerMap(mapsavedir), this),
1022 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1023 m_authmanager(mapsavedir+"/auth.txt"),
1025 m_emergethread(this),
1027 m_time_of_day_send_timer(0),
1029 m_mapsavedir(mapsavedir),
1030 m_shutdown_requested(false),
1031 m_ignore_map_edit_events(false),
1032 m_ignore_map_edit_events_peer_id(0)
1034 m_liquid_transform_timer = 0.0;
1035 m_print_info_timer = 0.0;
1036 m_objectdata_timer = 0.0;
1037 m_emergethread_trigger_timer = 0.0;
1038 m_savemap_timer = 0.0;
1042 m_step_dtime_mutex.Init();
1045 // Register us to receive map edit events
1046 m_env.getMap().addEventReceiver(this);
1048 // If file exists, load environment metadata
1049 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1051 dstream<<"Server: Loading environment metadata"<<std::endl;
1052 m_env.loadMeta(m_mapsavedir);
1056 dstream<<"Server: Loading players"<<std::endl;
1057 m_env.deSerializePlayers(m_mapsavedir);
1062 dstream<<"Server::~Server()"<<std::endl;
1065 Send shutdown message
1068 JMutexAutoLock conlock(m_con_mutex);
1070 std::wstring line = L"*** Server shutting down";
1073 Send the message to clients
1075 for(core::map<u16, RemoteClient*>::Iterator
1076 i = m_clients.getIterator();
1077 i.atEnd() == false; i++)
1079 // Get client and check that it is valid
1080 RemoteClient *client = i.getNode()->getValue();
1081 assert(client->peer_id == i.getNode()->getKey());
1082 if(client->serialization_version == SER_FMT_VER_INVALID)
1086 SendChatMessage(client->peer_id, line);
1088 catch(con::PeerNotFoundException &e)
1096 dstream<<"Server: Saving players"<<std::endl;
1097 m_env.serializePlayers(m_mapsavedir);
1100 Save environment metadata
1102 dstream<<"Server: Saving environment metadata"<<std::endl;
1103 m_env.saveMeta(m_mapsavedir);
1114 JMutexAutoLock clientslock(m_con_mutex);
1116 for(core::map<u16, RemoteClient*>::Iterator
1117 i = m_clients.getIterator();
1118 i.atEnd() == false; i++)
1121 // NOTE: These are removed by env destructor
1123 u16 peer_id = i.getNode()->getKey();
1124 JMutexAutoLock envlock(m_env_mutex);
1125 m_env.removePlayer(peer_id);
1129 delete i.getNode()->getValue();
1134 void Server::start(unsigned short port)
1136 DSTACK(__FUNCTION_NAME);
1137 // Stop thread if already running
1140 // Initialize connection
1141 m_con.setTimeoutMs(30);
1145 m_thread.setRun(true);
1148 dout_server<<"Server: Started on port "<<port<<std::endl;
1153 DSTACK(__FUNCTION_NAME);
1155 // Stop threads (set run=false first so both start stopping)
1156 m_thread.setRun(false);
1157 m_emergethread.setRun(false);
1159 m_emergethread.stop();
1161 dout_server<<"Server: Threads stopped"<<std::endl;
1164 void Server::step(float dtime)
1166 DSTACK(__FUNCTION_NAME);
1171 JMutexAutoLock lock(m_step_dtime_mutex);
1172 m_step_dtime += dtime;
1176 void Server::AsyncRunStep()
1178 DSTACK(__FUNCTION_NAME);
1182 JMutexAutoLock lock1(m_step_dtime_mutex);
1183 dtime = m_step_dtime;
1187 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1188 "blocks to clients");
1189 // Send blocks to clients
1196 //dstream<<"Server steps "<<dtime<<std::endl;
1197 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1200 JMutexAutoLock lock1(m_step_dtime_mutex);
1201 m_step_dtime -= dtime;
1208 m_uptime.set(m_uptime.get() + dtime);
1212 Update m_time_of_day and overall game time
1215 JMutexAutoLock envlock(m_env_mutex);
1217 m_time_counter += dtime;
1218 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1219 u32 units = (u32)(m_time_counter*speed);
1220 m_time_counter -= (f32)units / speed;
1222 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1224 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1227 Send to clients at constant intervals
1230 m_time_of_day_send_timer -= dtime;
1231 if(m_time_of_day_send_timer < 0.0)
1233 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1235 //JMutexAutoLock envlock(m_env_mutex);
1236 JMutexAutoLock conlock(m_con_mutex);
1238 for(core::map<u16, RemoteClient*>::Iterator
1239 i = m_clients.getIterator();
1240 i.atEnd() == false; i++)
1242 RemoteClient *client = i.getNode()->getValue();
1243 //Player *player = m_env.getPlayer(client->peer_id);
1245 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1246 m_env.getTimeOfDay());
1248 m_con.Send(client->peer_id, 0, data, true);
1254 // Process connection's timeouts
1255 JMutexAutoLock lock2(m_con_mutex);
1256 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1257 m_con.RunTimeouts(dtime);
1261 // This has to be called so that the client list gets synced
1262 // with the peer list of the connection
1263 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1264 handlePeerChanges();
1269 // This also runs Map's timers
1270 JMutexAutoLock lock(m_env_mutex);
1271 ScopeProfiler sp(&g_profiler, "Server: environment step");
1282 m_liquid_transform_timer += dtime;
1283 if(m_liquid_transform_timer >= 1.00)
1285 m_liquid_transform_timer -= 1.00;
1287 JMutexAutoLock lock(m_env_mutex);
1289 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1291 core::map<v3s16, MapBlock*> modified_blocks;
1292 m_env.getMap().transformLiquids(modified_blocks);
1297 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1298 ServerMap &map = ((ServerMap&)m_env.getMap());
1299 map.updateLighting(modified_blocks, lighting_modified_blocks);
1301 // Add blocks modified by lighting to modified_blocks
1302 for(core::map<v3s16, MapBlock*>::Iterator
1303 i = lighting_modified_blocks.getIterator();
1304 i.atEnd() == false; i++)
1306 MapBlock *block = i.getNode()->getValue();
1307 modified_blocks.insert(block->getPos(), block);
1311 Set the modified blocks unsent for all the clients
1314 JMutexAutoLock lock2(m_con_mutex);
1316 for(core::map<u16, RemoteClient*>::Iterator
1317 i = m_clients.getIterator();
1318 i.atEnd() == false; i++)
1320 RemoteClient *client = i.getNode()->getValue();
1322 if(modified_blocks.size() > 0)
1324 // Remove block from sent history
1325 client->SetBlocksNotSent(modified_blocks);
1330 // Periodically print some info
1332 float &counter = m_print_info_timer;
1338 JMutexAutoLock lock2(m_con_mutex);
1340 for(core::map<u16, RemoteClient*>::Iterator
1341 i = m_clients.getIterator();
1342 i.atEnd() == false; i++)
1344 //u16 peer_id = i.getNode()->getKey();
1345 RemoteClient *client = i.getNode()->getValue();
1346 Player *player = m_env.getPlayer(client->peer_id);
1349 std::cout<<player->getName()<<"\t";
1350 client->PrintInfo(std::cout);
1355 //if(g_settings.getBool("enable_experimental"))
1359 Check added and deleted active objects
1362 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1363 JMutexAutoLock envlock(m_env_mutex);
1364 JMutexAutoLock conlock(m_con_mutex);
1366 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1368 // Radius inside which objects are active
1371 for(core::map<u16, RemoteClient*>::Iterator
1372 i = m_clients.getIterator();
1373 i.atEnd() == false; i++)
1375 RemoteClient *client = i.getNode()->getValue();
1376 Player *player = m_env.getPlayer(client->peer_id);
1379 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1380 <<" has no associated player"<<std::endl;
1383 v3s16 pos = floatToInt(player->getPosition(), BS);
1385 core::map<u16, bool> removed_objects;
1386 core::map<u16, bool> added_objects;
1387 m_env.getRemovedActiveObjects(pos, radius,
1388 client->m_known_objects, removed_objects);
1389 m_env.getAddedActiveObjects(pos, radius,
1390 client->m_known_objects, added_objects);
1392 // Ignore if nothing happened
1393 if(removed_objects.size() == 0 && added_objects.size() == 0)
1395 //dstream<<"INFO: active objects: none changed"<<std::endl;
1399 std::string data_buffer;
1403 // Handle removed objects
1404 writeU16((u8*)buf, removed_objects.size());
1405 data_buffer.append(buf, 2);
1406 for(core::map<u16, bool>::Iterator
1407 i = removed_objects.getIterator();
1408 i.atEnd()==false; i++)
1411 u16 id = i.getNode()->getKey();
1412 ServerActiveObject* obj = m_env.getActiveObject(id);
1414 // Add to data buffer for sending
1415 writeU16((u8*)buf, i.getNode()->getKey());
1416 data_buffer.append(buf, 2);
1418 // Remove from known objects
1419 client->m_known_objects.remove(i.getNode()->getKey());
1421 if(obj && obj->m_known_by_count > 0)
1422 obj->m_known_by_count--;
1425 // Handle added objects
1426 writeU16((u8*)buf, added_objects.size());
1427 data_buffer.append(buf, 2);
1428 for(core::map<u16, bool>::Iterator
1429 i = added_objects.getIterator();
1430 i.atEnd()==false; i++)
1433 u16 id = i.getNode()->getKey();
1434 ServerActiveObject* obj = m_env.getActiveObject(id);
1437 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1439 dstream<<"WARNING: "<<__FUNCTION_NAME
1440 <<": NULL object"<<std::endl;
1442 type = obj->getType();
1444 // Add to data buffer for sending
1445 writeU16((u8*)buf, id);
1446 data_buffer.append(buf, 2);
1447 writeU8((u8*)buf, type);
1448 data_buffer.append(buf, 1);
1451 data_buffer.append(serializeLongString(
1452 obj->getClientInitializationData()));
1454 data_buffer.append(serializeLongString(""));
1456 // Add to known objects
1457 client->m_known_objects.insert(i.getNode()->getKey(), false);
1460 obj->m_known_by_count++;
1464 SharedBuffer<u8> reply(2 + data_buffer.size());
1465 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1466 memcpy((char*)&reply[2], data_buffer.c_str(),
1467 data_buffer.size());
1469 m_con.Send(client->peer_id, 0, reply, true);
1471 dstream<<"INFO: Server: Sent object remove/add: "
1472 <<removed_objects.size()<<" removed, "
1473 <<added_objects.size()<<" added, "
1474 <<"packet size is "<<reply.getSize()<<std::endl;
1479 Collect a list of all the objects known by the clients
1480 and report it back to the environment.
1483 core::map<u16, bool> all_known_objects;
1485 for(core::map<u16, RemoteClient*>::Iterator
1486 i = m_clients.getIterator();
1487 i.atEnd() == false; i++)
1489 RemoteClient *client = i.getNode()->getValue();
1490 // Go through all known objects of client
1491 for(core::map<u16, bool>::Iterator
1492 i = client->m_known_objects.getIterator();
1493 i.atEnd()==false; i++)
1495 u16 id = i.getNode()->getKey();
1496 all_known_objects[id] = true;
1500 m_env.setKnownActiveObjects(whatever);
1506 Send object messages
1509 JMutexAutoLock envlock(m_env_mutex);
1510 JMutexAutoLock conlock(m_con_mutex);
1512 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1515 // Value = data sent by object
1516 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1518 // Get active object messages from environment
1521 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1525 core::list<ActiveObjectMessage>* message_list = NULL;
1526 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1527 n = buffered_messages.find(aom.id);
1530 message_list = new core::list<ActiveObjectMessage>;
1531 buffered_messages.insert(aom.id, message_list);
1535 message_list = n->getValue();
1537 message_list->push_back(aom);
1540 // Route data to every client
1541 for(core::map<u16, RemoteClient*>::Iterator
1542 i = m_clients.getIterator();
1543 i.atEnd()==false; i++)
1545 RemoteClient *client = i.getNode()->getValue();
1546 std::string reliable_data;
1547 std::string unreliable_data;
1548 // Go through all objects in message buffer
1549 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1550 j = buffered_messages.getIterator();
1551 j.atEnd()==false; j++)
1553 // If object is not known by client, skip it
1554 u16 id = j.getNode()->getKey();
1555 if(client->m_known_objects.find(id) == NULL)
1557 // Get message list of object
1558 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1559 // Go through every message
1560 for(core::list<ActiveObjectMessage>::Iterator
1561 k = list->begin(); k != list->end(); k++)
1563 // Compose the full new data with header
1564 ActiveObjectMessage aom = *k;
1565 std::string new_data;
1568 writeU16((u8*)&buf[0], aom.id);
1569 new_data.append(buf, 2);
1571 new_data += serializeString(aom.datastring);
1572 // Add data to buffer
1574 reliable_data += new_data;
1576 unreliable_data += new_data;
1580 reliable_data and unreliable_data are now ready.
1583 if(reliable_data.size() > 0)
1585 SharedBuffer<u8> reply(2 + reliable_data.size());
1586 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1587 memcpy((char*)&reply[2], reliable_data.c_str(),
1588 reliable_data.size());
1590 m_con.Send(client->peer_id, 0, reply, true);
1592 if(unreliable_data.size() > 0)
1594 SharedBuffer<u8> reply(2 + unreliable_data.size());
1595 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1596 memcpy((char*)&reply[2], unreliable_data.c_str(),
1597 unreliable_data.size());
1598 // Send as unreliable
1599 m_con.Send(client->peer_id, 0, reply, false);
1602 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1604 dstream<<"INFO: Server: Size of object message data: "
1605 <<"reliable: "<<reliable_data.size()
1606 <<", unreliable: "<<unreliable_data.size()
1611 // Clear buffered_messages
1612 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1613 i = buffered_messages.getIterator();
1614 i.atEnd()==false; i++)
1616 delete i.getNode()->getValue();
1620 } // enable_experimental
1623 Send queued-for-sending map edit events.
1626 while(m_unsent_map_edit_queue.size() != 0)
1628 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1630 if(event->type == MEET_ADDNODE)
1632 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1633 sendAddNode(event->p, event->n, event->already_known_by_peer);
1635 else if(event->type == MEET_REMOVENODE)
1637 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1638 sendRemoveNode(event->p, event->already_known_by_peer);
1640 else if(event->type == MEET_OTHER)
1642 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1647 dstream<<"WARNING: Server: Unknown MapEditEvent "
1648 <<((u32)event->type)<<std::endl;
1656 Send object positions
1657 TODO: Get rid of MapBlockObjects
1660 float &counter = m_objectdata_timer;
1662 if(counter >= g_settings.getFloat("objectdata_interval"))
1664 JMutexAutoLock lock1(m_env_mutex);
1665 JMutexAutoLock lock2(m_con_mutex);
1667 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1669 SendObjectData(counter);
1679 //TimeTaker timer("Step node metadata");
1681 JMutexAutoLock envlock(m_env_mutex);
1682 JMutexAutoLock conlock(m_con_mutex);
1684 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1686 core::map<v3s16, MapBlock*> changed_blocks;
1687 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1689 for(core::map<v3s16, MapBlock*>::Iterator
1690 i = changed_blocks.getIterator();
1691 i.atEnd() == false; i++)
1693 MapBlock *block = i.getNode()->getValue();
1695 for(core::map<u16, RemoteClient*>::Iterator
1696 i = m_clients.getIterator();
1697 i.atEnd()==false; i++)
1699 RemoteClient *client = i.getNode()->getValue();
1700 client->SetBlockNotSent(block->getPos());
1706 Trigger emergethread (it somehow gets to a non-triggered but
1707 bysy state sometimes)
1710 float &counter = m_emergethread_trigger_timer;
1716 m_emergethread.trigger();
1720 // Save map, players and auth stuff
1722 float &counter = m_savemap_timer;
1724 if(counter >= g_settings.getFloat("server_map_save_interval"))
1728 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1731 m_authmanager.save();
1734 JMutexAutoLock lock(m_env_mutex);
1735 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1737 // Save only changed parts
1738 m_env.getMap().save(true);
1740 // Delete unused sectors
1741 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1742 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1743 if(deleted_count > 0)
1745 dout_server<<"Server: Unloaded "<<deleted_count
1746 <<" sectors from memory"<<std::endl;
1750 m_env.serializePlayers(m_mapsavedir);
1752 // Save environment metadata
1753 m_env.saveMeta(m_mapsavedir);
1759 void Server::Receive()
1761 DSTACK(__FUNCTION_NAME);
1762 u32 data_maxsize = 10000;
1763 Buffer<u8> data(data_maxsize);
1768 JMutexAutoLock conlock(m_con_mutex);
1769 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1772 // This has to be called so that the client list gets synced
1773 // with the peer list of the connection
1774 handlePeerChanges();
1776 ProcessData(*data, datasize, peer_id);
1778 catch(con::InvalidIncomingDataException &e)
1780 derr_server<<"Server::Receive(): "
1781 "InvalidIncomingDataException: what()="
1782 <<e.what()<<std::endl;
1784 catch(con::PeerNotFoundException &e)
1786 //NOTE: This is not needed anymore
1788 // The peer has been disconnected.
1789 // Find the associated player and remove it.
1791 /*JMutexAutoLock envlock(m_env_mutex);
1793 dout_server<<"ServerThread: peer_id="<<peer_id
1794 <<" has apparently closed connection. "
1795 <<"Removing player."<<std::endl;
1797 m_env.removePlayer(peer_id);*/
1801 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1803 DSTACK(__FUNCTION_NAME);
1804 // Environment is locked first.
1805 JMutexAutoLock envlock(m_env_mutex);
1806 JMutexAutoLock conlock(m_con_mutex);
1810 peer = m_con.GetPeer(peer_id);
1812 catch(con::PeerNotFoundException &e)
1814 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1815 <<peer_id<<" not found"<<std::endl;
1819 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1827 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1829 if(command == TOSERVER_INIT)
1831 // [0] u16 TOSERVER_INIT
1832 // [2] u8 SER_FMT_VER_HIGHEST
1833 // [3] u8[20] player_name
1834 // [23] u8[28] password <--- can be sent without this, from old versions
1836 if(datasize < 2+1+PLAYERNAME_SIZE)
1839 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1840 <<peer->id<<std::endl;
1842 // First byte after command is maximum supported
1843 // serialization version
1844 u8 client_max = data[2];
1845 u8 our_max = SER_FMT_VER_HIGHEST;
1846 // Use the highest version supported by both
1847 u8 deployed = core::min_(client_max, our_max);
1848 // If it's lower than the lowest supported, give up.
1849 if(deployed < SER_FMT_VER_LOWEST)
1850 deployed = SER_FMT_VER_INVALID;
1852 //peer->serialization_version = deployed;
1853 getClient(peer->id)->pending_serialization_version = deployed;
1855 if(deployed == SER_FMT_VER_INVALID)
1857 derr_server<<DTIME<<"Server: Cannot negotiate "
1858 "serialization version with peer "
1859 <<peer_id<<std::endl;
1868 char playername[PLAYERNAME_SIZE];
1869 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1871 playername[i] = data[3+i];
1873 playername[PLAYERNAME_SIZE-1] = 0;
1875 if(playername[0]=='\0')
1877 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1878 SendAccessDenied(m_con, peer_id,
1883 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1885 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1886 SendAccessDenied(m_con, peer_id,
1887 L"Name contains unallowed characters");
1892 char password[PASSWORD_SIZE];
1893 if(datasize == 2+1+PLAYERNAME_SIZE)
1895 // old version - assume blank password
1900 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1902 password[i] = data[23+i];
1904 password[PASSWORD_SIZE-1] = 0;
1907 std::string checkpwd;
1908 if(m_authmanager.exists(playername))
1910 checkpwd = m_authmanager.getPassword(playername);
1914 checkpwd = g_settings.get("default_password");
1917 if(password != checkpwd)
1919 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1920 <<": supplied invalid password for "
1921 <<playername<<std::endl;
1922 SendAccessDenied(m_con, peer_id, L"Invalid password");
1926 // Add player to auth manager
1927 if(m_authmanager.exists(playername) == false)
1929 derr_server<<DTIME<<"Server: adding player "<<playername
1930 <<" to auth manager"<<std::endl;
1931 m_authmanager.add(playername);
1932 m_authmanager.setPassword(playername, checkpwd);
1933 m_authmanager.setPrivs(playername,
1934 stringToPrivs(g_settings.get("default_privs")));
1935 m_authmanager.save();
1939 Player *player = emergePlayer(playername, password, peer_id);
1943 // DEBUG: Test serialization
1944 std::ostringstream test_os;
1945 player->serialize(test_os);
1946 dstream<<"Player serialization test: \""<<test_os.str()
1948 std::istringstream test_is(test_os.str());
1949 player->deSerialize(test_is);
1952 // If failed, cancel
1955 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1956 <<": failed to emerge player"<<std::endl;
1961 // If a client is already connected to the player, cancel
1962 if(player->peer_id != 0)
1964 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1965 <<" tried to connect to "
1966 "an already connected player (peer_id="
1967 <<player->peer_id<<")"<<std::endl;
1970 // Set client of player
1971 player->peer_id = peer_id;
1974 // Check if player doesn't exist
1976 throw con::InvalidIncomingDataException
1977 ("Server::ProcessData(): INIT: Player doesn't exist");
1979 /*// update name if it was supplied
1980 if(datasize >= 20+3)
1983 player->updateName((const char*)&data[3]);
1987 Answer with a TOCLIENT_INIT
1990 SharedBuffer<u8> reply(2+1+6+8);
1991 writeU16(&reply[0], TOCLIENT_INIT);
1992 writeU8(&reply[2], deployed);
1993 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1994 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1995 writeU64(&reply[2+1+6], 0); // no seed
1998 m_con.Send(peer_id, 0, reply, true);
2002 Send complete position information
2004 SendMovePlayer(player);
2009 if(command == TOSERVER_INIT2)
2011 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2012 <<peer->id<<std::endl;
2015 getClient(peer->id)->serialization_version
2016 = getClient(peer->id)->pending_serialization_version;
2019 Send some initialization data
2022 // Send player info to all players
2025 // Send inventory to player
2026 UpdateCrafting(peer->id);
2027 SendInventory(peer->id);
2031 Player *player = m_env.getPlayer(peer_id);
2032 SendPlayerHP(player);
2037 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2038 m_env.getTimeOfDay());
2039 m_con.Send(peer->id, 0, data, true);
2042 // Send information about server to player in chat
2043 SendChatMessage(peer_id, getStatusString());
2045 // Send information about joining in chat
2047 std::wstring name = L"unknown";
2048 Player *player = m_env.getPlayer(peer_id);
2050 name = narrow_to_wide(player->getName());
2052 std::wstring message;
2055 message += L" joined game";
2056 BroadcastChatMessage(message);
2062 if(peer_ser_ver == SER_FMT_VER_INVALID)
2064 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2065 " serialization format invalid or not initialized."
2066 " Skipping incoming command="<<command<<std::endl;
2070 Player *player = m_env.getPlayer(peer_id);
2073 derr_server<<"Server::ProcessData(): Cancelling: "
2074 "No player for peer_id="<<peer_id
2078 if(command == TOSERVER_PLAYERPOS)
2080 if(datasize < 2+12+12+4+4)
2084 v3s32 ps = readV3S32(&data[start+2]);
2085 v3s32 ss = readV3S32(&data[start+2+12]);
2086 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2087 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2088 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2089 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2090 pitch = wrapDegrees(pitch);
2091 yaw = wrapDegrees(yaw);
2092 player->setPosition(position);
2093 player->setSpeed(speed);
2094 player->setPitch(pitch);
2095 player->setYaw(yaw);
2097 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2098 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2099 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2101 else if(command == TOSERVER_GOTBLOCKS)
2114 u16 count = data[2];
2115 for(u16 i=0; i<count; i++)
2117 if((s16)datasize < 2+1+(i+1)*6)
2118 throw con::InvalidIncomingDataException
2119 ("GOTBLOCKS length is too short");
2120 v3s16 p = readV3S16(&data[2+1+i*6]);
2121 /*dstream<<"Server: GOTBLOCKS ("
2122 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2123 RemoteClient *client = getClient(peer_id);
2124 client->GotBlock(p);
2127 else if(command == TOSERVER_DELETEDBLOCKS)
2140 u16 count = data[2];
2141 for(u16 i=0; i<count; i++)
2143 if((s16)datasize < 2+1+(i+1)*6)
2144 throw con::InvalidIncomingDataException
2145 ("DELETEDBLOCKS length is too short");
2146 v3s16 p = readV3S16(&data[2+1+i*6]);
2147 /*dstream<<"Server: DELETEDBLOCKS ("
2148 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2149 RemoteClient *client = getClient(peer_id);
2150 client->SetBlockNotSent(p);
2153 else if(command == TOSERVER_CLICK_OBJECT)
2158 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2163 [2] u8 button (0=left, 1=right)
2168 u8 button = readU8(&data[2]);
2170 p.X = readS16(&data[3]);
2171 p.Y = readS16(&data[5]);
2172 p.Z = readS16(&data[7]);
2173 s16 id = readS16(&data[9]);
2174 //u16 item_i = readU16(&data[11]);
2176 MapBlock *block = NULL;
2179 block = m_env.getMap().getBlockNoCreate(p);
2181 catch(InvalidPositionException &e)
2183 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2187 MapBlockObject *obj = block->getObject(id);
2191 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2195 //TODO: Check that object is reasonably close
2200 InventoryList *ilist = player->inventory.getList("main");
2201 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2204 // Skip if inventory has no free space
2205 if(ilist->getUsedSlots() == ilist->getSize())
2207 dout_server<<"Player inventory has no free space"<<std::endl;
2212 Create the inventory item
2214 InventoryItem *item = NULL;
2215 // If it is an item-object, take the item from it
2216 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2218 item = ((ItemObject*)obj)->createInventoryItem();
2220 // Else create an item of the object
2223 item = new MapBlockObjectItem
2224 (obj->getInventoryString());
2227 // Add to inventory and send inventory
2228 ilist->addItem(item);
2229 UpdateCrafting(player->peer_id);
2230 SendInventory(player->peer_id);
2233 // Remove from block
2234 block->removeObject(id);
2237 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2242 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2248 [2] u8 button (0=left, 1=right)
2252 u8 button = readU8(&data[2]);
2253 u16 id = readS16(&data[3]);
2254 u16 item_i = readU16(&data[11]);
2256 ServerActiveObject *obj = m_env.getActiveObject(id);
2260 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2265 //TODO: Check that object is reasonably close
2267 // Left click, pick object up (usually)
2270 InventoryList *ilist = player->inventory.getList("main");
2271 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2274 // Skip if inventory has no free space
2275 if(ilist->getUsedSlots() == ilist->getSize())
2277 dout_server<<"Player inventory has no free space"<<std::endl;
2281 // Skip if object has been removed
2286 Create the inventory item
2288 InventoryItem *item = obj->createPickedUpItem();
2292 // Add to inventory and send inventory
2293 ilist->addItem(item);
2294 UpdateCrafting(player->peer_id);
2295 SendInventory(player->peer_id);
2297 // Remove object from environment
2298 obj->m_removed = true;
2303 Item cannot be picked up. Punch it instead.
2306 ToolItem *titem = NULL;
2307 std::string toolname = "";
2309 InventoryList *mlist = player->inventory.getList("main");
2312 InventoryItem *item = mlist->getItem(item_i);
2313 if(item && (std::string)item->getName() == "ToolItem")
2315 titem = (ToolItem*)item;
2316 toolname = titem->getToolName();
2320 u16 wear = obj->punch(toolname);
2324 bool weared_out = titem->addWear(wear);
2326 mlist->deleteItem(item_i);
2327 SendInventory(player->peer_id);
2333 else if(command == TOSERVER_GROUND_ACTION)
2341 [3] v3s16 nodepos_undersurface
2342 [9] v3s16 nodepos_abovesurface
2347 2: stop digging (all parameters ignored)
2348 3: digging completed
2350 u8 action = readU8(&data[2]);
2352 p_under.X = readS16(&data[3]);
2353 p_under.Y = readS16(&data[5]);
2354 p_under.Z = readS16(&data[7]);
2356 p_over.X = readS16(&data[9]);
2357 p_over.Y = readS16(&data[11]);
2358 p_over.Z = readS16(&data[13]);
2359 u16 item_i = readU16(&data[15]);
2361 //TODO: Check that target is reasonably close
2369 NOTE: This can be used in the future to check if
2370 somebody is cheating, by checking the timing.
2377 else if(action == 2)
2380 RemoteClient *client = getClient(peer->id);
2381 JMutexAutoLock digmutex(client->m_dig_mutex);
2382 client->m_dig_tool_item = -1;
2387 3: Digging completed
2389 else if(action == 3)
2391 // Mandatory parameter; actually used for nothing
2392 core::map<v3s16, MapBlock*> modified_blocks;
2394 u8 material = CONTENT_IGNORE;
2395 u8 mineral = MINERAL_NONE;
2397 bool cannot_remove_node = false;
2401 MapNode n = m_env.getMap().getNode(p_under);
2403 mineral = n.getMineral();
2404 // Get material at position
2406 // If not yet cancelled
2407 if(cannot_remove_node == false)
2409 // If it's not diggable, do nothing
2410 if(content_diggable(material) == false)
2412 derr_server<<"Server: Not finishing digging: "
2413 <<"Node not diggable"
2415 cannot_remove_node = true;
2418 // If not yet cancelled
2419 if(cannot_remove_node == false)
2421 // Get node metadata
2422 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2423 if(meta && meta->nodeRemovalDisabled() == true)
2425 derr_server<<"Server: Not finishing digging: "
2426 <<"Node metadata disables removal"
2428 cannot_remove_node = true;
2432 catch(InvalidPositionException &e)
2434 derr_server<<"Server: Not finishing digging: Node not found."
2435 <<" Adding block to emerge queue."
2437 m_emerge_queue.addBlock(peer_id,
2438 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2439 cannot_remove_node = true;
2442 // Make sure the player is allowed to do it
2443 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2445 dstream<<"Player "<<player->getName()<<" cannot remove node"
2446 <<" because privileges are "<<getPlayerPrivs(player)
2448 cannot_remove_node = true;
2452 If node can't be removed, set block to be re-sent to
2455 if(cannot_remove_node)
2457 derr_server<<"Server: Not finishing digging."<<std::endl;
2459 // Client probably has wrong data.
2460 // Set block not sent, so that client will get
2462 dstream<<"Client "<<peer_id<<" tried to dig "
2463 <<"node; but node cannot be removed."
2464 <<" setting MapBlock not sent."<<std::endl;
2465 RemoteClient *client = getClient(peer_id);
2466 v3s16 blockpos = getNodeBlockPos(p_under);
2467 client->SetBlockNotSent(blockpos);
2473 Send the removal to all other clients.
2474 - If other player is close, send REMOVENODE
2475 - Otherwise set blocks not sent
2477 core::list<u16> far_players;
2478 sendRemoveNode(p_under, peer_id, &far_players, 100);
2481 Update and send inventory
2484 if(g_settings.getBool("creative_mode") == false)
2489 InventoryList *mlist = player->inventory.getList("main");
2492 InventoryItem *item = mlist->getItem(item_i);
2493 if(item && (std::string)item->getName() == "ToolItem")
2495 ToolItem *titem = (ToolItem*)item;
2496 std::string toolname = titem->getToolName();
2498 // Get digging properties for material and tool
2499 DiggingProperties prop =
2500 getDiggingProperties(material, toolname);
2502 if(prop.diggable == false)
2504 derr_server<<"Server: WARNING: Player digged"
2505 <<" with impossible material + tool"
2506 <<" combination"<<std::endl;
2509 bool weared_out = titem->addWear(prop.wear);
2513 mlist->deleteItem(item_i);
2519 Add dug item to inventory
2522 InventoryItem *item = NULL;
2524 if(mineral != MINERAL_NONE)
2525 item = getDiggedMineralItem(mineral);
2530 std::string &dug_s = content_features(material).dug_item;
2533 std::istringstream is(dug_s, std::ios::binary);
2534 item = InventoryItem::deSerialize(is);
2540 // Add a item to inventory
2541 player->inventory.addItem("main", item);
2544 UpdateCrafting(player->peer_id);
2545 SendInventory(player->peer_id);
2551 (this takes some time so it is done after the quick stuff)
2553 m_ignore_map_edit_events = true;
2554 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2555 m_ignore_map_edit_events = false;
2558 Set blocks not sent to far players
2560 for(core::list<u16>::Iterator
2561 i = far_players.begin();
2562 i != far_players.end(); i++)
2565 RemoteClient *client = getClient(peer_id);
2568 client->SetBlocksNotSent(modified_blocks);
2575 else if(action == 1)
2578 InventoryList *ilist = player->inventory.getList("main");
2583 InventoryItem *item = ilist->getItem(item_i);
2585 // If there is no item, it is not possible to add it anywhere
2590 Handle material items
2592 if(std::string("MaterialItem") == item->getName())
2595 // Don't add a node if this is not a free space
2596 MapNode n2 = m_env.getMap().getNode(p_over);
2597 bool no_enough_privs =
2598 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2600 dstream<<"Player "<<player->getName()<<" cannot add node"
2601 <<" because privileges are "<<getPlayerPrivs(player)
2604 if(content_buildable_to(n2.d) == false
2607 // Client probably has wrong data.
2608 // Set block not sent, so that client will get
2610 dstream<<"Client "<<peer_id<<" tried to place"
2611 <<" node in invalid position; setting"
2612 <<" MapBlock not sent."<<std::endl;
2613 RemoteClient *client = getClient(peer_id);
2614 v3s16 blockpos = getNodeBlockPos(p_over);
2615 client->SetBlockNotSent(blockpos);
2619 catch(InvalidPositionException &e)
2621 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2622 <<" Adding block to emerge queue."
2624 m_emerge_queue.addBlock(peer_id,
2625 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2629 // Reset build time counter
2630 getClient(peer->id)->m_time_from_building = 0.0;
2633 MaterialItem *mitem = (MaterialItem*)item;
2635 n.d = mitem->getMaterial();
2636 if(content_features(n.d).wall_mounted)
2637 n.dir = packDir(p_under - p_over);
2642 core::list<u16> far_players;
2643 sendAddNode(p_over, n, 0, &far_players, 100);
2648 InventoryList *ilist = player->inventory.getList("main");
2649 if(g_settings.getBool("creative_mode") == false && ilist)
2651 // Remove from inventory and send inventory
2652 if(mitem->getCount() == 1)
2653 ilist->deleteItem(item_i);
2657 UpdateCrafting(peer_id);
2658 SendInventory(peer_id);
2664 This takes some time so it is done after the quick stuff
2666 core::map<v3s16, MapBlock*> modified_blocks;
2667 m_ignore_map_edit_events = true;
2668 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2669 m_ignore_map_edit_events = false;
2672 Set blocks not sent to far players
2674 for(core::list<u16>::Iterator
2675 i = far_players.begin();
2676 i != far_players.end(); i++)
2679 RemoteClient *client = getClient(peer_id);
2682 client->SetBlocksNotSent(modified_blocks);
2686 Calculate special events
2689 /*if(n.d == CONTENT_MESE)
2692 for(s16 z=-1; z<=1; z++)
2693 for(s16 y=-1; y<=1; y++)
2694 for(s16 x=-1; x<=1; x++)
2701 Place other item (not a block)
2705 v3s16 blockpos = getNodeBlockPos(p_over);
2708 Check that the block is loaded so that the item
2709 can properly be added to the static list too
2711 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2714 derr_server<<"Error while placing object: "
2715 "block not found"<<std::endl;
2719 dout_server<<"Placing a miscellaneous item on map"
2722 // Calculate a position for it
2723 v3f pos = intToFloat(p_over, BS);
2725 pos.Y -= BS*0.25; // let it drop a bit
2727 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2728 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2733 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2737 derr_server<<"WARNING: item resulted in NULL object, "
2738 <<"not placing onto map"
2743 // Add the object to the environment
2744 m_env.addActiveObject(obj);
2746 dout_server<<"Placed object"<<std::endl;
2748 if(g_settings.getBool("creative_mode") == false)
2750 // Delete the right amount of items from the slot
2751 u16 dropcount = item->getDropCount();
2753 // Delete item if all gone
2754 if(item->getCount() <= dropcount)
2756 if(item->getCount() < dropcount)
2757 dstream<<"WARNING: Server: dropped more items"
2758 <<" than the slot contains"<<std::endl;
2760 InventoryList *ilist = player->inventory.getList("main");
2762 // Remove from inventory and send inventory
2763 ilist->deleteItem(item_i);
2765 // Else decrement it
2767 item->remove(dropcount);
2770 UpdateCrafting(peer_id);
2771 SendInventory(peer_id);
2779 Catch invalid actions
2783 derr_server<<"WARNING: Server: Invalid action "
2784 <<action<<std::endl;
2788 else if(command == TOSERVER_RELEASE)
2797 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2800 else if(command == TOSERVER_SIGNTEXT)
2802 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2811 std::string datastring((char*)&data[2], datasize-2);
2812 std::istringstream is(datastring, std::ios_base::binary);
2815 is.read((char*)buf, 6);
2816 v3s16 blockpos = readV3S16(buf);
2817 is.read((char*)buf, 2);
2818 s16 id = readS16(buf);
2819 is.read((char*)buf, 2);
2820 u16 textlen = readU16(buf);
2822 for(u16 i=0; i<textlen; i++)
2824 is.read((char*)buf, 1);
2825 text += (char)buf[0];
2828 MapBlock *block = NULL;
2831 block = m_env.getMap().getBlockNoCreate(blockpos);
2833 catch(InvalidPositionException &e)
2835 derr_server<<"Error while setting sign text: "
2836 "block not found"<<std::endl;
2840 MapBlockObject *obj = block->getObject(id);
2843 derr_server<<"Error while setting sign text: "
2844 "object not found"<<std::endl;
2848 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2850 derr_server<<"Error while setting sign text: "
2851 "object is not a sign"<<std::endl;
2855 ((SignObject*)obj)->setText(text);
2857 obj->getBlock()->setChangedFlag();
2859 else if(command == TOSERVER_SIGNNODETEXT)
2861 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2869 std::string datastring((char*)&data[2], datasize-2);
2870 std::istringstream is(datastring, std::ios_base::binary);
2873 is.read((char*)buf, 6);
2874 v3s16 p = readV3S16(buf);
2875 is.read((char*)buf, 2);
2876 u16 textlen = readU16(buf);
2878 for(u16 i=0; i<textlen; i++)
2880 is.read((char*)buf, 1);
2881 text += (char)buf[0];
2884 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2887 if(meta->typeId() != CONTENT_SIGN_WALL)
2889 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2890 signmeta->setText(text);
2892 v3s16 blockpos = getNodeBlockPos(p);
2893 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2896 block->setChangedFlag();
2899 for(core::map<u16, RemoteClient*>::Iterator
2900 i = m_clients.getIterator();
2901 i.atEnd()==false; i++)
2903 RemoteClient *client = i.getNode()->getValue();
2904 client->SetBlockNotSent(blockpos);
2907 else if(command == TOSERVER_INVENTORY_ACTION)
2909 /*// Ignore inventory changes if in creative mode
2910 if(g_settings.getBool("creative_mode") == true)
2912 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2916 // Strip command and create a stream
2917 std::string datastring((char*)&data[2], datasize-2);
2918 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2919 std::istringstream is(datastring, std::ios_base::binary);
2921 InventoryAction *a = InventoryAction::deSerialize(is);
2926 c.current_player = player;
2929 Handle craftresult specially if not in creative mode
2931 bool disable_action = false;
2932 if(a->getType() == IACTION_MOVE
2933 && g_settings.getBool("creative_mode") == false)
2935 IMoveAction *ma = (IMoveAction*)a;
2936 if(ma->to_inv == "current_player" &&
2937 ma->from_inv == "current_player")
2939 InventoryList *rlist = player->inventory.getList("craftresult");
2941 InventoryList *clist = player->inventory.getList("craft");
2943 InventoryList *mlist = player->inventory.getList("main");
2946 Craftresult is no longer preview if something
2949 if(ma->to_list == "craftresult"
2950 && ma->from_list != "craftresult")
2952 // If it currently is a preview, remove
2954 if(player->craftresult_is_preview)
2956 rlist->deleteItem(0);
2958 player->craftresult_is_preview = false;
2961 Crafting takes place if this condition is true.
2963 if(player->craftresult_is_preview &&
2964 ma->from_list == "craftresult")
2966 player->craftresult_is_preview = false;
2967 clist->decrementMaterials(1);
2970 If the craftresult is placed on itself, move it to
2971 main inventory instead of doing the action
2973 if(ma->to_list == "craftresult"
2974 && ma->from_list == "craftresult")
2976 disable_action = true;
2978 InventoryItem *item1 = rlist->changeItem(0, NULL);
2979 mlist->addItem(item1);
2984 if(disable_action == false)
2986 // Feed action to player inventory
2994 UpdateCrafting(player->peer_id);
2995 SendInventory(player->peer_id);
3000 dstream<<"TOSERVER_INVENTORY_ACTION: "
3001 <<"InventoryAction::deSerialize() returned NULL"
3005 else if(command == TOSERVER_CHAT_MESSAGE)
3013 std::string datastring((char*)&data[2], datasize-2);
3014 std::istringstream is(datastring, std::ios_base::binary);
3017 is.read((char*)buf, 2);
3018 u16 len = readU16(buf);
3020 std::wstring message;
3021 for(u16 i=0; i<len; i++)
3023 is.read((char*)buf, 2);
3024 message += (wchar_t)readU16(buf);
3027 // Get player name of this client
3028 std::wstring name = narrow_to_wide(player->getName());
3030 // Line to send to players
3032 // Whether to send to the player that sent the line
3033 bool send_to_sender = false;
3034 // Whether to send to other players
3035 bool send_to_others = false;
3037 // Local player gets all privileges regardless of
3038 // what's set on their account.
3039 u64 privs = getPlayerPrivs(player);
3042 std::wstring commandprefix = L"/#";
3043 if(message.substr(0, commandprefix.size()) == commandprefix)
3045 line += L"Server: ";
3047 message = message.substr(commandprefix.size());
3049 ServerCommandContext *ctx = new ServerCommandContext(
3050 str_split(message, L' '),
3056 line += processServerCommand(ctx);
3057 send_to_sender = ctx->flags & 1;
3058 send_to_others = ctx->flags & 2;
3064 if(privs & PRIV_SHOUT)
3070 send_to_others = true;
3074 line += L"Server: You are not allowed to shout";
3075 send_to_sender = true;
3081 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3084 Send the message to clients
3086 for(core::map<u16, RemoteClient*>::Iterator
3087 i = m_clients.getIterator();
3088 i.atEnd() == false; i++)
3090 // Get client and check that it is valid
3091 RemoteClient *client = i.getNode()->getValue();
3092 assert(client->peer_id == i.getNode()->getKey());
3093 if(client->serialization_version == SER_FMT_VER_INVALID)
3097 bool sender_selected = (peer_id == client->peer_id);
3098 if(sender_selected == true && send_to_sender == false)
3100 if(sender_selected == false && send_to_others == false)
3103 SendChatMessage(client->peer_id, line);
3107 else if(command == TOSERVER_DAMAGE)
3109 if(g_settings.getBool("enable_damage"))
3111 std::string datastring((char*)&data[2], datasize-2);
3112 std::istringstream is(datastring, std::ios_base::binary);
3113 u8 damage = readU8(is);
3114 if(player->hp > damage)
3116 player->hp -= damage;
3122 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3125 v3f pos = findSpawnPos(m_env.getServerMap());
3126 player->setPosition(pos);
3128 SendMovePlayer(player);
3129 SendPlayerHP(player);
3131 //TODO: Throw items around
3135 SendPlayerHP(player);
3137 else if(command == TOSERVER_PASSWORD)
3140 [0] u16 TOSERVER_PASSWORD
3141 [2] u8[28] old password
3142 [30] u8[28] new password
3145 if(datasize != 2+PASSWORD_SIZE*2)
3147 /*char password[PASSWORD_SIZE];
3148 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3149 password[i] = data[2+i];
3150 password[PASSWORD_SIZE-1] = 0;*/
3152 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3160 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3162 char c = data[2+PASSWORD_SIZE+i];
3168 std::string playername = player->getName();
3170 if(m_authmanager.exists(playername) == false)
3172 dstream<<"Server: playername not found in authmanager"<<std::endl;
3173 // Wrong old password supplied!!
3174 SendChatMessage(peer_id, L"playername not found in authmanager");
3178 std::string checkpwd = m_authmanager.getPassword(playername);
3180 if(oldpwd != checkpwd)
3182 dstream<<"Server: invalid old password"<<std::endl;
3183 // Wrong old password supplied!!
3184 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3188 m_authmanager.setPassword(playername, newpwd);
3190 dstream<<"Server: password change successful for "<<playername
3192 SendChatMessage(peer_id, L"Password change successful");
3196 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3197 "unknown command "<<command<<std::endl;
3201 catch(SendFailedException &e)
3203 derr_server<<"Server::ProcessData(): SendFailedException: "
3209 void Server::onMapEditEvent(MapEditEvent *event)
3211 dstream<<"Server::onMapEditEvent()"<<std::endl;
3212 if(m_ignore_map_edit_events)
3214 MapEditEvent *e = event->clone();
3215 m_unsent_map_edit_queue.push_back(e);
3218 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3220 if(id == "current_player")
3222 assert(c->current_player);
3223 return &(c->current_player->inventory);
3227 std::string id0 = fn.next(":");
3229 if(id0 == "nodemeta")
3232 p.X = stoi(fn.next(","));
3233 p.Y = stoi(fn.next(","));
3234 p.Z = stoi(fn.next(","));
3235 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3237 return meta->getInventory();
3238 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3239 <<"no metadata found"<<std::endl;
3243 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3246 void Server::inventoryModified(InventoryContext *c, std::string id)
3248 if(id == "current_player")
3250 assert(c->current_player);
3252 UpdateCrafting(c->current_player->peer_id);
3253 SendInventory(c->current_player->peer_id);
3258 std::string id0 = fn.next(":");
3260 if(id0 == "nodemeta")
3263 p.X = stoi(fn.next(","));
3264 p.Y = stoi(fn.next(","));
3265 p.Z = stoi(fn.next(","));
3266 v3s16 blockpos = getNodeBlockPos(p);
3268 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3270 meta->inventoryModified();
3272 for(core::map<u16, RemoteClient*>::Iterator
3273 i = m_clients.getIterator();
3274 i.atEnd()==false; i++)
3276 RemoteClient *client = i.getNode()->getValue();
3277 client->SetBlockNotSent(blockpos);
3283 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3286 core::list<PlayerInfo> Server::getPlayerInfo()
3288 DSTACK(__FUNCTION_NAME);
3289 JMutexAutoLock envlock(m_env_mutex);
3290 JMutexAutoLock conlock(m_con_mutex);
3292 core::list<PlayerInfo> list;
3294 core::list<Player*> players = m_env.getPlayers();
3296 core::list<Player*>::Iterator i;
3297 for(i = players.begin();
3298 i != players.end(); i++)
3302 Player *player = *i;
3305 con::Peer *peer = m_con.GetPeer(player->peer_id);
3306 // Copy info from peer to info struct
3308 info.address = peer->address;
3309 info.avg_rtt = peer->avg_rtt;
3311 catch(con::PeerNotFoundException &e)
3313 // Set dummy peer info
3315 info.address = Address(0,0,0,0,0);
3319 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3320 info.position = player->getPosition();
3322 list.push_back(info);
3329 void Server::peerAdded(con::Peer *peer)
3331 DSTACK(__FUNCTION_NAME);
3332 dout_server<<"Server::peerAdded(): peer->id="
3333 <<peer->id<<std::endl;
3336 c.type = PEER_ADDED;
3337 c.peer_id = peer->id;
3339 m_peer_change_queue.push_back(c);
3342 void Server::deletingPeer(con::Peer *peer, bool timeout)
3344 DSTACK(__FUNCTION_NAME);
3345 dout_server<<"Server::deletingPeer(): peer->id="
3346 <<peer->id<<", timeout="<<timeout<<std::endl;
3349 c.type = PEER_REMOVED;
3350 c.peer_id = peer->id;
3351 c.timeout = timeout;
3352 m_peer_change_queue.push_back(c);
3359 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3361 DSTACK(__FUNCTION_NAME);
3362 std::ostringstream os(std::ios_base::binary);
3364 writeU16(os, TOCLIENT_HP);
3368 std::string s = os.str();
3369 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3371 con.Send(peer_id, 0, data, true);
3374 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3375 const std::wstring &reason)
3377 DSTACK(__FUNCTION_NAME);
3378 std::ostringstream os(std::ios_base::binary);
3380 writeU16(os, TOCLIENT_ACCESS_DENIED);
3381 os<<serializeWideString(reason);
3384 std::string s = os.str();
3385 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3387 con.Send(peer_id, 0, data, true);
3391 Non-static send methods
3394 void Server::SendObjectData(float dtime)
3396 DSTACK(__FUNCTION_NAME);
3398 core::map<v3s16, bool> stepped_blocks;
3400 for(core::map<u16, RemoteClient*>::Iterator
3401 i = m_clients.getIterator();
3402 i.atEnd() == false; i++)
3404 u16 peer_id = i.getNode()->getKey();
3405 RemoteClient *client = i.getNode()->getValue();
3406 assert(client->peer_id == peer_id);
3408 if(client->serialization_version == SER_FMT_VER_INVALID)
3411 client->SendObjectData(this, dtime, stepped_blocks);
3415 void Server::SendPlayerInfos()
3417 DSTACK(__FUNCTION_NAME);
3419 //JMutexAutoLock envlock(m_env_mutex);
3421 // Get connected players
3422 core::list<Player*> players = m_env.getPlayers(true);
3424 u32 player_count = players.getSize();
3425 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3427 SharedBuffer<u8> data(datasize);
3428 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3431 core::list<Player*>::Iterator i;
3432 for(i = players.begin();
3433 i != players.end(); i++)
3435 Player *player = *i;
3437 /*dstream<<"Server sending player info for player with "
3438 "peer_id="<<player->peer_id<<std::endl;*/
3440 writeU16(&data[start], player->peer_id);
3441 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3442 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3443 start += 2+PLAYERNAME_SIZE;
3446 //JMutexAutoLock conlock(m_con_mutex);
3449 m_con.SendToAll(0, data, true);
3452 void Server::SendInventory(u16 peer_id)
3454 DSTACK(__FUNCTION_NAME);
3456 Player* player = m_env.getPlayer(peer_id);
3463 std::ostringstream os;
3464 //os.imbue(std::locale("C"));
3466 player->inventory.serialize(os);
3468 std::string s = os.str();
3470 SharedBuffer<u8> data(s.size()+2);
3471 writeU16(&data[0], TOCLIENT_INVENTORY);
3472 memcpy(&data[2], s.c_str(), s.size());
3475 m_con.Send(peer_id, 0, data, true);
3478 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3480 DSTACK(__FUNCTION_NAME);
3482 std::ostringstream os(std::ios_base::binary);
3486 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3487 os.write((char*)buf, 2);
3490 writeU16(buf, message.size());
3491 os.write((char*)buf, 2);
3494 for(u32 i=0; i<message.size(); i++)
3498 os.write((char*)buf, 2);
3502 std::string s = os.str();
3503 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3505 m_con.Send(peer_id, 0, data, true);
3508 void Server::BroadcastChatMessage(const std::wstring &message)
3510 for(core::map<u16, RemoteClient*>::Iterator
3511 i = m_clients.getIterator();
3512 i.atEnd() == false; i++)
3514 // Get client and check that it is valid
3515 RemoteClient *client = i.getNode()->getValue();
3516 assert(client->peer_id == i.getNode()->getKey());
3517 if(client->serialization_version == SER_FMT_VER_INVALID)
3520 SendChatMessage(client->peer_id, message);
3524 void Server::SendPlayerHP(Player *player)
3526 SendHP(m_con, player->peer_id, player->hp);
3529 void Server::SendMovePlayer(Player *player)
3531 DSTACK(__FUNCTION_NAME);
3532 std::ostringstream os(std::ios_base::binary);
3534 writeU16(os, TOCLIENT_MOVE_PLAYER);
3535 writeV3F1000(os, player->getPosition());
3536 writeF1000(os, player->getPitch());
3537 writeF1000(os, player->getYaw());
3540 v3f pos = player->getPosition();
3541 f32 pitch = player->getPitch();
3542 f32 yaw = player->getYaw();
3543 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3544 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3551 std::string s = os.str();
3552 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3554 m_con.Send(player->peer_id, 0, data, true);
3557 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3558 core::list<u16> *far_players, float far_d_nodes)
3560 float maxd = far_d_nodes*BS;
3561 v3f p_f = intToFloat(p, BS);
3565 SharedBuffer<u8> reply(replysize);
3566 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3567 writeS16(&reply[2], p.X);
3568 writeS16(&reply[4], p.Y);
3569 writeS16(&reply[6], p.Z);
3571 for(core::map<u16, RemoteClient*>::Iterator
3572 i = m_clients.getIterator();
3573 i.atEnd() == false; i++)
3575 // Get client and check that it is valid
3576 RemoteClient *client = i.getNode()->getValue();
3577 assert(client->peer_id == i.getNode()->getKey());
3578 if(client->serialization_version == SER_FMT_VER_INVALID)
3581 // Don't send if it's the same one
3582 if(client->peer_id == ignore_id)
3588 Player *player = m_env.getPlayer(client->peer_id);
3591 // If player is far away, only set modified blocks not sent
3592 v3f player_pos = player->getPosition();
3593 if(player_pos.getDistanceFrom(p_f) > maxd)
3595 far_players->push_back(client->peer_id);
3602 m_con.Send(client->peer_id, 0, reply, true);
3606 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3607 core::list<u16> *far_players, float far_d_nodes)
3609 float maxd = far_d_nodes*BS;
3610 v3f p_f = intToFloat(p, BS);
3612 for(core::map<u16, RemoteClient*>::Iterator
3613 i = m_clients.getIterator();
3614 i.atEnd() == false; i++)
3616 // Get client and check that it is valid
3617 RemoteClient *client = i.getNode()->getValue();
3618 assert(client->peer_id == i.getNode()->getKey());
3619 if(client->serialization_version == SER_FMT_VER_INVALID)
3622 // Don't send if it's the same one
3623 if(client->peer_id == ignore_id)
3629 Player *player = m_env.getPlayer(client->peer_id);
3632 // If player is far away, only set modified blocks not sent
3633 v3f player_pos = player->getPosition();
3634 if(player_pos.getDistanceFrom(p_f) > maxd)
3636 far_players->push_back(client->peer_id);
3643 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3644 SharedBuffer<u8> reply(replysize);
3645 writeU16(&reply[0], TOCLIENT_ADDNODE);
3646 writeS16(&reply[2], p.X);
3647 writeS16(&reply[4], p.Y);
3648 writeS16(&reply[6], p.Z);
3649 n.serialize(&reply[8], client->serialization_version);
3652 m_con.Send(client->peer_id, 0, reply, true);
3656 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3658 DSTACK(__FUNCTION_NAME);
3660 v3s16 p = block->getPos();
3664 bool completely_air = true;
3665 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3666 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3667 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3669 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3671 completely_air = false;
3672 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3677 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3679 dstream<<"[completely air] ";
3684 Create a packet with the block in the right format
3687 std::ostringstream os(std::ios_base::binary);
3688 block->serialize(os, ver);
3689 std::string s = os.str();
3690 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3692 u32 replysize = 8 + blockdata.getSize();
3693 SharedBuffer<u8> reply(replysize);
3694 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3695 writeS16(&reply[2], p.X);
3696 writeS16(&reply[4], p.Y);
3697 writeS16(&reply[6], p.Z);
3698 memcpy(&reply[8], *blockdata, blockdata.getSize());
3700 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3701 <<": \tpacket size: "<<replysize<<std::endl;*/
3706 m_con.Send(peer_id, 1, reply, true);
3709 void Server::SendBlocks(float dtime)
3711 DSTACK(__FUNCTION_NAME);
3713 JMutexAutoLock envlock(m_env_mutex);
3714 JMutexAutoLock conlock(m_con_mutex);
3716 //TimeTaker timer("Server::SendBlocks");
3718 core::array<PrioritySortedBlockTransfer> queue;
3720 s32 total_sending = 0;
3723 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3725 for(core::map<u16, RemoteClient*>::Iterator
3726 i = m_clients.getIterator();
3727 i.atEnd() == false; i++)
3729 RemoteClient *client = i.getNode()->getValue();
3730 assert(client->peer_id == i.getNode()->getKey());
3732 total_sending += client->SendingCount();
3734 if(client->serialization_version == SER_FMT_VER_INVALID)
3737 client->GetNextBlocks(this, dtime, queue);
3742 // Lowest priority number comes first.
3743 // Lowest is most important.
3746 for(u32 i=0; i<queue.size(); i++)
3748 //TODO: Calculate limit dynamically
3749 if(total_sending >= g_settings.getS32
3750 ("max_simultaneous_block_sends_server_total"))
3753 PrioritySortedBlockTransfer q = queue[i];
3755 MapBlock *block = NULL;
3758 block = m_env.getMap().getBlockNoCreate(q.pos);
3760 catch(InvalidPositionException &e)
3765 RemoteClient *client = getClient(q.peer_id);
3767 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3769 client->SentBlock(q.pos);
3779 void Server::UpdateCrafting(u16 peer_id)
3781 DSTACK(__FUNCTION_NAME);
3783 Player* player = m_env.getPlayer(peer_id);
3787 Calculate crafting stuff
3789 if(g_settings.getBool("creative_mode") == false)
3791 InventoryList *clist = player->inventory.getList("craft");
3792 InventoryList *rlist = player->inventory.getList("craftresult");
3794 if(rlist->getUsedSlots() == 0)
3795 player->craftresult_is_preview = true;
3797 if(rlist && player->craftresult_is_preview)
3799 rlist->clearItems();
3801 if(clist && rlist && player->craftresult_is_preview)
3803 InventoryItem *items[9];
3804 for(u16 i=0; i<9; i++)
3806 items[i] = clist->getItem(i);
3815 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3816 if(checkItemCombination(items, specs))
3818 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3827 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3828 if(checkItemCombination(items, specs))
3830 rlist->addItem(new CraftItem("Stick", 4));
3839 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3840 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3841 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3842 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3843 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3844 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3845 if(checkItemCombination(items, specs))
3847 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3856 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3857 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3858 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3859 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3860 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3861 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3862 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3863 if(checkItemCombination(items, specs))
3865 //rlist->addItem(new MapBlockObjectItem("Sign"));
3866 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3875 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3876 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3877 if(checkItemCombination(items, specs))
3879 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3888 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3889 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3890 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3891 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3892 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3893 if(checkItemCombination(items, specs))
3895 rlist->addItem(new ToolItem("WPick", 0));
3904 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3905 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3906 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3907 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3908 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3909 if(checkItemCombination(items, specs))
3911 rlist->addItem(new ToolItem("STPick", 0));
3920 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3921 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3922 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3923 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3924 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3925 if(checkItemCombination(items, specs))
3927 rlist->addItem(new ToolItem("SteelPick", 0));
3936 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3937 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3938 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3939 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3940 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3941 if(checkItemCombination(items, specs))
3943 rlist->addItem(new ToolItem("MesePick", 0));
3952 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3953 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3954 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3955 if(checkItemCombination(items, specs))
3957 rlist->addItem(new ToolItem("WShovel", 0));
3966 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3967 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3968 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3969 if(checkItemCombination(items, specs))
3971 rlist->addItem(new ToolItem("STShovel", 0));
3980 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3981 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3982 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3983 if(checkItemCombination(items, specs))
3985 rlist->addItem(new ToolItem("SteelShovel", 0));
3994 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3995 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3996 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3997 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3998 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3999 if(checkItemCombination(items, specs))
4001 rlist->addItem(new ToolItem("WAxe", 0));
4010 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4011 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4012 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4013 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4014 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4015 if(checkItemCombination(items, specs))
4017 rlist->addItem(new ToolItem("STAxe", 0));
4026 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4027 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4028 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4029 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4030 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4031 if(checkItemCombination(items, specs))
4033 rlist->addItem(new ToolItem("SteelAxe", 0));
4042 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4043 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4044 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4045 if(checkItemCombination(items, specs))
4047 rlist->addItem(new ToolItem("WSword", 0));
4056 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4057 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4058 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4059 if(checkItemCombination(items, specs))
4061 rlist->addItem(new ToolItem("STSword", 0));
4070 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4071 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4072 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4073 if(checkItemCombination(items, specs))
4075 rlist->addItem(new ToolItem("SteelSword", 0));
4084 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4085 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4086 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4087 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4088 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4089 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4090 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4091 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4092 if(checkItemCombination(items, specs))
4094 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4103 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4104 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4105 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4106 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4107 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4108 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4109 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4110 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4111 if(checkItemCombination(items, specs))
4113 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4122 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4123 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4124 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4125 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4126 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4127 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4128 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4129 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4130 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4131 if(checkItemCombination(items, specs))
4133 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4139 } // if creative_mode == false
4142 RemoteClient* Server::getClient(u16 peer_id)
4144 DSTACK(__FUNCTION_NAME);
4145 //JMutexAutoLock lock(m_con_mutex);
4146 core::map<u16, RemoteClient*>::Node *n;
4147 n = m_clients.find(peer_id);
4148 // A client should exist for all peers
4150 return n->getValue();
4153 std::wstring Server::getStatusString()
4155 std::wostringstream os(std::ios_base::binary);
4158 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4160 os<<L", uptime="<<m_uptime.get();
4161 // Information about clients
4163 for(core::map<u16, RemoteClient*>::Iterator
4164 i = m_clients.getIterator();
4165 i.atEnd() == false; i++)
4167 // Get client and check that it is valid
4168 RemoteClient *client = i.getNode()->getValue();
4169 assert(client->peer_id == i.getNode()->getKey());
4170 if(client->serialization_version == SER_FMT_VER_INVALID)
4173 Player *player = m_env.getPlayer(client->peer_id);
4174 // Get name of player
4175 std::wstring name = L"unknown";
4177 name = narrow_to_wide(player->getName());
4178 // Add name to information string
4182 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4183 os<<" WARNING: Map saving is disabled."<<std::endl;
4188 void setCreativeInventory(Player *player)
4190 player->resetInventory();
4192 // Give some good tools
4194 InventoryItem *item = new ToolItem("MesePick", 0);
4195 void* r = player->inventory.addItem("main", item);
4199 InventoryItem *item = new ToolItem("SteelPick", 0);
4200 void* r = player->inventory.addItem("main", item);
4204 InventoryItem *item = new ToolItem("SteelAxe", 0);
4205 void* r = player->inventory.addItem("main", item);
4209 InventoryItem *item = new ToolItem("SteelShovel", 0);
4210 void* r = player->inventory.addItem("main", item);
4218 // CONTENT_IGNORE-terminated list
4219 u8 material_items[] = {
4230 CONTENT_WATERSOURCE,
4238 u8 *mip = material_items;
4239 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4241 if(*mip == CONTENT_IGNORE)
4244 InventoryItem *item = new MaterialItem(*mip, 1);
4245 player->inventory.addItem("main", item);
4251 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4254 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4255 player->inventory.addItem("main", item);
4258 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4260 // Skip some materials
4261 if(i == CONTENT_WATER || i == CONTENT_TORCH
4262 || i == CONTENT_COALSTONE)
4265 InventoryItem *item = new MaterialItem(i, 1);
4266 player->inventory.addItem("main", item);
4272 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4273 void* r = player->inventory.addItem("main", item);
4278 v3f findSpawnPos(ServerMap &map)
4280 //return v3f(50,50,50)*BS;
4283 s16 groundheight = 0;
4285 // Try to find a good place a few times
4286 for(s32 i=0; i<1000; i++)
4289 // We're going to try to throw the player to this position
4290 nodepos = v2s16(-range + (myrand()%(range*2)),
4291 -range + (myrand()%(range*2)));
4292 v2s16 sectorpos = getNodeSectorPos(nodepos);
4293 // Get sector (NOTE: Don't get because it's slow)
4294 //m_env.getMap().emergeSector(sectorpos);
4295 // Get ground height at point (fallbacks to heightmap function)
4296 groundheight = map.findGroundLevel(nodepos);
4297 // Don't go underwater
4298 if(groundheight < WATER_LEVEL)
4300 //dstream<<"-> Underwater"<<std::endl;
4303 // Don't go to high places
4304 if(groundheight > WATER_LEVEL + 4)
4306 //dstream<<"-> Underwater"<<std::endl;
4310 // Found a good place
4311 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4315 // If no suitable place was not found, go above water at least.
4316 if(groundheight < WATER_LEVEL)
4317 groundheight = WATER_LEVEL;
4319 return intToFloat(v3s16(
4326 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4329 Try to get an existing player
4331 Player *player = m_env.getPlayer(name);
4334 // If player is already connected, cancel
4335 if(player->peer_id != 0)
4337 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4342 player->peer_id = peer_id;
4344 // Reset inventory to creative if in creative mode
4345 if(g_settings.getBool("creative_mode"))
4347 setCreativeInventory(player);
4354 If player with the wanted peer_id already exists, cancel.
4356 if(m_env.getPlayer(peer_id) != NULL)
4358 dstream<<"emergePlayer(): Player with wrong name but same"
4359 " peer_id already exists"<<std::endl;
4367 player = new ServerRemotePlayer();
4368 //player->peer_id = c.peer_id;
4369 //player->peer_id = PEER_ID_INEXISTENT;
4370 player->peer_id = peer_id;
4371 player->updateName(name);
4372 m_authmanager.add(name);
4373 m_authmanager.setPassword(name, password);
4374 m_authmanager.setPrivs(name,
4375 stringToPrivs(g_settings.get("default_privs")));
4381 dstream<<"Server: Finding spawn place for player \""
4382 <<player->getName()<<"\""<<std::endl;
4384 v3f pos = findSpawnPos(m_env.getServerMap());
4386 player->setPosition(pos);
4389 Add player to environment
4392 m_env.addPlayer(player);
4395 Add stuff to inventory
4398 if(g_settings.getBool("creative_mode"))
4400 setCreativeInventory(player);
4402 else if(g_settings.getBool("give_initial_stuff"))
4405 InventoryItem *item = new ToolItem("SteelPick", 0);
4406 void* r = player->inventory.addItem("main", item);
4410 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4411 void* r = player->inventory.addItem("main", item);
4415 InventoryItem *item = new ToolItem("SteelAxe", 0);
4416 void* r = player->inventory.addItem("main", item);
4420 InventoryItem *item = new ToolItem("SteelShovel", 0);
4421 void* r = player->inventory.addItem("main", item);
4425 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4426 void* r = player->inventory.addItem("main", item);
4430 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4431 void* r = player->inventory.addItem("main", item);
4435 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4436 void* r = player->inventory.addItem("main", item);
4440 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4441 void* r = player->inventory.addItem("main", item);
4445 InventoryItem *item = new CraftItem("Stick", 4);
4446 void* r = player->inventory.addItem("main", item);
4450 InventoryItem *item = new ToolItem("WPick", 32000);
4451 void* r = player->inventory.addItem("main", item);
4455 InventoryItem *item = new ToolItem("STPick", 32000);
4456 void* r = player->inventory.addItem("main", item);
4460 for(u16 i=0; i<4; i++)
4462 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4463 bool r = player->inventory.addItem("main", item);
4466 /*// Give some other stuff
4468 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4469 bool r = player->inventory.addItem("main", item);
4476 } // create new player
4479 void Server::handlePeerChange(PeerChange &c)
4481 JMutexAutoLock envlock(m_env_mutex);
4482 JMutexAutoLock conlock(m_con_mutex);
4484 if(c.type == PEER_ADDED)
4491 core::map<u16, RemoteClient*>::Node *n;
4492 n = m_clients.find(c.peer_id);
4493 // The client shouldn't already exist
4497 RemoteClient *client = new RemoteClient();
4498 client->peer_id = c.peer_id;
4499 m_clients.insert(client->peer_id, client);
4502 else if(c.type == PEER_REMOVED)
4509 core::map<u16, RemoteClient*>::Node *n;
4510 n = m_clients.find(c.peer_id);
4511 // The client should exist
4515 Mark objects to be not known by the client
4517 RemoteClient *client = n->getValue();
4519 for(core::map<u16, bool>::Iterator
4520 i = client->m_known_objects.getIterator();
4521 i.atEnd()==false; i++)
4524 u16 id = i.getNode()->getKey();
4525 ServerActiveObject* obj = m_env.getActiveObject(id);
4527 if(obj && obj->m_known_by_count > 0)
4528 obj->m_known_by_count--;
4531 // Collect information about leaving in chat
4532 std::wstring message;
4534 std::wstring name = L"unknown";
4535 Player *player = m_env.getPlayer(c.peer_id);
4537 name = narrow_to_wide(player->getName());
4541 message += L" left game";
4543 message += L" (timed out)";
4548 m_env.removePlayer(c.peer_id);
4551 // Set player client disconnected
4553 Player *player = m_env.getPlayer(c.peer_id);
4555 player->peer_id = 0;
4559 delete m_clients[c.peer_id];
4560 m_clients.remove(c.peer_id);
4562 // Send player info to all remaining clients
4565 // Send leave chat message to all remaining clients
4566 BroadcastChatMessage(message);
4575 void Server::handlePeerChanges()
4577 while(m_peer_change_queue.size() > 0)
4579 PeerChange c = m_peer_change_queue.pop_front();
4581 dout_server<<"Server: Handling peer change: "
4582 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4585 handlePeerChange(c);
4589 u64 Server::getPlayerPrivs(Player *player)
4593 std::string playername = player->getName();
4594 // Local player gets all privileges regardless of
4595 // what's set on their account.
4596 if(g_settings.get("name") == playername)
4602 return getPlayerAuthPrivs(playername);
4606 void dedicated_server_loop(Server &server, bool &kill)
4608 DSTACK(__FUNCTION_NAME);
4610 dstream<<DTIME<<std::endl;
4611 dstream<<"========================"<<std::endl;
4612 dstream<<"Running dedicated server"<<std::endl;
4613 dstream<<"========================"<<std::endl;
4616 IntervalLimiter m_profiler_interval;
4620 // This is kind of a hack but can be done like this
4621 // because server.step() is very light
4623 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4628 if(server.getShutdownRequested() || kill)
4630 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4637 float profiler_print_interval =
4638 g_settings.getFloat("profiler_print_interval");
4639 if(profiler_print_interval != 0)
4641 if(m_profiler_interval.step(0.030, profiler_print_interval))
4643 dstream<<"Profiler:"<<std::endl;
4644 g_profiler.print(dstream);
4652 static int counter = 0;
4658 core::list<PlayerInfo> list = server.getPlayerInfo();
4659 core::list<PlayerInfo>::Iterator i;
4660 static u32 sum_old = 0;
4661 u32 sum = PIChecksum(list);
4664 dstream<<DTIME<<"Player info:"<<std::endl;
4665 for(i=list.begin(); i!=list.end(); i++)
4667 i->PrintLine(&dstream);