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);
1677 TODO: Move to ServerEnvironment and utilize active block stuff
1680 //TimeTaker timer("Step node metadata");
1682 JMutexAutoLock envlock(m_env_mutex);
1683 JMutexAutoLock conlock(m_con_mutex);
1685 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1687 core::map<v3s16, MapBlock*> changed_blocks;
1688 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1690 for(core::map<v3s16, MapBlock*>::Iterator
1691 i = changed_blocks.getIterator();
1692 i.atEnd() == false; i++)
1694 MapBlock *block = i.getNode()->getValue();
1696 for(core::map<u16, RemoteClient*>::Iterator
1697 i = m_clients.getIterator();
1698 i.atEnd()==false; i++)
1700 RemoteClient *client = i.getNode()->getValue();
1701 client->SetBlockNotSent(block->getPos());
1707 Trigger emergethread (it somehow gets to a non-triggered but
1708 bysy state sometimes)
1711 float &counter = m_emergethread_trigger_timer;
1717 m_emergethread.trigger();
1721 // Save map, players and auth stuff
1723 float &counter = m_savemap_timer;
1725 if(counter >= g_settings.getFloat("server_map_save_interval"))
1729 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1732 if(m_authmanager.isModified())
1733 m_authmanager.save();
1736 JMutexAutoLock lock(m_env_mutex);
1737 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1739 // Save only changed parts
1740 m_env.getMap().save(true);
1742 // Delete unused sectors
1743 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1744 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1745 if(deleted_count > 0)
1747 dout_server<<"Server: Unloaded "<<deleted_count
1748 <<" sectors from memory"<<std::endl;
1752 m_env.serializePlayers(m_mapsavedir);
1754 // Save environment metadata
1755 m_env.saveMeta(m_mapsavedir);
1761 void Server::Receive()
1763 DSTACK(__FUNCTION_NAME);
1764 u32 data_maxsize = 10000;
1765 Buffer<u8> data(data_maxsize);
1770 JMutexAutoLock conlock(m_con_mutex);
1771 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1774 // This has to be called so that the client list gets synced
1775 // with the peer list of the connection
1776 handlePeerChanges();
1778 ProcessData(*data, datasize, peer_id);
1780 catch(con::InvalidIncomingDataException &e)
1782 derr_server<<"Server::Receive(): "
1783 "InvalidIncomingDataException: what()="
1784 <<e.what()<<std::endl;
1786 catch(con::PeerNotFoundException &e)
1788 //NOTE: This is not needed anymore
1790 // The peer has been disconnected.
1791 // Find the associated player and remove it.
1793 /*JMutexAutoLock envlock(m_env_mutex);
1795 dout_server<<"ServerThread: peer_id="<<peer_id
1796 <<" has apparently closed connection. "
1797 <<"Removing player."<<std::endl;
1799 m_env.removePlayer(peer_id);*/
1803 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1805 DSTACK(__FUNCTION_NAME);
1806 // Environment is locked first.
1807 JMutexAutoLock envlock(m_env_mutex);
1808 JMutexAutoLock conlock(m_con_mutex);
1812 peer = m_con.GetPeer(peer_id);
1814 catch(con::PeerNotFoundException &e)
1816 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1817 <<peer_id<<" not found"<<std::endl;
1821 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1829 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1831 if(command == TOSERVER_INIT)
1833 // [0] u16 TOSERVER_INIT
1834 // [2] u8 SER_FMT_VER_HIGHEST
1835 // [3] u8[20] player_name
1836 // [23] u8[28] password <--- can be sent without this, from old versions
1838 if(datasize < 2+1+PLAYERNAME_SIZE)
1841 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1842 <<peer->id<<std::endl;
1844 // First byte after command is maximum supported
1845 // serialization version
1846 u8 client_max = data[2];
1847 u8 our_max = SER_FMT_VER_HIGHEST;
1848 // Use the highest version supported by both
1849 u8 deployed = core::min_(client_max, our_max);
1850 // If it's lower than the lowest supported, give up.
1851 if(deployed < SER_FMT_VER_LOWEST)
1852 deployed = SER_FMT_VER_INVALID;
1854 //peer->serialization_version = deployed;
1855 getClient(peer->id)->pending_serialization_version = deployed;
1857 if(deployed == SER_FMT_VER_INVALID)
1859 derr_server<<DTIME<<"Server: Cannot negotiate "
1860 "serialization version with peer "
1861 <<peer_id<<std::endl;
1870 char playername[PLAYERNAME_SIZE];
1871 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1873 playername[i] = data[3+i];
1875 playername[PLAYERNAME_SIZE-1] = 0;
1877 if(playername[0]=='\0')
1879 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1880 SendAccessDenied(m_con, peer_id,
1885 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1887 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1888 SendAccessDenied(m_con, peer_id,
1889 L"Name contains unallowed characters");
1894 char password[PASSWORD_SIZE];
1895 if(datasize == 2+1+PLAYERNAME_SIZE)
1897 // old version - assume blank password
1902 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1904 password[i] = data[23+i];
1906 password[PASSWORD_SIZE-1] = 0;
1909 std::string checkpwd;
1910 if(m_authmanager.exists(playername))
1912 checkpwd = m_authmanager.getPassword(playername);
1916 checkpwd = g_settings.get("default_password");
1919 if(password != checkpwd)
1921 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1922 <<": supplied invalid password for "
1923 <<playername<<std::endl;
1924 SendAccessDenied(m_con, peer_id, L"Invalid password");
1928 // Add player to auth manager
1929 if(m_authmanager.exists(playername) == false)
1931 derr_server<<DTIME<<"Server: adding player "<<playername
1932 <<" to auth manager"<<std::endl;
1933 m_authmanager.add(playername);
1934 m_authmanager.setPassword(playername, checkpwd);
1935 m_authmanager.setPrivs(playername,
1936 stringToPrivs(g_settings.get("default_privs")));
1937 m_authmanager.save();
1941 Player *player = emergePlayer(playername, password, peer_id);
1945 // DEBUG: Test serialization
1946 std::ostringstream test_os;
1947 player->serialize(test_os);
1948 dstream<<"Player serialization test: \""<<test_os.str()
1950 std::istringstream test_is(test_os.str());
1951 player->deSerialize(test_is);
1954 // If failed, cancel
1957 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1958 <<": failed to emerge player"<<std::endl;
1963 // If a client is already connected to the player, cancel
1964 if(player->peer_id != 0)
1966 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1967 <<" tried to connect to "
1968 "an already connected player (peer_id="
1969 <<player->peer_id<<")"<<std::endl;
1972 // Set client of player
1973 player->peer_id = peer_id;
1976 // Check if player doesn't exist
1978 throw con::InvalidIncomingDataException
1979 ("Server::ProcessData(): INIT: Player doesn't exist");
1981 /*// update name if it was supplied
1982 if(datasize >= 20+3)
1985 player->updateName((const char*)&data[3]);
1989 Answer with a TOCLIENT_INIT
1992 SharedBuffer<u8> reply(2+1+6+8);
1993 writeU16(&reply[0], TOCLIENT_INIT);
1994 writeU8(&reply[2], deployed);
1995 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1996 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1997 writeU64(&reply[2+1+6], 0); // no seed
2000 m_con.Send(peer_id, 0, reply, true);
2004 Send complete position information
2006 SendMovePlayer(player);
2011 if(command == TOSERVER_INIT2)
2013 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2014 <<peer->id<<std::endl;
2017 getClient(peer->id)->serialization_version
2018 = getClient(peer->id)->pending_serialization_version;
2021 Send some initialization data
2024 // Send player info to all players
2027 // Send inventory to player
2028 UpdateCrafting(peer->id);
2029 SendInventory(peer->id);
2033 Player *player = m_env.getPlayer(peer_id);
2034 SendPlayerHP(player);
2039 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2040 m_env.getTimeOfDay());
2041 m_con.Send(peer->id, 0, data, true);
2044 // Send information about server to player in chat
2045 SendChatMessage(peer_id, getStatusString());
2047 // Send information about joining in chat
2049 std::wstring name = L"unknown";
2050 Player *player = m_env.getPlayer(peer_id);
2052 name = narrow_to_wide(player->getName());
2054 std::wstring message;
2057 message += L" joined game";
2058 BroadcastChatMessage(message);
2064 if(peer_ser_ver == SER_FMT_VER_INVALID)
2066 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2067 " serialization format invalid or not initialized."
2068 " Skipping incoming command="<<command<<std::endl;
2072 Player *player = m_env.getPlayer(peer_id);
2075 derr_server<<"Server::ProcessData(): Cancelling: "
2076 "No player for peer_id="<<peer_id
2080 if(command == TOSERVER_PLAYERPOS)
2082 if(datasize < 2+12+12+4+4)
2086 v3s32 ps = readV3S32(&data[start+2]);
2087 v3s32 ss = readV3S32(&data[start+2+12]);
2088 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2089 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2090 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2091 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2092 pitch = wrapDegrees(pitch);
2093 yaw = wrapDegrees(yaw);
2094 player->setPosition(position);
2095 player->setSpeed(speed);
2096 player->setPitch(pitch);
2097 player->setYaw(yaw);
2099 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2100 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2101 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2103 else if(command == TOSERVER_GOTBLOCKS)
2116 u16 count = data[2];
2117 for(u16 i=0; i<count; i++)
2119 if((s16)datasize < 2+1+(i+1)*6)
2120 throw con::InvalidIncomingDataException
2121 ("GOTBLOCKS length is too short");
2122 v3s16 p = readV3S16(&data[2+1+i*6]);
2123 /*dstream<<"Server: GOTBLOCKS ("
2124 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2125 RemoteClient *client = getClient(peer_id);
2126 client->GotBlock(p);
2129 else if(command == TOSERVER_DELETEDBLOCKS)
2142 u16 count = data[2];
2143 for(u16 i=0; i<count; i++)
2145 if((s16)datasize < 2+1+(i+1)*6)
2146 throw con::InvalidIncomingDataException
2147 ("DELETEDBLOCKS length is too short");
2148 v3s16 p = readV3S16(&data[2+1+i*6]);
2149 /*dstream<<"Server: DELETEDBLOCKS ("
2150 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2151 RemoteClient *client = getClient(peer_id);
2152 client->SetBlockNotSent(p);
2155 else if(command == TOSERVER_CLICK_OBJECT)
2160 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2165 [2] u8 button (0=left, 1=right)
2170 u8 button = readU8(&data[2]);
2172 p.X = readS16(&data[3]);
2173 p.Y = readS16(&data[5]);
2174 p.Z = readS16(&data[7]);
2175 s16 id = readS16(&data[9]);
2176 //u16 item_i = readU16(&data[11]);
2178 MapBlock *block = NULL;
2181 block = m_env.getMap().getBlockNoCreate(p);
2183 catch(InvalidPositionException &e)
2185 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2189 MapBlockObject *obj = block->getObject(id);
2193 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2197 //TODO: Check that object is reasonably close
2202 InventoryList *ilist = player->inventory.getList("main");
2203 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2206 // Skip if inventory has no free space
2207 if(ilist->getUsedSlots() == ilist->getSize())
2209 dout_server<<"Player inventory has no free space"<<std::endl;
2214 Create the inventory item
2216 InventoryItem *item = NULL;
2217 // If it is an item-object, take the item from it
2218 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2220 item = ((ItemObject*)obj)->createInventoryItem();
2222 // Else create an item of the object
2225 item = new MapBlockObjectItem
2226 (obj->getInventoryString());
2229 // Add to inventory and send inventory
2230 ilist->addItem(item);
2231 UpdateCrafting(player->peer_id);
2232 SendInventory(player->peer_id);
2235 // Remove from block
2236 block->removeObject(id);
2239 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2244 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2250 [2] u8 button (0=left, 1=right)
2254 u8 button = readU8(&data[2]);
2255 u16 id = readS16(&data[3]);
2256 u16 item_i = readU16(&data[11]);
2258 ServerActiveObject *obj = m_env.getActiveObject(id);
2262 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2267 //TODO: Check that object is reasonably close
2269 // Left click, pick object up (usually)
2272 InventoryList *ilist = player->inventory.getList("main");
2273 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2276 // Skip if inventory has no free space
2277 if(ilist->getUsedSlots() == ilist->getSize())
2279 dout_server<<"Player inventory has no free space"<<std::endl;
2283 // Skip if object has been removed
2288 Create the inventory item
2290 InventoryItem *item = obj->createPickedUpItem();
2294 // Add to inventory and send inventory
2295 ilist->addItem(item);
2296 UpdateCrafting(player->peer_id);
2297 SendInventory(player->peer_id);
2299 // Remove object from environment
2300 obj->m_removed = true;
2305 Item cannot be picked up. Punch it instead.
2308 ToolItem *titem = NULL;
2309 std::string toolname = "";
2311 InventoryList *mlist = player->inventory.getList("main");
2314 InventoryItem *item = mlist->getItem(item_i);
2315 if(item && (std::string)item->getName() == "ToolItem")
2317 titem = (ToolItem*)item;
2318 toolname = titem->getToolName();
2322 u16 wear = obj->punch(toolname);
2326 bool weared_out = titem->addWear(wear);
2328 mlist->deleteItem(item_i);
2329 SendInventory(player->peer_id);
2335 else if(command == TOSERVER_GROUND_ACTION)
2343 [3] v3s16 nodepos_undersurface
2344 [9] v3s16 nodepos_abovesurface
2349 2: stop digging (all parameters ignored)
2350 3: digging completed
2352 u8 action = readU8(&data[2]);
2354 p_under.X = readS16(&data[3]);
2355 p_under.Y = readS16(&data[5]);
2356 p_under.Z = readS16(&data[7]);
2358 p_over.X = readS16(&data[9]);
2359 p_over.Y = readS16(&data[11]);
2360 p_over.Z = readS16(&data[13]);
2361 u16 item_i = readU16(&data[15]);
2363 //TODO: Check that target is reasonably close
2371 NOTE: This can be used in the future to check if
2372 somebody is cheating, by checking the timing.
2379 else if(action == 2)
2382 RemoteClient *client = getClient(peer->id);
2383 JMutexAutoLock digmutex(client->m_dig_mutex);
2384 client->m_dig_tool_item = -1;
2389 3: Digging completed
2391 else if(action == 3)
2393 // Mandatory parameter; actually used for nothing
2394 core::map<v3s16, MapBlock*> modified_blocks;
2396 u8 material = CONTENT_IGNORE;
2397 u8 mineral = MINERAL_NONE;
2399 bool cannot_remove_node = false;
2403 MapNode n = m_env.getMap().getNode(p_under);
2405 mineral = n.getMineral();
2406 // Get material at position
2408 // If not yet cancelled
2409 if(cannot_remove_node == false)
2411 // If it's not diggable, do nothing
2412 if(content_diggable(material) == false)
2414 derr_server<<"Server: Not finishing digging: "
2415 <<"Node not diggable"
2417 cannot_remove_node = true;
2420 // If not yet cancelled
2421 if(cannot_remove_node == false)
2423 // Get node metadata
2424 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2425 if(meta && meta->nodeRemovalDisabled() == true)
2427 derr_server<<"Server: Not finishing digging: "
2428 <<"Node metadata disables removal"
2430 cannot_remove_node = true;
2434 catch(InvalidPositionException &e)
2436 derr_server<<"Server: Not finishing digging: Node not found."
2437 <<" Adding block to emerge queue."
2439 m_emerge_queue.addBlock(peer_id,
2440 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2441 cannot_remove_node = true;
2444 // Make sure the player is allowed to do it
2445 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2447 dstream<<"Player "<<player->getName()<<" cannot remove node"
2448 <<" because privileges are "<<getPlayerPrivs(player)
2450 cannot_remove_node = true;
2454 If node can't be removed, set block to be re-sent to
2457 if(cannot_remove_node)
2459 derr_server<<"Server: Not finishing digging."<<std::endl;
2461 // Client probably has wrong data.
2462 // Set block not sent, so that client will get
2464 dstream<<"Client "<<peer_id<<" tried to dig "
2465 <<"node; but node cannot be removed."
2466 <<" setting MapBlock not sent."<<std::endl;
2467 RemoteClient *client = getClient(peer_id);
2468 v3s16 blockpos = getNodeBlockPos(p_under);
2469 client->SetBlockNotSent(blockpos);
2475 Send the removal to all other clients.
2476 - If other player is close, send REMOVENODE
2477 - Otherwise set blocks not sent
2479 core::list<u16> far_players;
2480 sendRemoveNode(p_under, peer_id, &far_players, 100);
2483 Update and send inventory
2486 if(g_settings.getBool("creative_mode") == false)
2491 InventoryList *mlist = player->inventory.getList("main");
2494 InventoryItem *item = mlist->getItem(item_i);
2495 if(item && (std::string)item->getName() == "ToolItem")
2497 ToolItem *titem = (ToolItem*)item;
2498 std::string toolname = titem->getToolName();
2500 // Get digging properties for material and tool
2501 DiggingProperties prop =
2502 getDiggingProperties(material, toolname);
2504 if(prop.diggable == false)
2506 derr_server<<"Server: WARNING: Player digged"
2507 <<" with impossible material + tool"
2508 <<" combination"<<std::endl;
2511 bool weared_out = titem->addWear(prop.wear);
2515 mlist->deleteItem(item_i);
2521 Add dug item to inventory
2524 InventoryItem *item = NULL;
2526 if(mineral != MINERAL_NONE)
2527 item = getDiggedMineralItem(mineral);
2532 std::string &dug_s = content_features(material).dug_item;
2535 std::istringstream is(dug_s, std::ios::binary);
2536 item = InventoryItem::deSerialize(is);
2542 // Add a item to inventory
2543 player->inventory.addItem("main", item);
2546 UpdateCrafting(player->peer_id);
2547 SendInventory(player->peer_id);
2553 (this takes some time so it is done after the quick stuff)
2555 m_ignore_map_edit_events = true;
2556 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2557 m_ignore_map_edit_events = false;
2560 Set blocks not sent to far players
2562 for(core::list<u16>::Iterator
2563 i = far_players.begin();
2564 i != far_players.end(); i++)
2567 RemoteClient *client = getClient(peer_id);
2570 client->SetBlocksNotSent(modified_blocks);
2577 else if(action == 1)
2580 InventoryList *ilist = player->inventory.getList("main");
2585 InventoryItem *item = ilist->getItem(item_i);
2587 // If there is no item, it is not possible to add it anywhere
2592 Handle material items
2594 if(std::string("MaterialItem") == item->getName())
2597 // Don't add a node if this is not a free space
2598 MapNode n2 = m_env.getMap().getNode(p_over);
2599 bool no_enough_privs =
2600 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2602 dstream<<"Player "<<player->getName()<<" cannot add node"
2603 <<" because privileges are "<<getPlayerPrivs(player)
2606 if(content_buildable_to(n2.d) == false
2609 // Client probably has wrong data.
2610 // Set block not sent, so that client will get
2612 dstream<<"Client "<<peer_id<<" tried to place"
2613 <<" node in invalid position; setting"
2614 <<" MapBlock not sent."<<std::endl;
2615 RemoteClient *client = getClient(peer_id);
2616 v3s16 blockpos = getNodeBlockPos(p_over);
2617 client->SetBlockNotSent(blockpos);
2621 catch(InvalidPositionException &e)
2623 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2624 <<" Adding block to emerge queue."
2626 m_emerge_queue.addBlock(peer_id,
2627 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2631 // Reset build time counter
2632 getClient(peer->id)->m_time_from_building = 0.0;
2635 MaterialItem *mitem = (MaterialItem*)item;
2637 n.d = mitem->getMaterial();
2638 if(content_features(n.d).wall_mounted)
2639 n.dir = packDir(p_under - p_over);
2644 core::list<u16> far_players;
2645 sendAddNode(p_over, n, 0, &far_players, 100);
2650 InventoryList *ilist = player->inventory.getList("main");
2651 if(g_settings.getBool("creative_mode") == false && ilist)
2653 // Remove from inventory and send inventory
2654 if(mitem->getCount() == 1)
2655 ilist->deleteItem(item_i);
2659 UpdateCrafting(peer_id);
2660 SendInventory(peer_id);
2666 This takes some time so it is done after the quick stuff
2668 core::map<v3s16, MapBlock*> modified_blocks;
2669 m_ignore_map_edit_events = true;
2670 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2671 m_ignore_map_edit_events = false;
2674 Set blocks not sent to far players
2676 for(core::list<u16>::Iterator
2677 i = far_players.begin();
2678 i != far_players.end(); i++)
2681 RemoteClient *client = getClient(peer_id);
2684 client->SetBlocksNotSent(modified_blocks);
2688 Calculate special events
2691 /*if(n.d == CONTENT_MESE)
2694 for(s16 z=-1; z<=1; z++)
2695 for(s16 y=-1; y<=1; y++)
2696 for(s16 x=-1; x<=1; x++)
2703 Place other item (not a block)
2707 v3s16 blockpos = getNodeBlockPos(p_over);
2710 Check that the block is loaded so that the item
2711 can properly be added to the static list too
2713 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2716 derr_server<<"Error while placing object: "
2717 "block not found"<<std::endl;
2721 dout_server<<"Placing a miscellaneous item on map"
2724 // Calculate a position for it
2725 v3f pos = intToFloat(p_over, BS);
2727 pos.Y -= BS*0.25; // let it drop a bit
2729 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2730 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2735 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2739 derr_server<<"WARNING: item resulted in NULL object, "
2740 <<"not placing onto map"
2745 // Add the object to the environment
2746 m_env.addActiveObject(obj);
2748 dout_server<<"Placed object"<<std::endl;
2750 if(g_settings.getBool("creative_mode") == false)
2752 // Delete the right amount of items from the slot
2753 u16 dropcount = item->getDropCount();
2755 // Delete item if all gone
2756 if(item->getCount() <= dropcount)
2758 if(item->getCount() < dropcount)
2759 dstream<<"WARNING: Server: dropped more items"
2760 <<" than the slot contains"<<std::endl;
2762 InventoryList *ilist = player->inventory.getList("main");
2764 // Remove from inventory and send inventory
2765 ilist->deleteItem(item_i);
2767 // Else decrement it
2769 item->remove(dropcount);
2772 UpdateCrafting(peer_id);
2773 SendInventory(peer_id);
2781 Catch invalid actions
2785 derr_server<<"WARNING: Server: Invalid action "
2786 <<action<<std::endl;
2790 else if(command == TOSERVER_RELEASE)
2799 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2802 else if(command == TOSERVER_SIGNTEXT)
2804 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2813 std::string datastring((char*)&data[2], datasize-2);
2814 std::istringstream is(datastring, std::ios_base::binary);
2817 is.read((char*)buf, 6);
2818 v3s16 blockpos = readV3S16(buf);
2819 is.read((char*)buf, 2);
2820 s16 id = readS16(buf);
2821 is.read((char*)buf, 2);
2822 u16 textlen = readU16(buf);
2824 for(u16 i=0; i<textlen; i++)
2826 is.read((char*)buf, 1);
2827 text += (char)buf[0];
2830 MapBlock *block = NULL;
2833 block = m_env.getMap().getBlockNoCreate(blockpos);
2835 catch(InvalidPositionException &e)
2837 derr_server<<"Error while setting sign text: "
2838 "block not found"<<std::endl;
2842 MapBlockObject *obj = block->getObject(id);
2845 derr_server<<"Error while setting sign text: "
2846 "object not found"<<std::endl;
2850 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2852 derr_server<<"Error while setting sign text: "
2853 "object is not a sign"<<std::endl;
2857 ((SignObject*)obj)->setText(text);
2859 obj->getBlock()->setChangedFlag();
2861 else if(command == TOSERVER_SIGNNODETEXT)
2863 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2871 std::string datastring((char*)&data[2], datasize-2);
2872 std::istringstream is(datastring, std::ios_base::binary);
2875 is.read((char*)buf, 6);
2876 v3s16 p = readV3S16(buf);
2877 is.read((char*)buf, 2);
2878 u16 textlen = readU16(buf);
2880 for(u16 i=0; i<textlen; i++)
2882 is.read((char*)buf, 1);
2883 text += (char)buf[0];
2886 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2889 if(meta->typeId() != CONTENT_SIGN_WALL)
2891 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2892 signmeta->setText(text);
2894 v3s16 blockpos = getNodeBlockPos(p);
2895 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2898 block->setChangedFlag();
2901 for(core::map<u16, RemoteClient*>::Iterator
2902 i = m_clients.getIterator();
2903 i.atEnd()==false; i++)
2905 RemoteClient *client = i.getNode()->getValue();
2906 client->SetBlockNotSent(blockpos);
2909 else if(command == TOSERVER_INVENTORY_ACTION)
2911 /*// Ignore inventory changes if in creative mode
2912 if(g_settings.getBool("creative_mode") == true)
2914 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2918 // Strip command and create a stream
2919 std::string datastring((char*)&data[2], datasize-2);
2920 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2921 std::istringstream is(datastring, std::ios_base::binary);
2923 InventoryAction *a = InventoryAction::deSerialize(is);
2928 c.current_player = player;
2931 Handle craftresult specially if not in creative mode
2933 bool disable_action = false;
2934 if(a->getType() == IACTION_MOVE
2935 && g_settings.getBool("creative_mode") == false)
2937 IMoveAction *ma = (IMoveAction*)a;
2938 if(ma->to_inv == "current_player" &&
2939 ma->from_inv == "current_player")
2941 InventoryList *rlist = player->inventory.getList("craftresult");
2943 InventoryList *clist = player->inventory.getList("craft");
2945 InventoryList *mlist = player->inventory.getList("main");
2948 Craftresult is no longer preview if something
2951 if(ma->to_list == "craftresult"
2952 && ma->from_list != "craftresult")
2954 // If it currently is a preview, remove
2956 if(player->craftresult_is_preview)
2958 rlist->deleteItem(0);
2960 player->craftresult_is_preview = false;
2963 Crafting takes place if this condition is true.
2965 if(player->craftresult_is_preview &&
2966 ma->from_list == "craftresult")
2968 player->craftresult_is_preview = false;
2969 clist->decrementMaterials(1);
2972 If the craftresult is placed on itself, move it to
2973 main inventory instead of doing the action
2975 if(ma->to_list == "craftresult"
2976 && ma->from_list == "craftresult")
2978 disable_action = true;
2980 InventoryItem *item1 = rlist->changeItem(0, NULL);
2981 mlist->addItem(item1);
2986 if(disable_action == false)
2988 // Feed action to player inventory
2996 UpdateCrafting(player->peer_id);
2997 SendInventory(player->peer_id);
3002 dstream<<"TOSERVER_INVENTORY_ACTION: "
3003 <<"InventoryAction::deSerialize() returned NULL"
3007 else if(command == TOSERVER_CHAT_MESSAGE)
3015 std::string datastring((char*)&data[2], datasize-2);
3016 std::istringstream is(datastring, std::ios_base::binary);
3019 is.read((char*)buf, 2);
3020 u16 len = readU16(buf);
3022 std::wstring message;
3023 for(u16 i=0; i<len; i++)
3025 is.read((char*)buf, 2);
3026 message += (wchar_t)readU16(buf);
3029 // Get player name of this client
3030 std::wstring name = narrow_to_wide(player->getName());
3032 // Line to send to players
3034 // Whether to send to the player that sent the line
3035 bool send_to_sender = false;
3036 // Whether to send to other players
3037 bool send_to_others = false;
3039 // Local player gets all privileges regardless of
3040 // what's set on their account.
3041 u64 privs = getPlayerPrivs(player);
3044 std::wstring commandprefix = L"/#";
3045 if(message.substr(0, commandprefix.size()) == commandprefix)
3047 line += L"Server: ";
3049 message = message.substr(commandprefix.size());
3051 ServerCommandContext *ctx = new ServerCommandContext(
3052 str_split(message, L' '),
3058 line += processServerCommand(ctx);
3059 send_to_sender = ctx->flags & 1;
3060 send_to_others = ctx->flags & 2;
3066 if(privs & PRIV_SHOUT)
3072 send_to_others = true;
3076 line += L"Server: You are not allowed to shout";
3077 send_to_sender = true;
3083 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3086 Send the message to clients
3088 for(core::map<u16, RemoteClient*>::Iterator
3089 i = m_clients.getIterator();
3090 i.atEnd() == false; i++)
3092 // Get client and check that it is valid
3093 RemoteClient *client = i.getNode()->getValue();
3094 assert(client->peer_id == i.getNode()->getKey());
3095 if(client->serialization_version == SER_FMT_VER_INVALID)
3099 bool sender_selected = (peer_id == client->peer_id);
3100 if(sender_selected == true && send_to_sender == false)
3102 if(sender_selected == false && send_to_others == false)
3105 SendChatMessage(client->peer_id, line);
3109 else if(command == TOSERVER_DAMAGE)
3111 if(g_settings.getBool("enable_damage"))
3113 std::string datastring((char*)&data[2], datasize-2);
3114 std::istringstream is(datastring, std::ios_base::binary);
3115 u8 damage = readU8(is);
3116 if(player->hp > damage)
3118 player->hp -= damage;
3124 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3127 v3f pos = findSpawnPos(m_env.getServerMap());
3128 player->setPosition(pos);
3130 SendMovePlayer(player);
3131 SendPlayerHP(player);
3133 //TODO: Throw items around
3137 SendPlayerHP(player);
3139 else if(command == TOSERVER_PASSWORD)
3142 [0] u16 TOSERVER_PASSWORD
3143 [2] u8[28] old password
3144 [30] u8[28] new password
3147 if(datasize != 2+PASSWORD_SIZE*2)
3149 /*char password[PASSWORD_SIZE];
3150 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3151 password[i] = data[2+i];
3152 password[PASSWORD_SIZE-1] = 0;*/
3154 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3162 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3164 char c = data[2+PASSWORD_SIZE+i];
3170 std::string playername = player->getName();
3172 if(m_authmanager.exists(playername) == false)
3174 dstream<<"Server: playername not found in authmanager"<<std::endl;
3175 // Wrong old password supplied!!
3176 SendChatMessage(peer_id, L"playername not found in authmanager");
3180 std::string checkpwd = m_authmanager.getPassword(playername);
3182 if(oldpwd != checkpwd)
3184 dstream<<"Server: invalid old password"<<std::endl;
3185 // Wrong old password supplied!!
3186 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3190 m_authmanager.setPassword(playername, newpwd);
3192 dstream<<"Server: password change successful for "<<playername
3194 SendChatMessage(peer_id, L"Password change successful");
3198 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3199 "unknown command "<<command<<std::endl;
3203 catch(SendFailedException &e)
3205 derr_server<<"Server::ProcessData(): SendFailedException: "
3211 void Server::onMapEditEvent(MapEditEvent *event)
3213 dstream<<"Server::onMapEditEvent()"<<std::endl;
3214 if(m_ignore_map_edit_events)
3216 MapEditEvent *e = event->clone();
3217 m_unsent_map_edit_queue.push_back(e);
3220 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3222 if(id == "current_player")
3224 assert(c->current_player);
3225 return &(c->current_player->inventory);
3229 std::string id0 = fn.next(":");
3231 if(id0 == "nodemeta")
3234 p.X = stoi(fn.next(","));
3235 p.Y = stoi(fn.next(","));
3236 p.Z = stoi(fn.next(","));
3237 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3239 return meta->getInventory();
3240 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3241 <<"no metadata found"<<std::endl;
3245 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3248 void Server::inventoryModified(InventoryContext *c, std::string id)
3250 if(id == "current_player")
3252 assert(c->current_player);
3254 UpdateCrafting(c->current_player->peer_id);
3255 SendInventory(c->current_player->peer_id);
3260 std::string id0 = fn.next(":");
3262 if(id0 == "nodemeta")
3265 p.X = stoi(fn.next(","));
3266 p.Y = stoi(fn.next(","));
3267 p.Z = stoi(fn.next(","));
3268 v3s16 blockpos = getNodeBlockPos(p);
3270 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3272 meta->inventoryModified();
3274 for(core::map<u16, RemoteClient*>::Iterator
3275 i = m_clients.getIterator();
3276 i.atEnd()==false; i++)
3278 RemoteClient *client = i.getNode()->getValue();
3279 client->SetBlockNotSent(blockpos);
3285 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3288 core::list<PlayerInfo> Server::getPlayerInfo()
3290 DSTACK(__FUNCTION_NAME);
3291 JMutexAutoLock envlock(m_env_mutex);
3292 JMutexAutoLock conlock(m_con_mutex);
3294 core::list<PlayerInfo> list;
3296 core::list<Player*> players = m_env.getPlayers();
3298 core::list<Player*>::Iterator i;
3299 for(i = players.begin();
3300 i != players.end(); i++)
3304 Player *player = *i;
3307 con::Peer *peer = m_con.GetPeer(player->peer_id);
3308 // Copy info from peer to info struct
3310 info.address = peer->address;
3311 info.avg_rtt = peer->avg_rtt;
3313 catch(con::PeerNotFoundException &e)
3315 // Set dummy peer info
3317 info.address = Address(0,0,0,0,0);
3321 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3322 info.position = player->getPosition();
3324 list.push_back(info);
3331 void Server::peerAdded(con::Peer *peer)
3333 DSTACK(__FUNCTION_NAME);
3334 dout_server<<"Server::peerAdded(): peer->id="
3335 <<peer->id<<std::endl;
3338 c.type = PEER_ADDED;
3339 c.peer_id = peer->id;
3341 m_peer_change_queue.push_back(c);
3344 void Server::deletingPeer(con::Peer *peer, bool timeout)
3346 DSTACK(__FUNCTION_NAME);
3347 dout_server<<"Server::deletingPeer(): peer->id="
3348 <<peer->id<<", timeout="<<timeout<<std::endl;
3351 c.type = PEER_REMOVED;
3352 c.peer_id = peer->id;
3353 c.timeout = timeout;
3354 m_peer_change_queue.push_back(c);
3361 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3363 DSTACK(__FUNCTION_NAME);
3364 std::ostringstream os(std::ios_base::binary);
3366 writeU16(os, TOCLIENT_HP);
3370 std::string s = os.str();
3371 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3373 con.Send(peer_id, 0, data, true);
3376 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3377 const std::wstring &reason)
3379 DSTACK(__FUNCTION_NAME);
3380 std::ostringstream os(std::ios_base::binary);
3382 writeU16(os, TOCLIENT_ACCESS_DENIED);
3383 os<<serializeWideString(reason);
3386 std::string s = os.str();
3387 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3389 con.Send(peer_id, 0, data, true);
3393 Non-static send methods
3396 void Server::SendObjectData(float dtime)
3398 DSTACK(__FUNCTION_NAME);
3400 core::map<v3s16, bool> stepped_blocks;
3402 for(core::map<u16, RemoteClient*>::Iterator
3403 i = m_clients.getIterator();
3404 i.atEnd() == false; i++)
3406 u16 peer_id = i.getNode()->getKey();
3407 RemoteClient *client = i.getNode()->getValue();
3408 assert(client->peer_id == peer_id);
3410 if(client->serialization_version == SER_FMT_VER_INVALID)
3413 client->SendObjectData(this, dtime, stepped_blocks);
3417 void Server::SendPlayerInfos()
3419 DSTACK(__FUNCTION_NAME);
3421 //JMutexAutoLock envlock(m_env_mutex);
3423 // Get connected players
3424 core::list<Player*> players = m_env.getPlayers(true);
3426 u32 player_count = players.getSize();
3427 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3429 SharedBuffer<u8> data(datasize);
3430 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3433 core::list<Player*>::Iterator i;
3434 for(i = players.begin();
3435 i != players.end(); i++)
3437 Player *player = *i;
3439 /*dstream<<"Server sending player info for player with "
3440 "peer_id="<<player->peer_id<<std::endl;*/
3442 writeU16(&data[start], player->peer_id);
3443 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3444 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3445 start += 2+PLAYERNAME_SIZE;
3448 //JMutexAutoLock conlock(m_con_mutex);
3451 m_con.SendToAll(0, data, true);
3454 void Server::SendInventory(u16 peer_id)
3456 DSTACK(__FUNCTION_NAME);
3458 Player* player = m_env.getPlayer(peer_id);
3465 std::ostringstream os;
3466 //os.imbue(std::locale("C"));
3468 player->inventory.serialize(os);
3470 std::string s = os.str();
3472 SharedBuffer<u8> data(s.size()+2);
3473 writeU16(&data[0], TOCLIENT_INVENTORY);
3474 memcpy(&data[2], s.c_str(), s.size());
3477 m_con.Send(peer_id, 0, data, true);
3480 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3482 DSTACK(__FUNCTION_NAME);
3484 std::ostringstream os(std::ios_base::binary);
3488 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3489 os.write((char*)buf, 2);
3492 writeU16(buf, message.size());
3493 os.write((char*)buf, 2);
3496 for(u32 i=0; i<message.size(); i++)
3500 os.write((char*)buf, 2);
3504 std::string s = os.str();
3505 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3507 m_con.Send(peer_id, 0, data, true);
3510 void Server::BroadcastChatMessage(const std::wstring &message)
3512 for(core::map<u16, RemoteClient*>::Iterator
3513 i = m_clients.getIterator();
3514 i.atEnd() == false; i++)
3516 // Get client and check that it is valid
3517 RemoteClient *client = i.getNode()->getValue();
3518 assert(client->peer_id == i.getNode()->getKey());
3519 if(client->serialization_version == SER_FMT_VER_INVALID)
3522 SendChatMessage(client->peer_id, message);
3526 void Server::SendPlayerHP(Player *player)
3528 SendHP(m_con, player->peer_id, player->hp);
3531 void Server::SendMovePlayer(Player *player)
3533 DSTACK(__FUNCTION_NAME);
3534 std::ostringstream os(std::ios_base::binary);
3536 writeU16(os, TOCLIENT_MOVE_PLAYER);
3537 writeV3F1000(os, player->getPosition());
3538 writeF1000(os, player->getPitch());
3539 writeF1000(os, player->getYaw());
3542 v3f pos = player->getPosition();
3543 f32 pitch = player->getPitch();
3544 f32 yaw = player->getYaw();
3545 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3546 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3553 std::string s = os.str();
3554 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3556 m_con.Send(player->peer_id, 0, data, true);
3559 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3560 core::list<u16> *far_players, float far_d_nodes)
3562 float maxd = far_d_nodes*BS;
3563 v3f p_f = intToFloat(p, BS);
3567 SharedBuffer<u8> reply(replysize);
3568 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3569 writeS16(&reply[2], p.X);
3570 writeS16(&reply[4], p.Y);
3571 writeS16(&reply[6], p.Z);
3573 for(core::map<u16, RemoteClient*>::Iterator
3574 i = m_clients.getIterator();
3575 i.atEnd() == false; i++)
3577 // Get client and check that it is valid
3578 RemoteClient *client = i.getNode()->getValue();
3579 assert(client->peer_id == i.getNode()->getKey());
3580 if(client->serialization_version == SER_FMT_VER_INVALID)
3583 // Don't send if it's the same one
3584 if(client->peer_id == ignore_id)
3590 Player *player = m_env.getPlayer(client->peer_id);
3593 // If player is far away, only set modified blocks not sent
3594 v3f player_pos = player->getPosition();
3595 if(player_pos.getDistanceFrom(p_f) > maxd)
3597 far_players->push_back(client->peer_id);
3604 m_con.Send(client->peer_id, 0, reply, true);
3608 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3609 core::list<u16> *far_players, float far_d_nodes)
3611 float maxd = far_d_nodes*BS;
3612 v3f p_f = intToFloat(p, BS);
3614 for(core::map<u16, RemoteClient*>::Iterator
3615 i = m_clients.getIterator();
3616 i.atEnd() == false; i++)
3618 // Get client and check that it is valid
3619 RemoteClient *client = i.getNode()->getValue();
3620 assert(client->peer_id == i.getNode()->getKey());
3621 if(client->serialization_version == SER_FMT_VER_INVALID)
3624 // Don't send if it's the same one
3625 if(client->peer_id == ignore_id)
3631 Player *player = m_env.getPlayer(client->peer_id);
3634 // If player is far away, only set modified blocks not sent
3635 v3f player_pos = player->getPosition();
3636 if(player_pos.getDistanceFrom(p_f) > maxd)
3638 far_players->push_back(client->peer_id);
3645 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3646 SharedBuffer<u8> reply(replysize);
3647 writeU16(&reply[0], TOCLIENT_ADDNODE);
3648 writeS16(&reply[2], p.X);
3649 writeS16(&reply[4], p.Y);
3650 writeS16(&reply[6], p.Z);
3651 n.serialize(&reply[8], client->serialization_version);
3654 m_con.Send(client->peer_id, 0, reply, true);
3658 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3660 DSTACK(__FUNCTION_NAME);
3662 v3s16 p = block->getPos();
3666 bool completely_air = true;
3667 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3668 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3669 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3671 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3673 completely_air = false;
3674 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3679 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3681 dstream<<"[completely air] ";
3686 Create a packet with the block in the right format
3689 std::ostringstream os(std::ios_base::binary);
3690 block->serialize(os, ver);
3691 std::string s = os.str();
3692 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3694 u32 replysize = 8 + blockdata.getSize();
3695 SharedBuffer<u8> reply(replysize);
3696 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3697 writeS16(&reply[2], p.X);
3698 writeS16(&reply[4], p.Y);
3699 writeS16(&reply[6], p.Z);
3700 memcpy(&reply[8], *blockdata, blockdata.getSize());
3702 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3703 <<": \tpacket size: "<<replysize<<std::endl;*/
3708 m_con.Send(peer_id, 1, reply, true);
3711 void Server::SendBlocks(float dtime)
3713 DSTACK(__FUNCTION_NAME);
3715 JMutexAutoLock envlock(m_env_mutex);
3716 JMutexAutoLock conlock(m_con_mutex);
3718 //TimeTaker timer("Server::SendBlocks");
3720 core::array<PrioritySortedBlockTransfer> queue;
3722 s32 total_sending = 0;
3725 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3727 for(core::map<u16, RemoteClient*>::Iterator
3728 i = m_clients.getIterator();
3729 i.atEnd() == false; i++)
3731 RemoteClient *client = i.getNode()->getValue();
3732 assert(client->peer_id == i.getNode()->getKey());
3734 total_sending += client->SendingCount();
3736 if(client->serialization_version == SER_FMT_VER_INVALID)
3739 client->GetNextBlocks(this, dtime, queue);
3744 // Lowest priority number comes first.
3745 // Lowest is most important.
3748 for(u32 i=0; i<queue.size(); i++)
3750 //TODO: Calculate limit dynamically
3751 if(total_sending >= g_settings.getS32
3752 ("max_simultaneous_block_sends_server_total"))
3755 PrioritySortedBlockTransfer q = queue[i];
3757 MapBlock *block = NULL;
3760 block = m_env.getMap().getBlockNoCreate(q.pos);
3762 catch(InvalidPositionException &e)
3767 RemoteClient *client = getClient(q.peer_id);
3769 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3771 client->SentBlock(q.pos);
3781 void Server::UpdateCrafting(u16 peer_id)
3783 DSTACK(__FUNCTION_NAME);
3785 Player* player = m_env.getPlayer(peer_id);
3789 Calculate crafting stuff
3791 if(g_settings.getBool("creative_mode") == false)
3793 InventoryList *clist = player->inventory.getList("craft");
3794 InventoryList *rlist = player->inventory.getList("craftresult");
3796 if(rlist->getUsedSlots() == 0)
3797 player->craftresult_is_preview = true;
3799 if(rlist && player->craftresult_is_preview)
3801 rlist->clearItems();
3803 if(clist && rlist && player->craftresult_is_preview)
3805 InventoryItem *items[9];
3806 for(u16 i=0; i<9; i++)
3808 items[i] = clist->getItem(i);
3817 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3818 if(checkItemCombination(items, specs))
3820 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3829 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3830 if(checkItemCombination(items, specs))
3832 rlist->addItem(new CraftItem("Stick", 4));
3841 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3842 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3843 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3844 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3845 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3846 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3847 if(checkItemCombination(items, specs))
3849 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3858 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3859 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3860 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3861 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3862 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3863 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3864 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3865 if(checkItemCombination(items, specs))
3867 //rlist->addItem(new MapBlockObjectItem("Sign"));
3868 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3877 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3878 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3879 if(checkItemCombination(items, specs))
3881 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3890 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3891 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3892 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3893 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3894 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3895 if(checkItemCombination(items, specs))
3897 rlist->addItem(new ToolItem("WPick", 0));
3906 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3907 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3908 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3909 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3910 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3911 if(checkItemCombination(items, specs))
3913 rlist->addItem(new ToolItem("STPick", 0));
3922 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3923 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3924 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3925 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3926 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3927 if(checkItemCombination(items, specs))
3929 rlist->addItem(new ToolItem("SteelPick", 0));
3938 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3939 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3940 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3941 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3942 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3943 if(checkItemCombination(items, specs))
3945 rlist->addItem(new ToolItem("MesePick", 0));
3954 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3955 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3956 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3957 if(checkItemCombination(items, specs))
3959 rlist->addItem(new ToolItem("WShovel", 0));
3968 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3969 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3970 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3971 if(checkItemCombination(items, specs))
3973 rlist->addItem(new ToolItem("STShovel", 0));
3982 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3983 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3984 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3985 if(checkItemCombination(items, specs))
3987 rlist->addItem(new ToolItem("SteelShovel", 0));
3996 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3997 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3998 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3999 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4000 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4001 if(checkItemCombination(items, specs))
4003 rlist->addItem(new ToolItem("WAxe", 0));
4012 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4013 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4014 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4015 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4016 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4017 if(checkItemCombination(items, specs))
4019 rlist->addItem(new ToolItem("STAxe", 0));
4028 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4029 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4030 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4031 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4032 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4033 if(checkItemCombination(items, specs))
4035 rlist->addItem(new ToolItem("SteelAxe", 0));
4044 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4045 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4046 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4047 if(checkItemCombination(items, specs))
4049 rlist->addItem(new ToolItem("WSword", 0));
4058 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4059 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4060 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4061 if(checkItemCombination(items, specs))
4063 rlist->addItem(new ToolItem("STSword", 0));
4072 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4073 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4074 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4075 if(checkItemCombination(items, specs))
4077 rlist->addItem(new ToolItem("SteelSword", 0));
4086 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4087 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4088 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4089 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4090 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4091 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4092 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4093 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4094 if(checkItemCombination(items, specs))
4096 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4105 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4106 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4107 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4108 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4109 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4110 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4111 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4112 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4113 if(checkItemCombination(items, specs))
4115 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4124 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4125 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4126 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4127 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4128 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4129 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4130 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4131 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4132 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4133 if(checkItemCombination(items, specs))
4135 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4141 } // if creative_mode == false
4144 RemoteClient* Server::getClient(u16 peer_id)
4146 DSTACK(__FUNCTION_NAME);
4147 //JMutexAutoLock lock(m_con_mutex);
4148 core::map<u16, RemoteClient*>::Node *n;
4149 n = m_clients.find(peer_id);
4150 // A client should exist for all peers
4152 return n->getValue();
4155 std::wstring Server::getStatusString()
4157 std::wostringstream os(std::ios_base::binary);
4160 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4162 os<<L", uptime="<<m_uptime.get();
4163 // Information about clients
4165 for(core::map<u16, RemoteClient*>::Iterator
4166 i = m_clients.getIterator();
4167 i.atEnd() == false; i++)
4169 // Get client and check that it is valid
4170 RemoteClient *client = i.getNode()->getValue();
4171 assert(client->peer_id == i.getNode()->getKey());
4172 if(client->serialization_version == SER_FMT_VER_INVALID)
4175 Player *player = m_env.getPlayer(client->peer_id);
4176 // Get name of player
4177 std::wstring name = L"unknown";
4179 name = narrow_to_wide(player->getName());
4180 // Add name to information string
4184 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4185 os<<" WARNING: Map saving is disabled."<<std::endl;
4190 void setCreativeInventory(Player *player)
4192 player->resetInventory();
4194 // Give some good tools
4196 InventoryItem *item = new ToolItem("MesePick", 0);
4197 void* r = player->inventory.addItem("main", item);
4201 InventoryItem *item = new ToolItem("SteelPick", 0);
4202 void* r = player->inventory.addItem("main", item);
4206 InventoryItem *item = new ToolItem("SteelAxe", 0);
4207 void* r = player->inventory.addItem("main", item);
4211 InventoryItem *item = new ToolItem("SteelShovel", 0);
4212 void* r = player->inventory.addItem("main", item);
4220 // CONTENT_IGNORE-terminated list
4221 u8 material_items[] = {
4232 CONTENT_WATERSOURCE,
4240 u8 *mip = material_items;
4241 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4243 if(*mip == CONTENT_IGNORE)
4246 InventoryItem *item = new MaterialItem(*mip, 1);
4247 player->inventory.addItem("main", item);
4253 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4256 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4257 player->inventory.addItem("main", item);
4260 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4262 // Skip some materials
4263 if(i == CONTENT_WATER || i == CONTENT_TORCH
4264 || i == CONTENT_COALSTONE)
4267 InventoryItem *item = new MaterialItem(i, 1);
4268 player->inventory.addItem("main", item);
4274 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4275 void* r = player->inventory.addItem("main", item);
4280 v3f findSpawnPos(ServerMap &map)
4282 //return v3f(50,50,50)*BS;
4285 s16 groundheight = 0;
4287 // Try to find a good place a few times
4288 for(s32 i=0; i<1000; i++)
4291 // We're going to try to throw the player to this position
4292 nodepos = v2s16(-range + (myrand()%(range*2)),
4293 -range + (myrand()%(range*2)));
4294 v2s16 sectorpos = getNodeSectorPos(nodepos);
4295 // Get sector (NOTE: Don't get because it's slow)
4296 //m_env.getMap().emergeSector(sectorpos);
4297 // Get ground height at point (fallbacks to heightmap function)
4298 groundheight = map.findGroundLevel(nodepos);
4299 // Don't go underwater
4300 if(groundheight < WATER_LEVEL)
4302 //dstream<<"-> Underwater"<<std::endl;
4305 // Don't go to high places
4306 if(groundheight > WATER_LEVEL + 4)
4308 //dstream<<"-> Underwater"<<std::endl;
4312 // Found a good place
4313 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4317 // If no suitable place was not found, go above water at least.
4318 if(groundheight < WATER_LEVEL)
4319 groundheight = WATER_LEVEL;
4321 return intToFloat(v3s16(
4328 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4331 Try to get an existing player
4333 Player *player = m_env.getPlayer(name);
4336 // If player is already connected, cancel
4337 if(player->peer_id != 0)
4339 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4344 player->peer_id = peer_id;
4346 // Reset inventory to creative if in creative mode
4347 if(g_settings.getBool("creative_mode"))
4349 setCreativeInventory(player);
4356 If player with the wanted peer_id already exists, cancel.
4358 if(m_env.getPlayer(peer_id) != NULL)
4360 dstream<<"emergePlayer(): Player with wrong name but same"
4361 " peer_id already exists"<<std::endl;
4369 player = new ServerRemotePlayer();
4370 //player->peer_id = c.peer_id;
4371 //player->peer_id = PEER_ID_INEXISTENT;
4372 player->peer_id = peer_id;
4373 player->updateName(name);
4374 m_authmanager.add(name);
4375 m_authmanager.setPassword(name, password);
4376 m_authmanager.setPrivs(name,
4377 stringToPrivs(g_settings.get("default_privs")));
4383 dstream<<"Server: Finding spawn place for player \""
4384 <<player->getName()<<"\""<<std::endl;
4386 v3f pos = findSpawnPos(m_env.getServerMap());
4388 player->setPosition(pos);
4391 Add player to environment
4394 m_env.addPlayer(player);
4397 Add stuff to inventory
4400 if(g_settings.getBool("creative_mode"))
4402 setCreativeInventory(player);
4404 else if(g_settings.getBool("give_initial_stuff"))
4407 InventoryItem *item = new ToolItem("SteelPick", 0);
4408 void* r = player->inventory.addItem("main", item);
4412 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4413 void* r = player->inventory.addItem("main", item);
4417 InventoryItem *item = new ToolItem("SteelAxe", 0);
4418 void* r = player->inventory.addItem("main", item);
4422 InventoryItem *item = new ToolItem("SteelShovel", 0);
4423 void* r = player->inventory.addItem("main", item);
4427 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4428 void* r = player->inventory.addItem("main", item);
4432 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4433 void* r = player->inventory.addItem("main", item);
4437 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4438 void* r = player->inventory.addItem("main", item);
4442 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4443 void* r = player->inventory.addItem("main", item);
4447 InventoryItem *item = new CraftItem("Stick", 4);
4448 void* r = player->inventory.addItem("main", item);
4452 InventoryItem *item = new ToolItem("WPick", 32000);
4453 void* r = player->inventory.addItem("main", item);
4457 InventoryItem *item = new ToolItem("STPick", 32000);
4458 void* r = player->inventory.addItem("main", item);
4462 for(u16 i=0; i<4; i++)
4464 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4465 bool r = player->inventory.addItem("main", item);
4468 /*// Give some other stuff
4470 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4471 bool r = player->inventory.addItem("main", item);
4478 } // create new player
4481 void Server::handlePeerChange(PeerChange &c)
4483 JMutexAutoLock envlock(m_env_mutex);
4484 JMutexAutoLock conlock(m_con_mutex);
4486 if(c.type == PEER_ADDED)
4493 core::map<u16, RemoteClient*>::Node *n;
4494 n = m_clients.find(c.peer_id);
4495 // The client shouldn't already exist
4499 RemoteClient *client = new RemoteClient();
4500 client->peer_id = c.peer_id;
4501 m_clients.insert(client->peer_id, client);
4504 else if(c.type == PEER_REMOVED)
4511 core::map<u16, RemoteClient*>::Node *n;
4512 n = m_clients.find(c.peer_id);
4513 // The client should exist
4517 Mark objects to be not known by the client
4519 RemoteClient *client = n->getValue();
4521 for(core::map<u16, bool>::Iterator
4522 i = client->m_known_objects.getIterator();
4523 i.atEnd()==false; i++)
4526 u16 id = i.getNode()->getKey();
4527 ServerActiveObject* obj = m_env.getActiveObject(id);
4529 if(obj && obj->m_known_by_count > 0)
4530 obj->m_known_by_count--;
4533 // Collect information about leaving in chat
4534 std::wstring message;
4536 std::wstring name = L"unknown";
4537 Player *player = m_env.getPlayer(c.peer_id);
4539 name = narrow_to_wide(player->getName());
4543 message += L" left game";
4545 message += L" (timed out)";
4550 m_env.removePlayer(c.peer_id);
4553 // Set player client disconnected
4555 Player *player = m_env.getPlayer(c.peer_id);
4557 player->peer_id = 0;
4561 delete m_clients[c.peer_id];
4562 m_clients.remove(c.peer_id);
4564 // Send player info to all remaining clients
4567 // Send leave chat message to all remaining clients
4568 BroadcastChatMessage(message);
4577 void Server::handlePeerChanges()
4579 while(m_peer_change_queue.size() > 0)
4581 PeerChange c = m_peer_change_queue.pop_front();
4583 dout_server<<"Server: Handling peer change: "
4584 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4587 handlePeerChange(c);
4591 u64 Server::getPlayerPrivs(Player *player)
4595 std::string playername = player->getName();
4596 // Local player gets all privileges regardless of
4597 // what's set on their account.
4598 if(g_settings.get("name") == playername)
4604 return getPlayerAuthPrivs(playername);
4608 void dedicated_server_loop(Server &server, bool &kill)
4610 DSTACK(__FUNCTION_NAME);
4612 dstream<<DTIME<<std::endl;
4613 dstream<<"========================"<<std::endl;
4614 dstream<<"Running dedicated server"<<std::endl;
4615 dstream<<"========================"<<std::endl;
4618 IntervalLimiter m_profiler_interval;
4622 // This is kind of a hack but can be done like this
4623 // because server.step() is very light
4625 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4630 if(server.getShutdownRequested() || kill)
4632 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4639 float profiler_print_interval =
4640 g_settings.getFloat("profiler_print_interval");
4641 if(profiler_print_interval != 0)
4643 if(m_profiler_interval.step(0.030, profiler_print_interval))
4645 dstream<<"Profiler:"<<std::endl;
4646 g_profiler.print(dstream);
4654 static int counter = 0;
4660 core::list<PlayerInfo> list = server.getPlayerInfo();
4661 core::list<PlayerInfo>::Iterator i;
4662 static u32 sum_old = 0;
4663 u32 sum = PIChecksum(list);
4666 dstream<<DTIME<<"Player info:"<<std::endl;
4667 for(i=list.begin(); i!=list.end(); i++)
4669 i->PrintLine(&dstream);