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_BLOCK_NODE_METADATA_CHANGED)
1642 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1643 setBlockNotSent(event->p);
1645 else if(event->type == MEET_OTHER)
1647 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1652 dstream<<"WARNING: Server: Unknown MapEditEvent "
1653 <<((u32)event->type)<<std::endl;
1661 Send object positions
1662 TODO: Get rid of MapBlockObjects
1665 float &counter = m_objectdata_timer;
1667 if(counter >= g_settings.getFloat("objectdata_interval"))
1669 JMutexAutoLock lock1(m_env_mutex);
1670 JMutexAutoLock lock2(m_con_mutex);
1672 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1674 SendObjectData(counter);
1682 TODO: Move to ServerEnvironment and utilize active block stuff
1685 //TimeTaker timer("Step node metadata");
1687 JMutexAutoLock envlock(m_env_mutex);
1688 JMutexAutoLock conlock(m_con_mutex);
1690 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1692 core::map<v3s16, MapBlock*> changed_blocks;
1693 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1695 // Use setBlockNotSent
1697 for(core::map<v3s16, MapBlock*>::Iterator
1698 i = changed_blocks.getIterator();
1699 i.atEnd() == false; i++)
1701 MapBlock *block = i.getNode()->getValue();
1703 for(core::map<u16, RemoteClient*>::Iterator
1704 i = m_clients.getIterator();
1705 i.atEnd()==false; i++)
1707 RemoteClient *client = i.getNode()->getValue();
1708 client->SetBlockNotSent(block->getPos());
1714 Trigger emergethread (it somehow gets to a non-triggered but
1715 bysy state sometimes)
1718 float &counter = m_emergethread_trigger_timer;
1724 m_emergethread.trigger();
1728 // Save map, players and auth stuff
1730 float &counter = m_savemap_timer;
1732 if(counter >= g_settings.getFloat("server_map_save_interval"))
1736 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1739 if(m_authmanager.isModified())
1740 m_authmanager.save();
1743 JMutexAutoLock lock(m_env_mutex);
1744 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1746 // Save only changed parts
1747 m_env.getMap().save(true);
1749 // Delete unused sectors
1750 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1751 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1752 if(deleted_count > 0)
1754 dout_server<<"Server: Unloaded "<<deleted_count
1755 <<" sectors from memory"<<std::endl;
1759 m_env.serializePlayers(m_mapsavedir);
1761 // Save environment metadata
1762 m_env.saveMeta(m_mapsavedir);
1768 void Server::Receive()
1770 DSTACK(__FUNCTION_NAME);
1771 u32 data_maxsize = 10000;
1772 Buffer<u8> data(data_maxsize);
1777 JMutexAutoLock conlock(m_con_mutex);
1778 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1781 // This has to be called so that the client list gets synced
1782 // with the peer list of the connection
1783 handlePeerChanges();
1785 ProcessData(*data, datasize, peer_id);
1787 catch(con::InvalidIncomingDataException &e)
1789 derr_server<<"Server::Receive(): "
1790 "InvalidIncomingDataException: what()="
1791 <<e.what()<<std::endl;
1793 catch(con::PeerNotFoundException &e)
1795 //NOTE: This is not needed anymore
1797 // The peer has been disconnected.
1798 // Find the associated player and remove it.
1800 /*JMutexAutoLock envlock(m_env_mutex);
1802 dout_server<<"ServerThread: peer_id="<<peer_id
1803 <<" has apparently closed connection. "
1804 <<"Removing player."<<std::endl;
1806 m_env.removePlayer(peer_id);*/
1810 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1812 DSTACK(__FUNCTION_NAME);
1813 // Environment is locked first.
1814 JMutexAutoLock envlock(m_env_mutex);
1815 JMutexAutoLock conlock(m_con_mutex);
1819 peer = m_con.GetPeer(peer_id);
1821 catch(con::PeerNotFoundException &e)
1823 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1824 <<peer_id<<" not found"<<std::endl;
1828 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1836 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1838 if(command == TOSERVER_INIT)
1840 // [0] u16 TOSERVER_INIT
1841 // [2] u8 SER_FMT_VER_HIGHEST
1842 // [3] u8[20] player_name
1843 // [23] u8[28] password <--- can be sent without this, from old versions
1845 if(datasize < 2+1+PLAYERNAME_SIZE)
1848 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1849 <<peer->id<<std::endl;
1851 // First byte after command is maximum supported
1852 // serialization version
1853 u8 client_max = data[2];
1854 u8 our_max = SER_FMT_VER_HIGHEST;
1855 // Use the highest version supported by both
1856 u8 deployed = core::min_(client_max, our_max);
1857 // If it's lower than the lowest supported, give up.
1858 if(deployed < SER_FMT_VER_LOWEST)
1859 deployed = SER_FMT_VER_INVALID;
1861 //peer->serialization_version = deployed;
1862 getClient(peer->id)->pending_serialization_version = deployed;
1864 if(deployed == SER_FMT_VER_INVALID)
1866 derr_server<<DTIME<<"Server: Cannot negotiate "
1867 "serialization version with peer "
1868 <<peer_id<<std::endl;
1877 char playername[PLAYERNAME_SIZE];
1878 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1880 playername[i] = data[3+i];
1882 playername[PLAYERNAME_SIZE-1] = 0;
1884 if(playername[0]=='\0')
1886 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1887 SendAccessDenied(m_con, peer_id,
1892 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1894 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1895 SendAccessDenied(m_con, peer_id,
1896 L"Name contains unallowed characters");
1901 char password[PASSWORD_SIZE];
1902 if(datasize == 2+1+PLAYERNAME_SIZE)
1904 // old version - assume blank password
1909 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1911 password[i] = data[23+i];
1913 password[PASSWORD_SIZE-1] = 0;
1916 std::string checkpwd;
1917 if(m_authmanager.exists(playername))
1919 checkpwd = m_authmanager.getPassword(playername);
1923 checkpwd = g_settings.get("default_password");
1926 if(password != checkpwd && checkpwd != "")
1928 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1929 <<": supplied invalid password for "
1930 <<playername<<std::endl;
1931 SendAccessDenied(m_con, peer_id, L"Invalid password");
1935 // Add player to auth manager
1936 if(m_authmanager.exists(playername) == false)
1938 derr_server<<DTIME<<"Server: adding player "<<playername
1939 <<" to auth manager"<<std::endl;
1940 m_authmanager.add(playername);
1941 m_authmanager.setPassword(playername, checkpwd);
1942 m_authmanager.setPrivs(playername,
1943 stringToPrivs(g_settings.get("default_privs")));
1944 m_authmanager.save();
1948 Player *player = emergePlayer(playername, password, peer_id);
1952 // DEBUG: Test serialization
1953 std::ostringstream test_os;
1954 player->serialize(test_os);
1955 dstream<<"Player serialization test: \""<<test_os.str()
1957 std::istringstream test_is(test_os.str());
1958 player->deSerialize(test_is);
1961 // If failed, cancel
1964 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1965 <<": failed to emerge player"<<std::endl;
1970 // If a client is already connected to the player, cancel
1971 if(player->peer_id != 0)
1973 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1974 <<" tried to connect to "
1975 "an already connected player (peer_id="
1976 <<player->peer_id<<")"<<std::endl;
1979 // Set client of player
1980 player->peer_id = peer_id;
1983 // Check if player doesn't exist
1985 throw con::InvalidIncomingDataException
1986 ("Server::ProcessData(): INIT: Player doesn't exist");
1988 /*// update name if it was supplied
1989 if(datasize >= 20+3)
1992 player->updateName((const char*)&data[3]);
1996 Answer with a TOCLIENT_INIT
1999 SharedBuffer<u8> reply(2+1+6+8);
2000 writeU16(&reply[0], TOCLIENT_INIT);
2001 writeU8(&reply[2], deployed);
2002 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2003 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2004 writeU64(&reply[2+1+6], 0); // no seed
2007 m_con.Send(peer_id, 0, reply, true);
2011 Send complete position information
2013 SendMovePlayer(player);
2018 if(command == TOSERVER_INIT2)
2020 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2021 <<peer->id<<std::endl;
2024 getClient(peer->id)->serialization_version
2025 = getClient(peer->id)->pending_serialization_version;
2028 Send some initialization data
2031 // Send player info to all players
2034 // Send inventory to player
2035 UpdateCrafting(peer->id);
2036 SendInventory(peer->id);
2040 Player *player = m_env.getPlayer(peer_id);
2041 SendPlayerHP(player);
2046 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2047 m_env.getTimeOfDay());
2048 m_con.Send(peer->id, 0, data, true);
2051 // Send information about server to player in chat
2052 SendChatMessage(peer_id, getStatusString());
2054 // Send information about joining in chat
2056 std::wstring name = L"unknown";
2057 Player *player = m_env.getPlayer(peer_id);
2059 name = narrow_to_wide(player->getName());
2061 std::wstring message;
2064 message += L" joined game";
2065 BroadcastChatMessage(message);
2071 if(peer_ser_ver == SER_FMT_VER_INVALID)
2073 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2074 " serialization format invalid or not initialized."
2075 " Skipping incoming command="<<command<<std::endl;
2079 Player *player = m_env.getPlayer(peer_id);
2082 derr_server<<"Server::ProcessData(): Cancelling: "
2083 "No player for peer_id="<<peer_id
2087 if(command == TOSERVER_PLAYERPOS)
2089 if(datasize < 2+12+12+4+4)
2093 v3s32 ps = readV3S32(&data[start+2]);
2094 v3s32 ss = readV3S32(&data[start+2+12]);
2095 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2096 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2097 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2098 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2099 pitch = wrapDegrees(pitch);
2100 yaw = wrapDegrees(yaw);
2101 player->setPosition(position);
2102 player->setSpeed(speed);
2103 player->setPitch(pitch);
2104 player->setYaw(yaw);
2106 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2107 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2108 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2110 else if(command == TOSERVER_GOTBLOCKS)
2123 u16 count = data[2];
2124 for(u16 i=0; i<count; i++)
2126 if((s16)datasize < 2+1+(i+1)*6)
2127 throw con::InvalidIncomingDataException
2128 ("GOTBLOCKS length is too short");
2129 v3s16 p = readV3S16(&data[2+1+i*6]);
2130 /*dstream<<"Server: GOTBLOCKS ("
2131 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2132 RemoteClient *client = getClient(peer_id);
2133 client->GotBlock(p);
2136 else if(command == TOSERVER_DELETEDBLOCKS)
2149 u16 count = data[2];
2150 for(u16 i=0; i<count; i++)
2152 if((s16)datasize < 2+1+(i+1)*6)
2153 throw con::InvalidIncomingDataException
2154 ("DELETEDBLOCKS length is too short");
2155 v3s16 p = readV3S16(&data[2+1+i*6]);
2156 /*dstream<<"Server: DELETEDBLOCKS ("
2157 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2158 RemoteClient *client = getClient(peer_id);
2159 client->SetBlockNotSent(p);
2162 else if(command == TOSERVER_CLICK_OBJECT)
2167 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2172 [2] u8 button (0=left, 1=right)
2177 u8 button = readU8(&data[2]);
2179 p.X = readS16(&data[3]);
2180 p.Y = readS16(&data[5]);
2181 p.Z = readS16(&data[7]);
2182 s16 id = readS16(&data[9]);
2183 //u16 item_i = readU16(&data[11]);
2185 MapBlock *block = NULL;
2188 block = m_env.getMap().getBlockNoCreate(p);
2190 catch(InvalidPositionException &e)
2192 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2196 MapBlockObject *obj = block->getObject(id);
2200 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2204 //TODO: Check that object is reasonably close
2209 InventoryList *ilist = player->inventory.getList("main");
2210 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2213 // Skip if inventory has no free space
2214 if(ilist->getUsedSlots() == ilist->getSize())
2216 dout_server<<"Player inventory has no free space"<<std::endl;
2221 Create the inventory item
2223 InventoryItem *item = NULL;
2224 // If it is an item-object, take the item from it
2225 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2227 item = ((ItemObject*)obj)->createInventoryItem();
2229 // Else create an item of the object
2232 item = new MapBlockObjectItem
2233 (obj->getInventoryString());
2236 // Add to inventory and send inventory
2237 ilist->addItem(item);
2238 UpdateCrafting(player->peer_id);
2239 SendInventory(player->peer_id);
2242 // Remove from block
2243 block->removeObject(id);
2246 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2251 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2257 [2] u8 button (0=left, 1=right)
2261 u8 button = readU8(&data[2]);
2262 u16 id = readS16(&data[3]);
2263 u16 item_i = readU16(&data[11]);
2265 ServerActiveObject *obj = m_env.getActiveObject(id);
2269 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2274 //TODO: Check that object is reasonably close
2276 // Left click, pick object up (usually)
2279 InventoryList *ilist = player->inventory.getList("main");
2280 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2283 // Skip if inventory has no free space
2284 if(ilist->getUsedSlots() == ilist->getSize())
2286 dout_server<<"Player inventory has no free space"<<std::endl;
2290 // Skip if object has been removed
2295 Create the inventory item
2297 InventoryItem *item = obj->createPickedUpItem();
2301 // Add to inventory and send inventory
2302 ilist->addItem(item);
2303 UpdateCrafting(player->peer_id);
2304 SendInventory(player->peer_id);
2306 // Remove object from environment
2307 obj->m_removed = true;
2312 Item cannot be picked up. Punch it instead.
2315 ToolItem *titem = NULL;
2316 std::string toolname = "";
2318 InventoryList *mlist = player->inventory.getList("main");
2321 InventoryItem *item = mlist->getItem(item_i);
2322 if(item && (std::string)item->getName() == "ToolItem")
2324 titem = (ToolItem*)item;
2325 toolname = titem->getToolName();
2329 u16 wear = obj->punch(toolname);
2333 bool weared_out = titem->addWear(wear);
2335 mlist->deleteItem(item_i);
2336 SendInventory(player->peer_id);
2342 else if(command == TOSERVER_GROUND_ACTION)
2350 [3] v3s16 nodepos_undersurface
2351 [9] v3s16 nodepos_abovesurface
2356 2: stop digging (all parameters ignored)
2357 3: digging completed
2359 u8 action = readU8(&data[2]);
2361 p_under.X = readS16(&data[3]);
2362 p_under.Y = readS16(&data[5]);
2363 p_under.Z = readS16(&data[7]);
2365 p_over.X = readS16(&data[9]);
2366 p_over.Y = readS16(&data[11]);
2367 p_over.Z = readS16(&data[13]);
2368 u16 item_i = readU16(&data[15]);
2370 //TODO: Check that target is reasonably close
2378 NOTE: This can be used in the future to check if
2379 somebody is cheating, by checking the timing.
2386 else if(action == 2)
2389 RemoteClient *client = getClient(peer->id);
2390 JMutexAutoLock digmutex(client->m_dig_mutex);
2391 client->m_dig_tool_item = -1;
2396 3: Digging completed
2398 else if(action == 3)
2400 // Mandatory parameter; actually used for nothing
2401 core::map<v3s16, MapBlock*> modified_blocks;
2403 u8 material = CONTENT_IGNORE;
2404 u8 mineral = MINERAL_NONE;
2406 bool cannot_remove_node = false;
2410 MapNode n = m_env.getMap().getNode(p_under);
2412 mineral = n.getMineral();
2413 // Get material at position
2415 // If not yet cancelled
2416 if(cannot_remove_node == false)
2418 // If it's not diggable, do nothing
2419 if(content_diggable(material) == false)
2421 derr_server<<"Server: Not finishing digging: "
2422 <<"Node not diggable"
2424 cannot_remove_node = true;
2427 // If not yet cancelled
2428 if(cannot_remove_node == false)
2430 // Get node metadata
2431 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2432 if(meta && meta->nodeRemovalDisabled() == true)
2434 derr_server<<"Server: Not finishing digging: "
2435 <<"Node metadata disables removal"
2437 cannot_remove_node = true;
2441 catch(InvalidPositionException &e)
2443 derr_server<<"Server: Not finishing digging: Node not found."
2444 <<" Adding block to emerge queue."
2446 m_emerge_queue.addBlock(peer_id,
2447 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2448 cannot_remove_node = true;
2451 // Make sure the player is allowed to do it
2452 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2454 dstream<<"Player "<<player->getName()<<" cannot remove node"
2455 <<" because privileges are "<<getPlayerPrivs(player)
2457 cannot_remove_node = true;
2461 If node can't be removed, set block to be re-sent to
2464 if(cannot_remove_node)
2466 derr_server<<"Server: Not finishing digging."<<std::endl;
2468 // Client probably has wrong data.
2469 // Set block not sent, so that client will get
2471 dstream<<"Client "<<peer_id<<" tried to dig "
2472 <<"node; but node cannot be removed."
2473 <<" setting MapBlock not sent."<<std::endl;
2474 RemoteClient *client = getClient(peer_id);
2475 v3s16 blockpos = getNodeBlockPos(p_under);
2476 client->SetBlockNotSent(blockpos);
2482 Send the removal to all other clients.
2483 - If other player is close, send REMOVENODE
2484 - Otherwise set blocks not sent
2486 core::list<u16> far_players;
2487 sendRemoveNode(p_under, peer_id, &far_players, 100);
2490 Update and send inventory
2493 if(g_settings.getBool("creative_mode") == false)
2498 InventoryList *mlist = player->inventory.getList("main");
2501 InventoryItem *item = mlist->getItem(item_i);
2502 if(item && (std::string)item->getName() == "ToolItem")
2504 ToolItem *titem = (ToolItem*)item;
2505 std::string toolname = titem->getToolName();
2507 // Get digging properties for material and tool
2508 DiggingProperties prop =
2509 getDiggingProperties(material, toolname);
2511 if(prop.diggable == false)
2513 derr_server<<"Server: WARNING: Player digged"
2514 <<" with impossible material + tool"
2515 <<" combination"<<std::endl;
2518 bool weared_out = titem->addWear(prop.wear);
2522 mlist->deleteItem(item_i);
2528 Add dug item to inventory
2531 InventoryItem *item = NULL;
2533 if(mineral != MINERAL_NONE)
2534 item = getDiggedMineralItem(mineral);
2539 std::string &dug_s = content_features(material).dug_item;
2542 std::istringstream is(dug_s, std::ios::binary);
2543 item = InventoryItem::deSerialize(is);
2549 // Add a item to inventory
2550 player->inventory.addItem("main", item);
2553 UpdateCrafting(player->peer_id);
2554 SendInventory(player->peer_id);
2560 (this takes some time so it is done after the quick stuff)
2562 m_ignore_map_edit_events = true;
2563 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2564 m_ignore_map_edit_events = false;
2567 Set blocks not sent to far players
2569 for(core::list<u16>::Iterator
2570 i = far_players.begin();
2571 i != far_players.end(); i++)
2574 RemoteClient *client = getClient(peer_id);
2577 client->SetBlocksNotSent(modified_blocks);
2584 else if(action == 1)
2587 InventoryList *ilist = player->inventory.getList("main");
2592 InventoryItem *item = ilist->getItem(item_i);
2594 // If there is no item, it is not possible to add it anywhere
2599 Handle material items
2601 if(std::string("MaterialItem") == item->getName())
2604 // Don't add a node if this is not a free space
2605 MapNode n2 = m_env.getMap().getNode(p_over);
2606 bool no_enough_privs =
2607 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2609 dstream<<"Player "<<player->getName()<<" cannot add node"
2610 <<" because privileges are "<<getPlayerPrivs(player)
2613 if(content_buildable_to(n2.d) == false
2616 // Client probably has wrong data.
2617 // Set block not sent, so that client will get
2619 dstream<<"Client "<<peer_id<<" tried to place"
2620 <<" node in invalid position; setting"
2621 <<" MapBlock not sent."<<std::endl;
2622 RemoteClient *client = getClient(peer_id);
2623 v3s16 blockpos = getNodeBlockPos(p_over);
2624 client->SetBlockNotSent(blockpos);
2628 catch(InvalidPositionException &e)
2630 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2631 <<" Adding block to emerge queue."
2633 m_emerge_queue.addBlock(peer_id,
2634 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2638 // Reset build time counter
2639 getClient(peer->id)->m_time_from_building = 0.0;
2642 MaterialItem *mitem = (MaterialItem*)item;
2644 n.d = mitem->getMaterial();
2645 if(content_features(n.d).wall_mounted)
2646 n.dir = packDir(p_under - p_over);
2651 core::list<u16> far_players;
2652 sendAddNode(p_over, n, 0, &far_players, 100);
2657 InventoryList *ilist = player->inventory.getList("main");
2658 if(g_settings.getBool("creative_mode") == false && ilist)
2660 // Remove from inventory and send inventory
2661 if(mitem->getCount() == 1)
2662 ilist->deleteItem(item_i);
2666 UpdateCrafting(peer_id);
2667 SendInventory(peer_id);
2673 This takes some time so it is done after the quick stuff
2675 core::map<v3s16, MapBlock*> modified_blocks;
2676 m_ignore_map_edit_events = true;
2677 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2678 m_ignore_map_edit_events = false;
2681 Set blocks not sent to far players
2683 for(core::list<u16>::Iterator
2684 i = far_players.begin();
2685 i != far_players.end(); i++)
2688 RemoteClient *client = getClient(peer_id);
2691 client->SetBlocksNotSent(modified_blocks);
2695 Calculate special events
2698 /*if(n.d == CONTENT_MESE)
2701 for(s16 z=-1; z<=1; z++)
2702 for(s16 y=-1; y<=1; y++)
2703 for(s16 x=-1; x<=1; x++)
2710 Place other item (not a block)
2714 v3s16 blockpos = getNodeBlockPos(p_over);
2717 Check that the block is loaded so that the item
2718 can properly be added to the static list too
2720 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2723 derr_server<<"Error while placing object: "
2724 "block not found"<<std::endl;
2728 dout_server<<"Placing a miscellaneous item on map"
2731 // Calculate a position for it
2732 v3f pos = intToFloat(p_over, BS);
2734 pos.Y -= BS*0.25; // let it drop a bit
2736 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2737 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2742 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2746 derr_server<<"WARNING: item resulted in NULL object, "
2747 <<"not placing onto map"
2752 // Add the object to the environment
2753 m_env.addActiveObject(obj);
2755 dout_server<<"Placed object"<<std::endl;
2757 if(g_settings.getBool("creative_mode") == false)
2759 // Delete the right amount of items from the slot
2760 u16 dropcount = item->getDropCount();
2762 // Delete item if all gone
2763 if(item->getCount() <= dropcount)
2765 if(item->getCount() < dropcount)
2766 dstream<<"WARNING: Server: dropped more items"
2767 <<" than the slot contains"<<std::endl;
2769 InventoryList *ilist = player->inventory.getList("main");
2771 // Remove from inventory and send inventory
2772 ilist->deleteItem(item_i);
2774 // Else decrement it
2776 item->remove(dropcount);
2779 UpdateCrafting(peer_id);
2780 SendInventory(peer_id);
2788 Catch invalid actions
2792 derr_server<<"WARNING: Server: Invalid action "
2793 <<action<<std::endl;
2797 else if(command == TOSERVER_RELEASE)
2806 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2809 else if(command == TOSERVER_SIGNTEXT)
2811 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2820 std::string datastring((char*)&data[2], datasize-2);
2821 std::istringstream is(datastring, std::ios_base::binary);
2824 is.read((char*)buf, 6);
2825 v3s16 blockpos = readV3S16(buf);
2826 is.read((char*)buf, 2);
2827 s16 id = readS16(buf);
2828 is.read((char*)buf, 2);
2829 u16 textlen = readU16(buf);
2831 for(u16 i=0; i<textlen; i++)
2833 is.read((char*)buf, 1);
2834 text += (char)buf[0];
2837 MapBlock *block = NULL;
2840 block = m_env.getMap().getBlockNoCreate(blockpos);
2842 catch(InvalidPositionException &e)
2844 derr_server<<"Error while setting sign text: "
2845 "block not found"<<std::endl;
2849 MapBlockObject *obj = block->getObject(id);
2852 derr_server<<"Error while setting sign text: "
2853 "object not found"<<std::endl;
2857 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2859 derr_server<<"Error while setting sign text: "
2860 "object is not a sign"<<std::endl;
2864 ((SignObject*)obj)->setText(text);
2866 obj->getBlock()->setChangedFlag();
2868 else if(command == TOSERVER_SIGNNODETEXT)
2870 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2878 std::string datastring((char*)&data[2], datasize-2);
2879 std::istringstream is(datastring, std::ios_base::binary);
2882 is.read((char*)buf, 6);
2883 v3s16 p = readV3S16(buf);
2884 is.read((char*)buf, 2);
2885 u16 textlen = readU16(buf);
2887 for(u16 i=0; i<textlen; i++)
2889 is.read((char*)buf, 1);
2890 text += (char)buf[0];
2893 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2896 if(meta->typeId() != CONTENT_SIGN_WALL)
2898 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2899 signmeta->setText(text);
2901 v3s16 blockpos = getNodeBlockPos(p);
2902 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2905 block->setChangedFlag();
2908 for(core::map<u16, RemoteClient*>::Iterator
2909 i = m_clients.getIterator();
2910 i.atEnd()==false; i++)
2912 RemoteClient *client = i.getNode()->getValue();
2913 client->SetBlockNotSent(blockpos);
2916 else if(command == TOSERVER_INVENTORY_ACTION)
2918 /*// Ignore inventory changes if in creative mode
2919 if(g_settings.getBool("creative_mode") == true)
2921 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2925 // Strip command and create a stream
2926 std::string datastring((char*)&data[2], datasize-2);
2927 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2928 std::istringstream is(datastring, std::ios_base::binary);
2930 InventoryAction *a = InventoryAction::deSerialize(is);
2935 c.current_player = player;
2938 Handle craftresult specially if not in creative mode
2940 bool disable_action = false;
2941 if(a->getType() == IACTION_MOVE
2942 && g_settings.getBool("creative_mode") == false)
2944 IMoveAction *ma = (IMoveAction*)a;
2945 if(ma->to_inv == "current_player" &&
2946 ma->from_inv == "current_player")
2948 InventoryList *rlist = player->inventory.getList("craftresult");
2950 InventoryList *clist = player->inventory.getList("craft");
2952 InventoryList *mlist = player->inventory.getList("main");
2955 Craftresult is no longer preview if something
2958 if(ma->to_list == "craftresult"
2959 && ma->from_list != "craftresult")
2961 // If it currently is a preview, remove
2963 if(player->craftresult_is_preview)
2965 rlist->deleteItem(0);
2967 player->craftresult_is_preview = false;
2970 Crafting takes place if this condition is true.
2972 if(player->craftresult_is_preview &&
2973 ma->from_list == "craftresult")
2975 player->craftresult_is_preview = false;
2976 clist->decrementMaterials(1);
2979 If the craftresult is placed on itself, move it to
2980 main inventory instead of doing the action
2982 if(ma->to_list == "craftresult"
2983 && ma->from_list == "craftresult")
2985 disable_action = true;
2987 InventoryItem *item1 = rlist->changeItem(0, NULL);
2988 mlist->addItem(item1);
2993 if(disable_action == false)
2995 // Feed action to player inventory
3003 UpdateCrafting(player->peer_id);
3004 SendInventory(player->peer_id);
3009 dstream<<"TOSERVER_INVENTORY_ACTION: "
3010 <<"InventoryAction::deSerialize() returned NULL"
3014 else if(command == TOSERVER_CHAT_MESSAGE)
3022 std::string datastring((char*)&data[2], datasize-2);
3023 std::istringstream is(datastring, std::ios_base::binary);
3026 is.read((char*)buf, 2);
3027 u16 len = readU16(buf);
3029 std::wstring message;
3030 for(u16 i=0; i<len; i++)
3032 is.read((char*)buf, 2);
3033 message += (wchar_t)readU16(buf);
3036 // Get player name of this client
3037 std::wstring name = narrow_to_wide(player->getName());
3039 // Line to send to players
3041 // Whether to send to the player that sent the line
3042 bool send_to_sender = false;
3043 // Whether to send to other players
3044 bool send_to_others = false;
3046 // Local player gets all privileges regardless of
3047 // what's set on their account.
3048 u64 privs = getPlayerPrivs(player);
3051 std::wstring commandprefix = L"/#";
3052 if(message.substr(0, commandprefix.size()) == commandprefix)
3054 line += L"Server: ";
3056 message = message.substr(commandprefix.size());
3058 ServerCommandContext *ctx = new ServerCommandContext(
3059 str_split(message, L' '),
3065 line += processServerCommand(ctx);
3066 send_to_sender = ctx->flags & 1;
3067 send_to_others = ctx->flags & 2;
3073 if(privs & PRIV_SHOUT)
3079 send_to_others = true;
3083 line += L"Server: You are not allowed to shout";
3084 send_to_sender = true;
3090 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3093 Send the message to clients
3095 for(core::map<u16, RemoteClient*>::Iterator
3096 i = m_clients.getIterator();
3097 i.atEnd() == false; i++)
3099 // Get client and check that it is valid
3100 RemoteClient *client = i.getNode()->getValue();
3101 assert(client->peer_id == i.getNode()->getKey());
3102 if(client->serialization_version == SER_FMT_VER_INVALID)
3106 bool sender_selected = (peer_id == client->peer_id);
3107 if(sender_selected == true && send_to_sender == false)
3109 if(sender_selected == false && send_to_others == false)
3112 SendChatMessage(client->peer_id, line);
3116 else if(command == TOSERVER_DAMAGE)
3118 if(g_settings.getBool("enable_damage"))
3120 std::string datastring((char*)&data[2], datasize-2);
3121 std::istringstream is(datastring, std::ios_base::binary);
3122 u8 damage = readU8(is);
3123 if(player->hp > damage)
3125 player->hp -= damage;
3131 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3134 v3f pos = findSpawnPos(m_env.getServerMap());
3135 player->setPosition(pos);
3137 SendMovePlayer(player);
3138 SendPlayerHP(player);
3140 //TODO: Throw items around
3144 SendPlayerHP(player);
3146 else if(command == TOSERVER_PASSWORD)
3149 [0] u16 TOSERVER_PASSWORD
3150 [2] u8[28] old password
3151 [30] u8[28] new password
3154 if(datasize != 2+PASSWORD_SIZE*2)
3156 /*char password[PASSWORD_SIZE];
3157 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3158 password[i] = data[2+i];
3159 password[PASSWORD_SIZE-1] = 0;*/
3161 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3169 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3171 char c = data[2+PASSWORD_SIZE+i];
3177 std::string playername = player->getName();
3179 if(m_authmanager.exists(playername) == false)
3181 dstream<<"Server: playername not found in authmanager"<<std::endl;
3182 // Wrong old password supplied!!
3183 SendChatMessage(peer_id, L"playername not found in authmanager");
3187 std::string checkpwd = m_authmanager.getPassword(playername);
3189 if(oldpwd != checkpwd)
3191 dstream<<"Server: invalid old password"<<std::endl;
3192 // Wrong old password supplied!!
3193 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3197 m_authmanager.setPassword(playername, newpwd);
3199 dstream<<"Server: password change successful for "<<playername
3201 SendChatMessage(peer_id, L"Password change successful");
3205 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3206 "unknown command "<<command<<std::endl;
3210 catch(SendFailedException &e)
3212 derr_server<<"Server::ProcessData(): SendFailedException: "
3218 void Server::onMapEditEvent(MapEditEvent *event)
3220 dstream<<"Server::onMapEditEvent()"<<std::endl;
3221 if(m_ignore_map_edit_events)
3223 MapEditEvent *e = event->clone();
3224 m_unsent_map_edit_queue.push_back(e);
3227 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3229 if(id == "current_player")
3231 assert(c->current_player);
3232 return &(c->current_player->inventory);
3236 std::string id0 = fn.next(":");
3238 if(id0 == "nodemeta")
3241 p.X = stoi(fn.next(","));
3242 p.Y = stoi(fn.next(","));
3243 p.Z = stoi(fn.next(","));
3244 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3246 return meta->getInventory();
3247 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3248 <<"no metadata found"<<std::endl;
3252 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3255 void Server::inventoryModified(InventoryContext *c, std::string id)
3257 if(id == "current_player")
3259 assert(c->current_player);
3261 UpdateCrafting(c->current_player->peer_id);
3262 SendInventory(c->current_player->peer_id);
3267 std::string id0 = fn.next(":");
3269 if(id0 == "nodemeta")
3272 p.X = stoi(fn.next(","));
3273 p.Y = stoi(fn.next(","));
3274 p.Z = stoi(fn.next(","));
3275 v3s16 blockpos = getNodeBlockPos(p);
3277 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3279 meta->inventoryModified();
3281 for(core::map<u16, RemoteClient*>::Iterator
3282 i = m_clients.getIterator();
3283 i.atEnd()==false; i++)
3285 RemoteClient *client = i.getNode()->getValue();
3286 client->SetBlockNotSent(blockpos);
3292 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3295 core::list<PlayerInfo> Server::getPlayerInfo()
3297 DSTACK(__FUNCTION_NAME);
3298 JMutexAutoLock envlock(m_env_mutex);
3299 JMutexAutoLock conlock(m_con_mutex);
3301 core::list<PlayerInfo> list;
3303 core::list<Player*> players = m_env.getPlayers();
3305 core::list<Player*>::Iterator i;
3306 for(i = players.begin();
3307 i != players.end(); i++)
3311 Player *player = *i;
3314 con::Peer *peer = m_con.GetPeer(player->peer_id);
3315 // Copy info from peer to info struct
3317 info.address = peer->address;
3318 info.avg_rtt = peer->avg_rtt;
3320 catch(con::PeerNotFoundException &e)
3322 // Set dummy peer info
3324 info.address = Address(0,0,0,0,0);
3328 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3329 info.position = player->getPosition();
3331 list.push_back(info);
3338 void Server::peerAdded(con::Peer *peer)
3340 DSTACK(__FUNCTION_NAME);
3341 dout_server<<"Server::peerAdded(): peer->id="
3342 <<peer->id<<std::endl;
3345 c.type = PEER_ADDED;
3346 c.peer_id = peer->id;
3348 m_peer_change_queue.push_back(c);
3351 void Server::deletingPeer(con::Peer *peer, bool timeout)
3353 DSTACK(__FUNCTION_NAME);
3354 dout_server<<"Server::deletingPeer(): peer->id="
3355 <<peer->id<<", timeout="<<timeout<<std::endl;
3358 c.type = PEER_REMOVED;
3359 c.peer_id = peer->id;
3360 c.timeout = timeout;
3361 m_peer_change_queue.push_back(c);
3368 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3370 DSTACK(__FUNCTION_NAME);
3371 std::ostringstream os(std::ios_base::binary);
3373 writeU16(os, TOCLIENT_HP);
3377 std::string s = os.str();
3378 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3380 con.Send(peer_id, 0, data, true);
3383 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3384 const std::wstring &reason)
3386 DSTACK(__FUNCTION_NAME);
3387 std::ostringstream os(std::ios_base::binary);
3389 writeU16(os, TOCLIENT_ACCESS_DENIED);
3390 os<<serializeWideString(reason);
3393 std::string s = os.str();
3394 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3396 con.Send(peer_id, 0, data, true);
3400 Non-static send methods
3403 void Server::SendObjectData(float dtime)
3405 DSTACK(__FUNCTION_NAME);
3407 core::map<v3s16, bool> stepped_blocks;
3409 for(core::map<u16, RemoteClient*>::Iterator
3410 i = m_clients.getIterator();
3411 i.atEnd() == false; i++)
3413 u16 peer_id = i.getNode()->getKey();
3414 RemoteClient *client = i.getNode()->getValue();
3415 assert(client->peer_id == peer_id);
3417 if(client->serialization_version == SER_FMT_VER_INVALID)
3420 client->SendObjectData(this, dtime, stepped_blocks);
3424 void Server::SendPlayerInfos()
3426 DSTACK(__FUNCTION_NAME);
3428 //JMutexAutoLock envlock(m_env_mutex);
3430 // Get connected players
3431 core::list<Player*> players = m_env.getPlayers(true);
3433 u32 player_count = players.getSize();
3434 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3436 SharedBuffer<u8> data(datasize);
3437 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3440 core::list<Player*>::Iterator i;
3441 for(i = players.begin();
3442 i != players.end(); i++)
3444 Player *player = *i;
3446 /*dstream<<"Server sending player info for player with "
3447 "peer_id="<<player->peer_id<<std::endl;*/
3449 writeU16(&data[start], player->peer_id);
3450 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3451 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3452 start += 2+PLAYERNAME_SIZE;
3455 //JMutexAutoLock conlock(m_con_mutex);
3458 m_con.SendToAll(0, data, true);
3461 void Server::SendInventory(u16 peer_id)
3463 DSTACK(__FUNCTION_NAME);
3465 Player* player = m_env.getPlayer(peer_id);
3472 std::ostringstream os;
3473 //os.imbue(std::locale("C"));
3475 player->inventory.serialize(os);
3477 std::string s = os.str();
3479 SharedBuffer<u8> data(s.size()+2);
3480 writeU16(&data[0], TOCLIENT_INVENTORY);
3481 memcpy(&data[2], s.c_str(), s.size());
3484 m_con.Send(peer_id, 0, data, true);
3487 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3489 DSTACK(__FUNCTION_NAME);
3491 std::ostringstream os(std::ios_base::binary);
3495 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3496 os.write((char*)buf, 2);
3499 writeU16(buf, message.size());
3500 os.write((char*)buf, 2);
3503 for(u32 i=0; i<message.size(); i++)
3507 os.write((char*)buf, 2);
3511 std::string s = os.str();
3512 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3514 m_con.Send(peer_id, 0, data, true);
3517 void Server::BroadcastChatMessage(const std::wstring &message)
3519 for(core::map<u16, RemoteClient*>::Iterator
3520 i = m_clients.getIterator();
3521 i.atEnd() == false; i++)
3523 // Get client and check that it is valid
3524 RemoteClient *client = i.getNode()->getValue();
3525 assert(client->peer_id == i.getNode()->getKey());
3526 if(client->serialization_version == SER_FMT_VER_INVALID)
3529 SendChatMessage(client->peer_id, message);
3533 void Server::SendPlayerHP(Player *player)
3535 SendHP(m_con, player->peer_id, player->hp);
3538 void Server::SendMovePlayer(Player *player)
3540 DSTACK(__FUNCTION_NAME);
3541 std::ostringstream os(std::ios_base::binary);
3543 writeU16(os, TOCLIENT_MOVE_PLAYER);
3544 writeV3F1000(os, player->getPosition());
3545 writeF1000(os, player->getPitch());
3546 writeF1000(os, player->getYaw());
3549 v3f pos = player->getPosition();
3550 f32 pitch = player->getPitch();
3551 f32 yaw = player->getYaw();
3552 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3553 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3560 std::string s = os.str();
3561 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3563 m_con.Send(player->peer_id, 0, data, true);
3566 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3567 core::list<u16> *far_players, float far_d_nodes)
3569 float maxd = far_d_nodes*BS;
3570 v3f p_f = intToFloat(p, BS);
3574 SharedBuffer<u8> reply(replysize);
3575 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3576 writeS16(&reply[2], p.X);
3577 writeS16(&reply[4], p.Y);
3578 writeS16(&reply[6], p.Z);
3580 for(core::map<u16, RemoteClient*>::Iterator
3581 i = m_clients.getIterator();
3582 i.atEnd() == false; i++)
3584 // Get client and check that it is valid
3585 RemoteClient *client = i.getNode()->getValue();
3586 assert(client->peer_id == i.getNode()->getKey());
3587 if(client->serialization_version == SER_FMT_VER_INVALID)
3590 // Don't send if it's the same one
3591 if(client->peer_id == ignore_id)
3597 Player *player = m_env.getPlayer(client->peer_id);
3600 // If player is far away, only set modified blocks not sent
3601 v3f player_pos = player->getPosition();
3602 if(player_pos.getDistanceFrom(p_f) > maxd)
3604 far_players->push_back(client->peer_id);
3611 m_con.Send(client->peer_id, 0, reply, true);
3615 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3616 core::list<u16> *far_players, float far_d_nodes)
3618 float maxd = far_d_nodes*BS;
3619 v3f p_f = intToFloat(p, BS);
3621 for(core::map<u16, RemoteClient*>::Iterator
3622 i = m_clients.getIterator();
3623 i.atEnd() == false; i++)
3625 // Get client and check that it is valid
3626 RemoteClient *client = i.getNode()->getValue();
3627 assert(client->peer_id == i.getNode()->getKey());
3628 if(client->serialization_version == SER_FMT_VER_INVALID)
3631 // Don't send if it's the same one
3632 if(client->peer_id == ignore_id)
3638 Player *player = m_env.getPlayer(client->peer_id);
3641 // If player is far away, only set modified blocks not sent
3642 v3f player_pos = player->getPosition();
3643 if(player_pos.getDistanceFrom(p_f) > maxd)
3645 far_players->push_back(client->peer_id);
3652 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3653 SharedBuffer<u8> reply(replysize);
3654 writeU16(&reply[0], TOCLIENT_ADDNODE);
3655 writeS16(&reply[2], p.X);
3656 writeS16(&reply[4], p.Y);
3657 writeS16(&reply[6], p.Z);
3658 n.serialize(&reply[8], client->serialization_version);
3661 m_con.Send(client->peer_id, 0, reply, true);
3665 void Server::setBlockNotSent(v3s16 p)
3667 for(core::map<u16, RemoteClient*>::Iterator
3668 i = m_clients.getIterator();
3669 i.atEnd()==false; i++)
3671 RemoteClient *client = i.getNode()->getValue();
3672 client->SetBlockNotSent(p);
3676 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3678 DSTACK(__FUNCTION_NAME);
3680 v3s16 p = block->getPos();
3684 bool completely_air = true;
3685 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3686 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3687 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3689 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3691 completely_air = false;
3692 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3697 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3699 dstream<<"[completely air] ";
3704 Create a packet with the block in the right format
3707 std::ostringstream os(std::ios_base::binary);
3708 block->serialize(os, ver);
3709 std::string s = os.str();
3710 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3712 u32 replysize = 8 + blockdata.getSize();
3713 SharedBuffer<u8> reply(replysize);
3714 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3715 writeS16(&reply[2], p.X);
3716 writeS16(&reply[4], p.Y);
3717 writeS16(&reply[6], p.Z);
3718 memcpy(&reply[8], *blockdata, blockdata.getSize());
3720 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3721 <<": \tpacket size: "<<replysize<<std::endl;*/
3726 m_con.Send(peer_id, 1, reply, true);
3729 void Server::SendBlocks(float dtime)
3731 DSTACK(__FUNCTION_NAME);
3733 JMutexAutoLock envlock(m_env_mutex);
3734 JMutexAutoLock conlock(m_con_mutex);
3736 //TimeTaker timer("Server::SendBlocks");
3738 core::array<PrioritySortedBlockTransfer> queue;
3740 s32 total_sending = 0;
3743 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3745 for(core::map<u16, RemoteClient*>::Iterator
3746 i = m_clients.getIterator();
3747 i.atEnd() == false; i++)
3749 RemoteClient *client = i.getNode()->getValue();
3750 assert(client->peer_id == i.getNode()->getKey());
3752 total_sending += client->SendingCount();
3754 if(client->serialization_version == SER_FMT_VER_INVALID)
3757 client->GetNextBlocks(this, dtime, queue);
3762 // Lowest priority number comes first.
3763 // Lowest is most important.
3766 for(u32 i=0; i<queue.size(); i++)
3768 //TODO: Calculate limit dynamically
3769 if(total_sending >= g_settings.getS32
3770 ("max_simultaneous_block_sends_server_total"))
3773 PrioritySortedBlockTransfer q = queue[i];
3775 MapBlock *block = NULL;
3778 block = m_env.getMap().getBlockNoCreate(q.pos);
3780 catch(InvalidPositionException &e)
3785 RemoteClient *client = getClient(q.peer_id);
3787 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3789 client->SentBlock(q.pos);
3799 void Server::UpdateCrafting(u16 peer_id)
3801 DSTACK(__FUNCTION_NAME);
3803 Player* player = m_env.getPlayer(peer_id);
3807 Calculate crafting stuff
3809 if(g_settings.getBool("creative_mode") == false)
3811 InventoryList *clist = player->inventory.getList("craft");
3812 InventoryList *rlist = player->inventory.getList("craftresult");
3814 if(rlist->getUsedSlots() == 0)
3815 player->craftresult_is_preview = true;
3817 if(rlist && player->craftresult_is_preview)
3819 rlist->clearItems();
3821 if(clist && rlist && player->craftresult_is_preview)
3823 InventoryItem *items[9];
3824 for(u16 i=0; i<9; i++)
3826 items[i] = clist->getItem(i);
3835 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3836 if(checkItemCombination(items, specs))
3838 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3847 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3848 if(checkItemCombination(items, specs))
3850 rlist->addItem(new CraftItem("Stick", 4));
3859 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3860 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3861 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3862 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3863 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3864 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3865 if(checkItemCombination(items, specs))
3867 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3876 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3877 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3878 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3879 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3880 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3881 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3882 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3883 if(checkItemCombination(items, specs))
3885 //rlist->addItem(new MapBlockObjectItem("Sign"));
3886 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3895 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3896 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3897 if(checkItemCombination(items, specs))
3899 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3908 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3909 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3910 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3911 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3912 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3913 if(checkItemCombination(items, specs))
3915 rlist->addItem(new ToolItem("WPick", 0));
3924 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3925 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3926 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3927 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3928 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3929 if(checkItemCombination(items, specs))
3931 rlist->addItem(new ToolItem("STPick", 0));
3940 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3941 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3942 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3943 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3944 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3945 if(checkItemCombination(items, specs))
3947 rlist->addItem(new ToolItem("SteelPick", 0));
3956 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3957 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3958 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3959 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3960 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3961 if(checkItemCombination(items, specs))
3963 rlist->addItem(new ToolItem("MesePick", 0));
3972 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3973 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3974 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3975 if(checkItemCombination(items, specs))
3977 rlist->addItem(new ToolItem("WShovel", 0));
3986 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3987 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3988 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3989 if(checkItemCombination(items, specs))
3991 rlist->addItem(new ToolItem("STShovel", 0));
4000 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4001 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4002 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4003 if(checkItemCombination(items, specs))
4005 rlist->addItem(new ToolItem("SteelShovel", 0));
4014 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4015 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4016 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4017 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4018 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4019 if(checkItemCombination(items, specs))
4021 rlist->addItem(new ToolItem("WAxe", 0));
4030 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4031 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4032 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4033 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4034 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4035 if(checkItemCombination(items, specs))
4037 rlist->addItem(new ToolItem("STAxe", 0));
4046 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4047 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4048 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4049 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4050 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4051 if(checkItemCombination(items, specs))
4053 rlist->addItem(new ToolItem("SteelAxe", 0));
4062 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4063 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4064 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4065 if(checkItemCombination(items, specs))
4067 rlist->addItem(new ToolItem("WSword", 0));
4076 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4077 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4078 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4079 if(checkItemCombination(items, specs))
4081 rlist->addItem(new ToolItem("STSword", 0));
4090 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4091 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4092 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4093 if(checkItemCombination(items, specs))
4095 rlist->addItem(new ToolItem("SteelSword", 0));
4104 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4105 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4106 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4107 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4108 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4109 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4110 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4111 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4112 if(checkItemCombination(items, specs))
4114 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4123 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4124 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4125 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4126 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4127 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4128 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4129 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4130 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4131 if(checkItemCombination(items, specs))
4133 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4142 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4143 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4144 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4145 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4146 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4147 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4148 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4149 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4150 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4151 if(checkItemCombination(items, specs))
4153 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4159 } // if creative_mode == false
4162 RemoteClient* Server::getClient(u16 peer_id)
4164 DSTACK(__FUNCTION_NAME);
4165 //JMutexAutoLock lock(m_con_mutex);
4166 core::map<u16, RemoteClient*>::Node *n;
4167 n = m_clients.find(peer_id);
4168 // A client should exist for all peers
4170 return n->getValue();
4173 std::wstring Server::getStatusString()
4175 std::wostringstream os(std::ios_base::binary);
4178 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4180 os<<L", uptime="<<m_uptime.get();
4181 // Information about clients
4183 for(core::map<u16, RemoteClient*>::Iterator
4184 i = m_clients.getIterator();
4185 i.atEnd() == false; i++)
4187 // Get client and check that it is valid
4188 RemoteClient *client = i.getNode()->getValue();
4189 assert(client->peer_id == i.getNode()->getKey());
4190 if(client->serialization_version == SER_FMT_VER_INVALID)
4193 Player *player = m_env.getPlayer(client->peer_id);
4194 // Get name of player
4195 std::wstring name = L"unknown";
4197 name = narrow_to_wide(player->getName());
4198 // Add name to information string
4202 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4203 os<<" WARNING: Map saving is disabled."<<std::endl;
4208 void setCreativeInventory(Player *player)
4210 player->resetInventory();
4212 // Give some good tools
4214 InventoryItem *item = new ToolItem("MesePick", 0);
4215 void* r = player->inventory.addItem("main", item);
4219 InventoryItem *item = new ToolItem("SteelPick", 0);
4220 void* r = player->inventory.addItem("main", item);
4224 InventoryItem *item = new ToolItem("SteelAxe", 0);
4225 void* r = player->inventory.addItem("main", item);
4229 InventoryItem *item = new ToolItem("SteelShovel", 0);
4230 void* r = player->inventory.addItem("main", item);
4238 // CONTENT_IGNORE-terminated list
4239 u8 material_items[] = {
4250 CONTENT_WATERSOURCE,
4258 u8 *mip = material_items;
4259 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4261 if(*mip == CONTENT_IGNORE)
4264 InventoryItem *item = new MaterialItem(*mip, 1);
4265 player->inventory.addItem("main", item);
4271 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4274 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4275 player->inventory.addItem("main", item);
4278 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4280 // Skip some materials
4281 if(i == CONTENT_WATER || i == CONTENT_TORCH
4282 || i == CONTENT_COALSTONE)
4285 InventoryItem *item = new MaterialItem(i, 1);
4286 player->inventory.addItem("main", item);
4292 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4293 void* r = player->inventory.addItem("main", item);
4298 v3f findSpawnPos(ServerMap &map)
4300 //return v3f(50,50,50)*BS;
4303 s16 groundheight = 0;
4305 // Try to find a good place a few times
4306 for(s32 i=0; i<1000; i++)
4309 // We're going to try to throw the player to this position
4310 nodepos = v2s16(-range + (myrand()%(range*2)),
4311 -range + (myrand()%(range*2)));
4312 v2s16 sectorpos = getNodeSectorPos(nodepos);
4313 // Get sector (NOTE: Don't get because it's slow)
4314 //m_env.getMap().emergeSector(sectorpos);
4315 // Get ground height at point (fallbacks to heightmap function)
4316 groundheight = map.findGroundLevel(nodepos);
4317 // Don't go underwater
4318 if(groundheight < WATER_LEVEL)
4320 //dstream<<"-> Underwater"<<std::endl;
4323 // Don't go to high places
4324 if(groundheight > WATER_LEVEL + 4)
4326 //dstream<<"-> Underwater"<<std::endl;
4330 // Found a good place
4331 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4335 // If no suitable place was not found, go above water at least.
4336 if(groundheight < WATER_LEVEL)
4337 groundheight = WATER_LEVEL;
4339 return intToFloat(v3s16(
4346 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4349 Try to get an existing player
4351 Player *player = m_env.getPlayer(name);
4354 // If player is already connected, cancel
4355 if(player->peer_id != 0)
4357 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4362 player->peer_id = peer_id;
4364 // Reset inventory to creative if in creative mode
4365 if(g_settings.getBool("creative_mode"))
4367 setCreativeInventory(player);
4374 If player with the wanted peer_id already exists, cancel.
4376 if(m_env.getPlayer(peer_id) != NULL)
4378 dstream<<"emergePlayer(): Player with wrong name but same"
4379 " peer_id already exists"<<std::endl;
4387 player = new ServerRemotePlayer();
4388 //player->peer_id = c.peer_id;
4389 //player->peer_id = PEER_ID_INEXISTENT;
4390 player->peer_id = peer_id;
4391 player->updateName(name);
4392 m_authmanager.add(name);
4393 m_authmanager.setPassword(name, password);
4394 m_authmanager.setPrivs(name,
4395 stringToPrivs(g_settings.get("default_privs")));
4401 dstream<<"Server: Finding spawn place for player \""
4402 <<player->getName()<<"\""<<std::endl;
4404 v3f pos = findSpawnPos(m_env.getServerMap());
4406 player->setPosition(pos);
4409 Add player to environment
4412 m_env.addPlayer(player);
4415 Add stuff to inventory
4418 if(g_settings.getBool("creative_mode"))
4420 setCreativeInventory(player);
4422 else if(g_settings.getBool("give_initial_stuff"))
4425 InventoryItem *item = new ToolItem("SteelPick", 0);
4426 void* r = player->inventory.addItem("main", item);
4430 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4431 void* r = player->inventory.addItem("main", item);
4435 InventoryItem *item = new ToolItem("SteelAxe", 0);
4436 void* r = player->inventory.addItem("main", item);
4440 InventoryItem *item = new ToolItem("SteelShovel", 0);
4441 void* r = player->inventory.addItem("main", item);
4445 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4446 void* r = player->inventory.addItem("main", item);
4450 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4451 void* r = player->inventory.addItem("main", item);
4455 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4456 void* r = player->inventory.addItem("main", item);
4460 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4461 void* r = player->inventory.addItem("main", item);
4465 InventoryItem *item = new CraftItem("Stick", 4);
4466 void* r = player->inventory.addItem("main", item);
4470 InventoryItem *item = new ToolItem("WPick", 32000);
4471 void* r = player->inventory.addItem("main", item);
4475 InventoryItem *item = new ToolItem("STPick", 32000);
4476 void* r = player->inventory.addItem("main", item);
4480 for(u16 i=0; i<4; i++)
4482 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4483 bool r = player->inventory.addItem("main", item);
4486 /*// Give some other stuff
4488 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4489 bool r = player->inventory.addItem("main", item);
4496 } // create new player
4499 void Server::handlePeerChange(PeerChange &c)
4501 JMutexAutoLock envlock(m_env_mutex);
4502 JMutexAutoLock conlock(m_con_mutex);
4504 if(c.type == PEER_ADDED)
4511 core::map<u16, RemoteClient*>::Node *n;
4512 n = m_clients.find(c.peer_id);
4513 // The client shouldn't already exist
4517 RemoteClient *client = new RemoteClient();
4518 client->peer_id = c.peer_id;
4519 m_clients.insert(client->peer_id, client);
4522 else if(c.type == PEER_REMOVED)
4529 core::map<u16, RemoteClient*>::Node *n;
4530 n = m_clients.find(c.peer_id);
4531 // The client should exist
4535 Mark objects to be not known by the client
4537 RemoteClient *client = n->getValue();
4539 for(core::map<u16, bool>::Iterator
4540 i = client->m_known_objects.getIterator();
4541 i.atEnd()==false; i++)
4544 u16 id = i.getNode()->getKey();
4545 ServerActiveObject* obj = m_env.getActiveObject(id);
4547 if(obj && obj->m_known_by_count > 0)
4548 obj->m_known_by_count--;
4551 // Collect information about leaving in chat
4552 std::wstring message;
4554 std::wstring name = L"unknown";
4555 Player *player = m_env.getPlayer(c.peer_id);
4557 name = narrow_to_wide(player->getName());
4561 message += L" left game";
4563 message += L" (timed out)";
4568 m_env.removePlayer(c.peer_id);
4571 // Set player client disconnected
4573 Player *player = m_env.getPlayer(c.peer_id);
4575 player->peer_id = 0;
4579 delete m_clients[c.peer_id];
4580 m_clients.remove(c.peer_id);
4582 // Send player info to all remaining clients
4585 // Send leave chat message to all remaining clients
4586 BroadcastChatMessage(message);
4595 void Server::handlePeerChanges()
4597 while(m_peer_change_queue.size() > 0)
4599 PeerChange c = m_peer_change_queue.pop_front();
4601 dout_server<<"Server: Handling peer change: "
4602 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4605 handlePeerChange(c);
4609 u64 Server::getPlayerPrivs(Player *player)
4613 std::string playername = player->getName();
4614 // Local player gets all privileges regardless of
4615 // what's set on their account.
4616 if(g_settings.get("name") == playername)
4622 return getPlayerAuthPrivs(playername);
4626 void dedicated_server_loop(Server &server, bool &kill)
4628 DSTACK(__FUNCTION_NAME);
4630 dstream<<DTIME<<std::endl;
4631 dstream<<"========================"<<std::endl;
4632 dstream<<"Running dedicated server"<<std::endl;
4633 dstream<<"========================"<<std::endl;
4636 IntervalLimiter m_profiler_interval;
4640 // This is kind of a hack but can be done like this
4641 // because server.step() is very light
4643 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4648 if(server.getShutdownRequested() || kill)
4650 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4657 float profiler_print_interval =
4658 g_settings.getFloat("profiler_print_interval");
4659 if(profiler_print_interval != 0)
4661 if(m_profiler_interval.step(0.030, profiler_print_interval))
4663 dstream<<"Profiler:"<<std::endl;
4664 g_profiler.print(dstream);
4672 static int counter = 0;
4678 core::list<PlayerInfo> list = server.getPlayerInfo();
4679 core::list<PlayerInfo>::Iterator i;
4680 static u32 sum_old = 0;
4681 u32 sum = PIChecksum(list);
4684 dstream<<DTIME<<"Player info:"<<std::endl;
4685 for(i=list.begin(); i!=list.end(); i++)
4687 i->PrintLine(&dstream);