3 Copyright (C) 2010 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"
33 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
35 void * ServerThread::Thread()
39 DSTACK(__FUNCTION_NAME);
41 BEGIN_DEBUG_EXCEPTION_HANDLER
46 //TimeTaker timer("AsyncRunStep() + Receive()");
49 //TimeTaker timer("AsyncRunStep()");
50 m_server->AsyncRunStep();
53 //dout_server<<"Running m_server->Receive()"<<std::endl;
56 catch(con::NoIncomingDataException &e)
59 catch(con::PeerNotFoundException &e)
61 dout_server<<"Server: PeerNotFoundException"<<std::endl;
65 END_DEBUG_EXCEPTION_HANDLER
70 void * EmergeThread::Thread()
74 DSTACK(__FUNCTION_NAME);
78 BEGIN_DEBUG_EXCEPTION_HANDLER
81 Get block info from queue, emerge them and send them
84 After queue is empty, exit.
88 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
92 SharedPtr<QueuedBlockEmerge> q(qptr);
98 Do not generate over-limit
100 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
101 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
108 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
110 //TimeTaker timer("block emerge");
113 Try to emerge it from somewhere.
115 If it is only wanted as optional, only loading from disk
120 Check if any peer wants it as non-optional. In that case it
123 Also decrement the emerge queue count in clients.
126 bool optional = true;
129 core::map<u16, u8>::Iterator i;
130 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
132 //u16 peer_id = i.getNode()->getKey();
135 u8 flags = i.getNode()->getValue();
136 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
142 /*dstream<<"EmergeThread: p="
143 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
144 <<"optional="<<optional<<std::endl;*/
146 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
148 core::map<v3s16, MapBlock*> changed_blocks;
149 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
151 MapBlock *block = NULL;
152 bool got_block = true;
153 core::map<v3s16, MapBlock*> modified_blocks;
155 bool only_from_disk = false;
158 only_from_disk = true;
160 v2s16 chunkpos = map.sector_to_chunk(p2d);
162 bool generate_chunk = false;
163 if(only_from_disk == false)
165 JMutexAutoLock envlock(m_server->m_env_mutex);
166 if(map.chunkNonVolatile(chunkpos) == false)
167 generate_chunk = true;
174 JMutexAutoLock envlock(m_server->m_env_mutex);
175 map.initChunkMake(data, chunkpos);
181 JMutexAutoLock envlock(m_server->m_env_mutex);
182 map.finishChunkMake(data, changed_blocks);
187 Fetch block from map or generate a single block
190 JMutexAutoLock envlock(m_server->m_env_mutex);
192 // Load sector if it isn't loaded
193 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
194 map.loadSectorFull(p2d);
196 block = map.getBlockNoCreateNoEx(p);
197 if(!block || block->isDummy())
205 // Get, load or create sector
206 ServerMapSector *sector =
207 (ServerMapSector*)map.createSector(p2d);
209 block = map.generateBlock(p, block, sector, changed_blocks,
210 lighting_invalidated_blocks);
217 if(block->getLightingExpired()){
218 lighting_invalidated_blocks[block->getPos()] = block;
222 // TODO: Some additional checking and lighting updating,
227 JMutexAutoLock envlock(m_server->m_env_mutex);
232 Collect a list of blocks that have been modified in
233 addition to the fetched one.
236 if(lighting_invalidated_blocks.size() > 0)
238 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
239 <<" blocks"<<std::endl;*/
241 // 50-100ms for single block generation
242 //TimeTaker timer("** EmergeThread updateLighting");
244 // Update lighting without locking the environment mutex,
245 // add modified blocks to changed blocks
246 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
249 // Add all from changed_blocks to modified_blocks
250 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
251 i.atEnd() == false; i++)
253 MapBlock *block = i.getNode()->getValue();
254 modified_blocks.insert(block->getPos(), block);
257 // If we got no block, there should be no invalidated blocks
260 assert(lighting_invalidated_blocks.size() == 0);
266 Set sent status of modified blocks on clients
269 // NOTE: Server's clients are also behind the connection mutex
270 JMutexAutoLock lock(m_server->m_con_mutex);
273 Add the originally fetched block to the modified list
277 modified_blocks.insert(p, block);
281 Set the modified blocks unsent for all the clients
284 for(core::map<u16, RemoteClient*>::Iterator
285 i = m_server->m_clients.getIterator();
286 i.atEnd() == false; i++)
288 RemoteClient *client = i.getNode()->getValue();
290 if(modified_blocks.size() > 0)
292 // Remove block from sent history
293 client->SetBlocksNotSent(modified_blocks);
299 END_DEBUG_EXCEPTION_HANDLER
304 void RemoteClient::GetNextBlocks(Server *server, float dtime,
305 core::array<PrioritySortedBlockTransfer> &dest)
307 DSTACK(__FUNCTION_NAME);
310 m_nearest_unsent_reset_timer += dtime;
312 // Won't send anything if already sending
313 if(m_blocks_sending.size() >= g_settings.getU16
314 ("max_simultaneous_block_sends_per_client"))
316 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
320 Player *player = server->m_env.getPlayer(peer_id);
322 assert(player != NULL);
324 v3f playerpos = player->getPosition();
325 v3f playerspeed = player->getSpeed();
327 v3s16 center_nodepos = floatToInt(playerpos, BS);
329 v3s16 center = getNodeBlockPos(center_nodepos);
331 // Camera position and direction
333 playerpos + v3f(0, BS+BS/2, 0);
334 v3f camera_dir = v3f(0,0,1);
335 camera_dir.rotateYZBy(player->getPitch());
336 camera_dir.rotateXZBy(player->getYaw());
339 Get the starting value of the block finder radius.
341 s16 last_nearest_unsent_d;
344 if(m_last_center != center)
346 m_nearest_unsent_d = 0;
347 m_last_center = center;
350 /*dstream<<"m_nearest_unsent_reset_timer="
351 <<m_nearest_unsent_reset_timer<<std::endl;*/
352 if(m_nearest_unsent_reset_timer > 5.0)
354 m_nearest_unsent_reset_timer = 0;
355 m_nearest_unsent_d = 0;
356 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
359 last_nearest_unsent_d = m_nearest_unsent_d;
361 d_start = m_nearest_unsent_d;
363 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
364 ("max_simultaneous_block_sends_per_client");
365 u16 maximum_simultaneous_block_sends =
366 maximum_simultaneous_block_sends_setting;
369 Check the time from last addNode/removeNode.
371 Decrease send rate if player is building stuff.
373 m_time_from_building += dtime;
374 if(m_time_from_building < g_settings.getFloat(
375 "full_block_send_enable_min_time_from_building"))
377 maximum_simultaneous_block_sends
378 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
381 u32 num_blocks_selected = m_blocks_sending.size();
384 next time d will be continued from the d from which the nearest
385 unsent block was found this time.
387 This is because not necessarily any of the blocks found this
388 time are actually sent.
390 s32 new_nearest_unsent_d = -1;
392 s16 d_max = g_settings.getS16("max_block_send_distance");
393 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
395 //dstream<<"Starting from "<<d_start<<std::endl;
397 for(s16 d = d_start; d <= d_max; d++)
399 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
402 If m_nearest_unsent_d was changed by the EmergeThread
403 (it can change it to 0 through SetBlockNotSent),
405 Else update m_nearest_unsent_d
407 if(m_nearest_unsent_d != last_nearest_unsent_d)
409 d = m_nearest_unsent_d;
410 last_nearest_unsent_d = m_nearest_unsent_d;
414 Get the border/face dot coordinates of a "d-radiused"
417 core::list<v3s16> list;
418 getFacePositions(list, d);
420 core::list<v3s16>::Iterator li;
421 for(li=list.begin(); li!=list.end(); li++)
423 v3s16 p = *li + center;
427 - Don't allow too many simultaneous transfers
428 - EXCEPT when the blocks are very close
430 Also, don't send blocks that are already flying.
433 u16 maximum_simultaneous_block_sends_now =
434 maximum_simultaneous_block_sends;
436 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
438 maximum_simultaneous_block_sends_now =
439 maximum_simultaneous_block_sends_setting;
442 // Limit is dynamically lowered when building
443 if(num_blocks_selected
444 >= maximum_simultaneous_block_sends_now)
446 /*dstream<<"Not sending more blocks. Queue full. "
447 <<m_blocks_sending.size()
452 if(m_blocks_sending.find(p) != NULL)
458 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
459 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
460 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
461 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
462 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
466 // If this is true, inexistent block will be made from scratch
467 bool generate = d <= d_max_gen;
470 /*// Limit the generating area vertically to 2/3
471 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
474 // Limit the send area vertically to 2/3
475 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
481 If block is far away, don't generate it unless it is
484 NOTE: We can't know the ground level this way with the
490 MapSector *sector = NULL;
493 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
495 catch(InvalidPositionException &e)
501 // Get center ground height in nodes
502 f32 gh = sector->getGroundHeight(
503 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
504 // Block center y in nodes
505 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
506 // If differs a lot, don't generate
507 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
514 Don't generate or send if not in sight
517 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
523 Don't send already sent blocks
526 if(m_blocks_sent.find(p) != NULL)
531 Check if map has this block
533 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
535 bool surely_not_found_on_disk = false;
536 bool block_is_invalid = false;
541 surely_not_found_on_disk = true;
544 if(block->isValid() == false)
546 block_is_invalid = true;
549 /*if(block->isFullyGenerated() == false)
551 block_is_invalid = true;
555 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
556 v2s16 chunkpos = map->sector_to_chunk(p2d);
557 if(map->chunkNonVolatile(chunkpos) == false)
558 block_is_invalid = true;
562 If block has been marked to not exist on disk (dummy)
563 and generating new ones is not wanted, skip block.
565 if(generate == false && surely_not_found_on_disk == true)
572 Record the lowest d from which a a block has been
573 found being not sent and possibly to exist
575 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
577 new_nearest_unsent_d = d;
581 Add inexistent block to emerge queue.
583 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
585 //TODO: Get value from somewhere
586 // Allow only one block in emerge queue
587 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
588 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
590 //dstream<<"Adding block to emerge queue"<<std::endl;
592 // Add it to the emerge queue and trigger the thread
595 if(generate == false)
596 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
598 server->m_emerge_queue.addBlock(peer_id, p, flags);
599 server->m_emergethread.trigger();
607 Add block to send queue
610 PrioritySortedBlockTransfer q((float)d, p, peer_id);
614 num_blocks_selected += 1;
619 if(new_nearest_unsent_d != -1)
621 m_nearest_unsent_d = new_nearest_unsent_d;
625 void RemoteClient::SendObjectData(
628 core::map<v3s16, bool> &stepped_blocks
631 DSTACK(__FUNCTION_NAME);
633 // Can't send anything without knowing version
634 if(serialization_version == SER_FMT_VER_INVALID)
636 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
642 Send a TOCLIENT_OBJECTDATA packet.
646 u16 number of player positions
657 std::ostringstream os(std::ios_base::binary);
661 writeU16(buf, TOCLIENT_OBJECTDATA);
662 os.write((char*)buf, 2);
665 Get and write player data
668 // Get connected players
669 core::list<Player*> players = server->m_env.getPlayers(true);
671 // Write player count
672 u16 playercount = players.size();
673 writeU16(buf, playercount);
674 os.write((char*)buf, 2);
676 core::list<Player*>::Iterator i;
677 for(i = players.begin();
678 i != players.end(); i++)
682 v3f pf = player->getPosition();
683 v3f sf = player->getSpeed();
685 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
686 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
687 s32 pitch_i (player->getPitch() * 100);
688 s32 yaw_i (player->getYaw() * 100);
690 writeU16(buf, player->peer_id);
691 os.write((char*)buf, 2);
692 writeV3S32(buf, position_i);
693 os.write((char*)buf, 12);
694 writeV3S32(buf, speed_i);
695 os.write((char*)buf, 12);
696 writeS32(buf, pitch_i);
697 os.write((char*)buf, 4);
698 writeS32(buf, yaw_i);
699 os.write((char*)buf, 4);
703 Get and write object data
709 For making players to be able to build to their nearby
710 environment (building is not possible on blocks that are not
713 - Add blocks to emerge queue if they are not found
715 SUGGESTION: These could be ignored from the backside of the player
718 Player *player = server->m_env.getPlayer(peer_id);
722 v3f playerpos = player->getPosition();
723 v3f playerspeed = player->getSpeed();
725 v3s16 center_nodepos = floatToInt(playerpos, BS);
726 v3s16 center = getNodeBlockPos(center_nodepos);
728 s16 d_max = g_settings.getS16("active_object_range");
730 // Number of blocks whose objects were written to bos
733 std::ostringstream bos(std::ios_base::binary);
735 for(s16 d = 0; d <= d_max; d++)
737 core::list<v3s16> list;
738 getFacePositions(list, d);
740 core::list<v3s16>::Iterator li;
741 for(li=list.begin(); li!=list.end(); li++)
743 v3s16 p = *li + center;
746 Ignore blocks that haven't been sent to the client
749 if(m_blocks_sent.find(p) == NULL)
753 // Try stepping block and add it to a send queue
758 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
761 Step block if not in stepped_blocks and add to stepped_blocks.
763 if(stepped_blocks.find(p) == NULL)
765 block->stepObjects(dtime, true, server->getDayNightRatio());
766 stepped_blocks.insert(p, true);
767 block->setChangedFlag();
770 // Skip block if there are no objects
771 if(block->getObjectCount() == 0)
780 bos.write((char*)buf, 6);
783 block->serializeObjects(bos, serialization_version);
788 Stop collecting objects if data is already too big
790 // Sum of player and object data sizes
791 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
792 // break out if data too big
793 if(sum > MAX_OBJECTDATA_SIZE)
795 goto skip_subsequent;
799 catch(InvalidPositionException &e)
802 // Add it to the emerge queue and trigger the thread.
803 // Fetch the block only if it is on disk.
805 // Grab and increment counter
806 /*SharedPtr<JMutexAutoLock> lock
807 (m_num_blocks_in_emerge_queue.getLock());
808 m_num_blocks_in_emerge_queue.m_value++;*/
810 // Add to queue as an anonymous fetch from disk
811 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
812 server->m_emerge_queue.addBlock(0, p, flags);
813 server->m_emergethread.trigger();
821 writeU16(buf, blockcount);
822 os.write((char*)buf, 2);
824 // Write block objects
831 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
834 std::string s = os.str();
835 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
836 // Send as unreliable
837 server->m_con.Send(peer_id, 0, data, false);
840 void RemoteClient::GotBlock(v3s16 p)
842 if(m_blocks_sending.find(p) != NULL)
843 m_blocks_sending.remove(p);
846 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
847 " m_blocks_sending"<<std::endl;*/
848 m_excess_gotblocks++;
850 m_blocks_sent.insert(p, true);
853 void RemoteClient::SentBlock(v3s16 p)
855 if(m_blocks_sending.find(p) == NULL)
856 m_blocks_sending.insert(p, 0.0);
858 dstream<<"RemoteClient::SentBlock(): Sent block"
859 " already in m_blocks_sending"<<std::endl;
862 void RemoteClient::SetBlockNotSent(v3s16 p)
864 m_nearest_unsent_d = 0;
866 if(m_blocks_sending.find(p) != NULL)
867 m_blocks_sending.remove(p);
868 if(m_blocks_sent.find(p) != NULL)
869 m_blocks_sent.remove(p);
872 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
874 m_nearest_unsent_d = 0;
876 for(core::map<v3s16, MapBlock*>::Iterator
877 i = blocks.getIterator();
878 i.atEnd()==false; i++)
880 v3s16 p = i.getNode()->getKey();
882 if(m_blocks_sending.find(p) != NULL)
883 m_blocks_sending.remove(p);
884 if(m_blocks_sent.find(p) != NULL)
885 m_blocks_sent.remove(p);
893 PlayerInfo::PlayerInfo()
899 void PlayerInfo::PrintLine(std::ostream *s)
902 (*s)<<"\""<<name<<"\" ("
903 <<(position.X/10)<<","<<(position.Y/10)
904 <<","<<(position.Z/10)<<") ";
906 (*s)<<" avg_rtt="<<avg_rtt;
910 u32 PIChecksum(core::list<PlayerInfo> &l)
912 core::list<PlayerInfo>::Iterator i;
915 for(i=l.begin(); i!=l.end(); i++)
917 checksum += a * (i->id+1);
918 checksum ^= 0x435aafcd;
929 std::string mapsavedir
931 m_env(new ServerMap(mapsavedir), this),
932 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
934 m_emergethread(this),
937 m_time_of_day_send_timer(0),
939 m_mapsavedir(mapsavedir),
940 m_shutdown_requested(false),
941 m_ignore_map_edit_events(false),
942 m_ignore_map_edit_events_peer_id(0)
944 m_liquid_transform_timer = 0.0;
945 m_print_info_timer = 0.0;
946 m_objectdata_timer = 0.0;
947 m_emergethread_trigger_timer = 0.0;
948 m_savemap_timer = 0.0;
952 m_step_dtime_mutex.Init();
955 m_env.getMap().addEventReceiver(this);
958 m_env.deSerializePlayers(m_mapsavedir);
963 dstream<<"Server::~Server()"<<std::endl;
966 Send shutdown message
969 JMutexAutoLock conlock(m_con_mutex);
971 std::wstring line = L"*** Server shutting down";
974 Send the message to clients
976 for(core::map<u16, RemoteClient*>::Iterator
977 i = m_clients.getIterator();
978 i.atEnd() == false; i++)
980 // Get client and check that it is valid
981 RemoteClient *client = i.getNode()->getValue();
982 assert(client->peer_id == i.getNode()->getKey());
983 if(client->serialization_version == SER_FMT_VER_INVALID)
987 SendChatMessage(client->peer_id, line);
989 catch(con::PeerNotFoundException &e)
997 dstream<<"Server: Saving players"<<std::endl;
998 m_env.serializePlayers(m_mapsavedir);
1009 JMutexAutoLock clientslock(m_con_mutex);
1011 for(core::map<u16, RemoteClient*>::Iterator
1012 i = m_clients.getIterator();
1013 i.atEnd() == false; i++)
1016 // NOTE: These are removed by env destructor
1018 u16 peer_id = i.getNode()->getKey();
1019 JMutexAutoLock envlock(m_env_mutex);
1020 m_env.removePlayer(peer_id);
1024 delete i.getNode()->getValue();
1029 void Server::start(unsigned short port)
1031 DSTACK(__FUNCTION_NAME);
1032 // Stop thread if already running
1035 // Initialize connection
1036 m_con.setTimeoutMs(30);
1040 m_thread.setRun(true);
1043 dout_server<<"Server: Started on port "<<port<<std::endl;
1048 DSTACK(__FUNCTION_NAME);
1050 // Stop threads (set run=false first so both start stopping)
1051 m_thread.setRun(false);
1052 m_emergethread.setRun(false);
1054 m_emergethread.stop();
1056 dout_server<<"Server: Threads stopped"<<std::endl;
1059 void Server::step(float dtime)
1061 DSTACK(__FUNCTION_NAME);
1066 JMutexAutoLock lock(m_step_dtime_mutex);
1067 m_step_dtime += dtime;
1071 void Server::AsyncRunStep()
1073 DSTACK(__FUNCTION_NAME);
1077 JMutexAutoLock lock1(m_step_dtime_mutex);
1078 dtime = m_step_dtime;
1081 // Send blocks to clients
1087 //dstream<<"Server steps "<<dtime<<std::endl;
1088 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1091 JMutexAutoLock lock1(m_step_dtime_mutex);
1092 m_step_dtime -= dtime;
1099 m_uptime.set(m_uptime.get() + dtime);
1103 Update m_time_of_day
1106 m_time_counter += dtime;
1107 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1108 u32 units = (u32)(m_time_counter*speed);
1109 m_time_counter -= (f32)units / speed;
1110 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1112 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1115 Send to clients at constant intervals
1118 m_time_of_day_send_timer -= dtime;
1119 if(m_time_of_day_send_timer < 0.0)
1121 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1123 //JMutexAutoLock envlock(m_env_mutex);
1124 JMutexAutoLock conlock(m_con_mutex);
1126 for(core::map<u16, RemoteClient*>::Iterator
1127 i = m_clients.getIterator();
1128 i.atEnd() == false; i++)
1130 RemoteClient *client = i.getNode()->getValue();
1131 //Player *player = m_env.getPlayer(client->peer_id);
1133 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1134 m_time_of_day.get());
1136 m_con.Send(client->peer_id, 0, data, true);
1142 // Process connection's timeouts
1143 JMutexAutoLock lock2(m_con_mutex);
1144 m_con.RunTimeouts(dtime);
1148 // This has to be called so that the client list gets synced
1149 // with the peer list of the connection
1150 handlePeerChanges();
1155 // This also runs Map's timers
1156 JMutexAutoLock lock(m_env_mutex);
1167 m_liquid_transform_timer += dtime;
1168 if(m_liquid_transform_timer >= 1.00)
1170 m_liquid_transform_timer -= 1.00;
1172 JMutexAutoLock lock(m_env_mutex);
1174 core::map<v3s16, MapBlock*> modified_blocks;
1175 m_env.getMap().transformLiquids(modified_blocks);
1180 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1181 ServerMap &map = ((ServerMap&)m_env.getMap());
1182 map.updateLighting(modified_blocks, lighting_modified_blocks);
1184 // Add blocks modified by lighting to modified_blocks
1185 for(core::map<v3s16, MapBlock*>::Iterator
1186 i = lighting_modified_blocks.getIterator();
1187 i.atEnd() == false; i++)
1189 MapBlock *block = i.getNode()->getValue();
1190 modified_blocks.insert(block->getPos(), block);
1194 Set the modified blocks unsent for all the clients
1197 JMutexAutoLock lock2(m_con_mutex);
1199 for(core::map<u16, RemoteClient*>::Iterator
1200 i = m_clients.getIterator();
1201 i.atEnd() == false; i++)
1203 RemoteClient *client = i.getNode()->getValue();
1205 if(modified_blocks.size() > 0)
1207 // Remove block from sent history
1208 client->SetBlocksNotSent(modified_blocks);
1213 // Periodically print some info
1215 float &counter = m_print_info_timer;
1221 JMutexAutoLock lock2(m_con_mutex);
1223 for(core::map<u16, RemoteClient*>::Iterator
1224 i = m_clients.getIterator();
1225 i.atEnd() == false; i++)
1227 //u16 peer_id = i.getNode()->getKey();
1228 RemoteClient *client = i.getNode()->getValue();
1229 Player *player = m_env.getPlayer(client->peer_id);
1232 std::cout<<player->getName()<<"\t";
1233 client->PrintInfo(std::cout);
1238 //if(g_settings.getBool("enable_experimental"))
1242 Check added and deleted active objects
1245 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1247 JMutexAutoLock envlock(m_env_mutex);
1248 JMutexAutoLock conlock(m_con_mutex);
1250 // Radius inside which objects are active
1253 for(core::map<u16, RemoteClient*>::Iterator
1254 i = m_clients.getIterator();
1255 i.atEnd() == false; i++)
1257 RemoteClient *client = i.getNode()->getValue();
1258 Player *player = m_env.getPlayer(client->peer_id);
1261 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1262 <<" has no associated player"<<std::endl;
1265 v3s16 pos = floatToInt(player->getPosition(), BS);
1267 core::map<u16, bool> removed_objects;
1268 core::map<u16, bool> added_objects;
1269 m_env.getRemovedActiveObjects(pos, radius,
1270 client->m_known_objects, removed_objects);
1271 m_env.getAddedActiveObjects(pos, radius,
1272 client->m_known_objects, added_objects);
1274 // Ignore if nothing happened
1275 if(removed_objects.size() == 0 && added_objects.size() == 0)
1277 //dstream<<"INFO: active objects: none changed"<<std::endl;
1281 std::string data_buffer;
1285 // Handle removed objects
1286 writeU16((u8*)buf, removed_objects.size());
1287 data_buffer.append(buf, 2);
1288 for(core::map<u16, bool>::Iterator
1289 i = removed_objects.getIterator();
1290 i.atEnd()==false; i++)
1293 u16 id = i.getNode()->getKey();
1294 ServerActiveObject* obj = m_env.getActiveObject(id);
1296 // Add to data buffer for sending
1297 writeU16((u8*)buf, i.getNode()->getKey());
1298 data_buffer.append(buf, 2);
1300 // Remove from known objects
1301 client->m_known_objects.remove(i.getNode()->getKey());
1303 if(obj && obj->m_known_by_count > 0)
1304 obj->m_known_by_count--;
1307 // Handle added objects
1308 writeU16((u8*)buf, added_objects.size());
1309 data_buffer.append(buf, 2);
1310 for(core::map<u16, bool>::Iterator
1311 i = added_objects.getIterator();
1312 i.atEnd()==false; i++)
1315 u16 id = i.getNode()->getKey();
1316 ServerActiveObject* obj = m_env.getActiveObject(id);
1319 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1321 dstream<<"WARNING: "<<__FUNCTION_NAME
1322 <<": NULL object"<<std::endl;
1324 type = obj->getType();
1326 // Add to data buffer for sending
1327 writeU16((u8*)buf, id);
1328 data_buffer.append(buf, 2);
1329 writeU8((u8*)buf, type);
1330 data_buffer.append(buf, 1);
1333 data_buffer.append(serializeLongString(
1334 obj->getClientInitializationData()));
1336 data_buffer.append(serializeLongString(""));
1338 // Add to known objects
1339 client->m_known_objects.insert(i.getNode()->getKey(), false);
1342 obj->m_known_by_count++;
1346 SharedBuffer<u8> reply(2 + data_buffer.size());
1347 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1348 memcpy((char*)&reply[2], data_buffer.c_str(),
1349 data_buffer.size());
1351 m_con.Send(client->peer_id, 0, reply, true);
1353 dstream<<"INFO: Server: Sent object remove/add: "
1354 <<removed_objects.size()<<" removed, "
1355 <<added_objects.size()<<" added, "
1356 <<"packet size is "<<reply.getSize()<<std::endl;
1361 Collect a list of all the objects known by the clients
1362 and report it back to the environment.
1365 core::map<u16, bool> all_known_objects;
1367 for(core::map<u16, RemoteClient*>::Iterator
1368 i = m_clients.getIterator();
1369 i.atEnd() == false; i++)
1371 RemoteClient *client = i.getNode()->getValue();
1372 // Go through all known objects of client
1373 for(core::map<u16, bool>::Iterator
1374 i = client->m_known_objects.getIterator();
1375 i.atEnd()==false; i++)
1377 u16 id = i.getNode()->getKey();
1378 all_known_objects[id] = true;
1382 m_env.setKnownActiveObjects(whatever);
1388 Send object messages
1391 JMutexAutoLock envlock(m_env_mutex);
1392 JMutexAutoLock conlock(m_con_mutex);
1395 // Value = data sent by object
1396 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1398 // Get active object messages from environment
1401 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1405 core::list<ActiveObjectMessage>* message_list = NULL;
1406 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1407 n = buffered_messages.find(aom.id);
1410 message_list = new core::list<ActiveObjectMessage>;
1411 buffered_messages.insert(aom.id, message_list);
1415 message_list = n->getValue();
1417 message_list->push_back(aom);
1420 // Route data to every client
1421 for(core::map<u16, RemoteClient*>::Iterator
1422 i = m_clients.getIterator();
1423 i.atEnd()==false; i++)
1425 RemoteClient *client = i.getNode()->getValue();
1426 std::string reliable_data;
1427 std::string unreliable_data;
1428 // Go through all objects in message buffer
1429 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1430 j = buffered_messages.getIterator();
1431 j.atEnd()==false; j++)
1433 // If object is not known by client, skip it
1434 u16 id = j.getNode()->getKey();
1435 if(client->m_known_objects.find(id) == NULL)
1437 // Get message list of object
1438 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1439 // Go through every message
1440 for(core::list<ActiveObjectMessage>::Iterator
1441 k = list->begin(); k != list->end(); k++)
1443 // Compose the full new data with header
1444 ActiveObjectMessage aom = *k;
1445 std::string new_data;
1448 writeU16((u8*)&buf[0], aom.id);
1449 new_data.append(buf, 2);
1451 new_data += serializeString(aom.datastring);
1452 // Add data to buffer
1454 reliable_data += new_data;
1456 unreliable_data += new_data;
1460 reliable_data and unreliable_data are now ready.
1463 if(reliable_data.size() > 0)
1465 SharedBuffer<u8> reply(2 + reliable_data.size());
1466 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1467 memcpy((char*)&reply[2], reliable_data.c_str(),
1468 reliable_data.size());
1470 m_con.Send(client->peer_id, 0, reply, true);
1472 if(unreliable_data.size() > 0)
1474 SharedBuffer<u8> reply(2 + unreliable_data.size());
1475 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1476 memcpy((char*)&reply[2], unreliable_data.c_str(),
1477 unreliable_data.size());
1478 // Send as unreliable
1479 m_con.Send(client->peer_id, 0, reply, false);
1482 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1484 dstream<<"INFO: Server: Size of object message data: "
1485 <<"reliable: "<<reliable_data.size()
1486 <<", unreliable: "<<unreliable_data.size()
1491 // Clear buffered_messages
1492 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1493 i = buffered_messages.getIterator();
1494 i.atEnd()==false; i++)
1496 delete i.getNode()->getValue();
1500 } // enable_experimental
1503 Send queued-for-sending map edit events.
1506 while(m_unsent_map_edit_queue.size() != 0)
1508 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1510 if(event->type == MEET_ADDNODE)
1512 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1513 sendAddNode(event->p, event->n, event->already_known_by_peer);
1515 else if(event->type == MEET_REMOVENODE)
1517 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1518 sendRemoveNode(event->p, event->already_known_by_peer);
1520 else if(event->type == MEET_OTHER)
1522 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1527 dstream<<"WARNING: Server: Unknown MapEditEvent "
1528 <<((u32)event->type)<<std::endl;
1536 Send object positions
1537 TODO: Get rid of MapBlockObjects
1540 float &counter = m_objectdata_timer;
1542 if(counter >= g_settings.getFloat("objectdata_interval"))
1544 JMutexAutoLock lock1(m_env_mutex);
1545 JMutexAutoLock lock2(m_con_mutex);
1546 SendObjectData(counter);
1556 //TimeTaker timer("Step node metadata");
1558 JMutexAutoLock envlock(m_env_mutex);
1559 JMutexAutoLock conlock(m_con_mutex);
1561 core::map<v3s16, MapBlock*> changed_blocks;
1562 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1564 for(core::map<v3s16, MapBlock*>::Iterator
1565 i = changed_blocks.getIterator();
1566 i.atEnd() == false; i++)
1568 MapBlock *block = i.getNode()->getValue();
1570 for(core::map<u16, RemoteClient*>::Iterator
1571 i = m_clients.getIterator();
1572 i.atEnd()==false; i++)
1574 RemoteClient *client = i.getNode()->getValue();
1575 client->SetBlockNotSent(block->getPos());
1581 Trigger emergethread (it somehow gets to a non-triggered but
1582 bysy state sometimes)
1585 float &counter = m_emergethread_trigger_timer;
1591 m_emergethread.trigger();
1597 float &counter = m_savemap_timer;
1599 if(counter >= g_settings.getFloat("server_map_save_interval"))
1603 JMutexAutoLock lock(m_env_mutex);
1605 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1607 // Save only changed parts
1608 m_env.getMap().save(true);
1610 // Delete unused sectors
1611 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1612 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1613 if(deleted_count > 0)
1615 dout_server<<"Server: Unloaded "<<deleted_count
1616 <<" sectors from memory"<<std::endl;
1620 m_env.serializePlayers(m_mapsavedir);
1626 void Server::Receive()
1628 DSTACK(__FUNCTION_NAME);
1629 u32 data_maxsize = 10000;
1630 Buffer<u8> data(data_maxsize);
1635 JMutexAutoLock conlock(m_con_mutex);
1636 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1639 // This has to be called so that the client list gets synced
1640 // with the peer list of the connection
1641 handlePeerChanges();
1643 ProcessData(*data, datasize, peer_id);
1645 catch(con::InvalidIncomingDataException &e)
1647 derr_server<<"Server::Receive(): "
1648 "InvalidIncomingDataException: what()="
1649 <<e.what()<<std::endl;
1651 catch(con::PeerNotFoundException &e)
1653 //NOTE: This is not needed anymore
1655 // The peer has been disconnected.
1656 // Find the associated player and remove it.
1658 /*JMutexAutoLock envlock(m_env_mutex);
1660 dout_server<<"ServerThread: peer_id="<<peer_id
1661 <<" has apparently closed connection. "
1662 <<"Removing player."<<std::endl;
1664 m_env.removePlayer(peer_id);*/
1668 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1670 DSTACK(__FUNCTION_NAME);
1671 // Environment is locked first.
1672 JMutexAutoLock envlock(m_env_mutex);
1673 JMutexAutoLock conlock(m_con_mutex);
1677 peer = m_con.GetPeer(peer_id);
1679 catch(con::PeerNotFoundException &e)
1681 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1682 <<peer_id<<" not found"<<std::endl;
1686 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1694 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1696 if(command == TOSERVER_INIT)
1698 // [0] u16 TOSERVER_INIT
1699 // [2] u8 SER_FMT_VER_HIGHEST
1700 // [3] u8[20] player_name
1705 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1706 <<peer->id<<std::endl;
1708 // First byte after command is maximum supported
1709 // serialization version
1710 u8 client_max = data[2];
1711 u8 our_max = SER_FMT_VER_HIGHEST;
1712 // Use the highest version supported by both
1713 u8 deployed = core::min_(client_max, our_max);
1714 // If it's lower than the lowest supported, give up.
1715 if(deployed < SER_FMT_VER_LOWEST)
1716 deployed = SER_FMT_VER_INVALID;
1718 //peer->serialization_version = deployed;
1719 getClient(peer->id)->pending_serialization_version = deployed;
1721 if(deployed == SER_FMT_VER_INVALID)
1723 derr_server<<DTIME<<"Server: Cannot negotiate "
1724 "serialization version with peer "
1725 <<peer_id<<std::endl;
1734 const u32 playername_size = 20;
1735 char playername[playername_size];
1736 for(u32 i=0; i<playername_size-1; i++)
1738 playername[i] = data[3+i];
1740 playername[playername_size-1] = 0;
1743 Player *player = emergePlayer(playername, "", peer_id);
1744 //Player *player = m_env.getPlayer(peer_id);
1747 // DEBUG: Test serialization
1748 std::ostringstream test_os;
1749 player->serialize(test_os);
1750 dstream<<"Player serialization test: \""<<test_os.str()
1752 std::istringstream test_is(test_os.str());
1753 player->deSerialize(test_is);
1756 // If failed, cancel
1759 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1760 <<": failed to emerge player"<<std::endl;
1765 // If a client is already connected to the player, cancel
1766 if(player->peer_id != 0)
1768 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1769 <<" tried to connect to "
1770 "an already connected player (peer_id="
1771 <<player->peer_id<<")"<<std::endl;
1774 // Set client of player
1775 player->peer_id = peer_id;
1778 // Check if player doesn't exist
1780 throw con::InvalidIncomingDataException
1781 ("Server::ProcessData(): INIT: Player doesn't exist");
1783 /*// update name if it was supplied
1784 if(datasize >= 20+3)
1787 player->updateName((const char*)&data[3]);
1791 Answer with a TOCLIENT_INIT
1794 SharedBuffer<u8> reply(2+1+6+8);
1795 writeU16(&reply[0], TOCLIENT_INIT);
1796 writeU8(&reply[2], deployed);
1797 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1798 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1799 writeU64(&reply[2+1+6], 0); // no seed
1802 m_con.Send(peer_id, 0, reply, true);
1806 Send complete position information
1808 SendMovePlayer(player);
1813 if(command == TOSERVER_INIT2)
1815 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1816 <<peer->id<<std::endl;
1819 getClient(peer->id)->serialization_version
1820 = getClient(peer->id)->pending_serialization_version;
1823 Send some initialization data
1826 // Send player info to all players
1829 // Send inventory to player
1830 UpdateCrafting(peer->id);
1831 SendInventory(peer->id);
1835 Player *player = m_env.getPlayer(peer_id);
1836 SendPlayerHP(player);
1841 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1842 m_time_of_day.get());
1843 m_con.Send(peer->id, 0, data, true);
1846 // Send information about server to player in chat
1847 SendChatMessage(peer_id, getStatusString());
1849 // Send information about joining in chat
1851 std::wstring name = L"unknown";
1852 Player *player = m_env.getPlayer(peer_id);
1854 name = narrow_to_wide(player->getName());
1856 std::wstring message;
1859 message += L" joined game";
1860 BroadcastChatMessage(message);
1866 if(peer_ser_ver == SER_FMT_VER_INVALID)
1868 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1869 " serialization format invalid or not initialized."
1870 " Skipping incoming command="<<command<<std::endl;
1874 Player *player = m_env.getPlayer(peer_id);
1877 derr_server<<"Server::ProcessData(): Cancelling: "
1878 "No player for peer_id="<<peer_id
1882 if(command == TOSERVER_PLAYERPOS)
1884 if(datasize < 2+12+12+4+4)
1888 v3s32 ps = readV3S32(&data[start+2]);
1889 v3s32 ss = readV3S32(&data[start+2+12]);
1890 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1891 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1892 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1893 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1894 pitch = wrapDegrees(pitch);
1895 yaw = wrapDegrees(yaw);
1896 player->setPosition(position);
1897 player->setSpeed(speed);
1898 player->setPitch(pitch);
1899 player->setYaw(yaw);
1901 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1902 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1903 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1905 else if(command == TOSERVER_GOTBLOCKS)
1918 u16 count = data[2];
1919 for(u16 i=0; i<count; i++)
1921 if((s16)datasize < 2+1+(i+1)*6)
1922 throw con::InvalidIncomingDataException
1923 ("GOTBLOCKS length is too short");
1924 v3s16 p = readV3S16(&data[2+1+i*6]);
1925 /*dstream<<"Server: GOTBLOCKS ("
1926 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1927 RemoteClient *client = getClient(peer_id);
1928 client->GotBlock(p);
1931 else if(command == TOSERVER_DELETEDBLOCKS)
1944 u16 count = data[2];
1945 for(u16 i=0; i<count; i++)
1947 if((s16)datasize < 2+1+(i+1)*6)
1948 throw con::InvalidIncomingDataException
1949 ("DELETEDBLOCKS length is too short");
1950 v3s16 p = readV3S16(&data[2+1+i*6]);
1951 /*dstream<<"Server: DELETEDBLOCKS ("
1952 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1953 RemoteClient *client = getClient(peer_id);
1954 client->SetBlockNotSent(p);
1957 else if(command == TOSERVER_CLICK_OBJECT)
1964 [2] u8 button (0=left, 1=right)
1969 u8 button = readU8(&data[2]);
1971 p.X = readS16(&data[3]);
1972 p.Y = readS16(&data[5]);
1973 p.Z = readS16(&data[7]);
1974 s16 id = readS16(&data[9]);
1975 //u16 item_i = readU16(&data[11]);
1977 MapBlock *block = NULL;
1980 block = m_env.getMap().getBlockNoCreate(p);
1982 catch(InvalidPositionException &e)
1984 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1988 MapBlockObject *obj = block->getObject(id);
1992 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1996 //TODO: Check that object is reasonably close
2001 InventoryList *ilist = player->inventory.getList("main");
2002 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2005 // Skip if inventory has no free space
2006 if(ilist->getUsedSlots() == ilist->getSize())
2008 dout_server<<"Player inventory has no free space"<<std::endl;
2013 Create the inventory item
2015 InventoryItem *item = NULL;
2016 // If it is an item-object, take the item from it
2017 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2019 item = ((ItemObject*)obj)->createInventoryItem();
2021 // Else create an item of the object
2024 item = new MapBlockObjectItem
2025 (obj->getInventoryString());
2028 // Add to inventory and send inventory
2029 ilist->addItem(item);
2030 UpdateCrafting(player->peer_id);
2031 SendInventory(player->peer_id);
2034 // Remove from block
2035 block->removeObject(id);
2038 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2046 [2] u8 button (0=left, 1=right)
2050 u8 button = readU8(&data[2]);
2051 u16 id = readS16(&data[3]);
2052 u16 item_i = readU16(&data[11]);
2054 ServerActiveObject *obj = m_env.getActiveObject(id);
2058 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2063 //TODO: Check that object is reasonably close
2065 // Left click, pick object up (usually)
2068 InventoryList *ilist = player->inventory.getList("main");
2069 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2072 // Skip if inventory has no free space
2073 if(ilist->getUsedSlots() == ilist->getSize())
2075 dout_server<<"Player inventory has no free space"<<std::endl;
2079 // Skip if object has been removed
2084 Create the inventory item
2086 InventoryItem *item = obj->createPickedUpItem();
2090 // Add to inventory and send inventory
2091 ilist->addItem(item);
2092 UpdateCrafting(player->peer_id);
2093 SendInventory(player->peer_id);
2095 // Remove object from environment
2096 obj->m_removed = true;
2101 Item cannot be picked up. Punch it instead.
2104 ToolItem *titem = NULL;
2105 std::string toolname = "";
2107 InventoryList *mlist = player->inventory.getList("main");
2110 InventoryItem *item = mlist->getItem(item_i);
2111 if(item && (std::string)item->getName() == "ToolItem")
2113 titem = (ToolItem*)item;
2114 toolname = titem->getToolName();
2118 u16 wear = obj->punch(toolname);
2122 bool weared_out = titem->addWear(wear);
2124 mlist->deleteItem(item_i);
2125 SendInventory(player->peer_id);
2131 else if(command == TOSERVER_GROUND_ACTION)
2139 [3] v3s16 nodepos_undersurface
2140 [9] v3s16 nodepos_abovesurface
2145 2: stop digging (all parameters ignored)
2146 3: digging completed
2148 u8 action = readU8(&data[2]);
2150 p_under.X = readS16(&data[3]);
2151 p_under.Y = readS16(&data[5]);
2152 p_under.Z = readS16(&data[7]);
2154 p_over.X = readS16(&data[9]);
2155 p_over.Y = readS16(&data[11]);
2156 p_over.Z = readS16(&data[13]);
2157 u16 item_i = readU16(&data[15]);
2159 //TODO: Check that target is reasonably close
2167 NOTE: This can be used in the future to check if
2168 somebody is cheating, by checking the timing.
2175 else if(action == 2)
2178 RemoteClient *client = getClient(peer->id);
2179 JMutexAutoLock digmutex(client->m_dig_mutex);
2180 client->m_dig_tool_item = -1;
2185 3: Digging completed
2187 else if(action == 3)
2189 // Mandatory parameter; actually used for nothing
2190 core::map<v3s16, MapBlock*> modified_blocks;
2193 u8 mineral = MINERAL_NONE;
2195 bool cannot_remove_node = false;
2199 MapNode n = m_env.getMap().getNode(p_under);
2201 mineral = n.getMineral();
2202 // Get material at position
2204 // If not yet cancelled
2205 if(cannot_remove_node == false)
2207 // If it's not diggable, do nothing
2208 if(content_diggable(material) == false)
2210 derr_server<<"Server: Not finishing digging: "
2211 <<"Node not diggable"
2213 cannot_remove_node = true;
2216 // If not yet cancelled
2217 if(cannot_remove_node == false)
2219 // Get node metadata
2220 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2221 if(meta && meta->nodeRemovalDisabled() == true)
2223 derr_server<<"Server: Not finishing digging: "
2224 <<"Node metadata disables removal"
2226 cannot_remove_node = true;
2230 catch(InvalidPositionException &e)
2232 derr_server<<"Server: Not finishing digging: Node not found."
2233 <<" Adding block to emerge queue."
2235 m_emerge_queue.addBlock(peer_id,
2236 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2237 cannot_remove_node = true;
2241 If node can't be removed, set block to be re-sent to
2244 if(cannot_remove_node)
2246 derr_server<<"Server: Not finishing digging."<<std::endl;
2248 // Client probably has wrong data.
2249 // Set block not sent, so that client will get
2251 dstream<<"Client "<<peer_id<<" tried to dig "
2252 <<"node; but node cannot be removed."
2253 <<" setting MapBlock not sent."<<std::endl;
2254 RemoteClient *client = getClient(peer_id);
2255 v3s16 blockpos = getNodeBlockPos(p_under);
2256 client->SetBlockNotSent(blockpos);
2262 Send the removal to all other clients.
2263 - If other player is close, send REMOVENODE
2264 - Otherwise set blocks not sent
2266 core::list<u16> far_players;
2267 sendRemoveNode(p_under, peer_id, &far_players, 100);
2270 Update and send inventory
2273 if(g_settings.getBool("creative_mode") == false)
2278 InventoryList *mlist = player->inventory.getList("main");
2281 InventoryItem *item = mlist->getItem(item_i);
2282 if(item && (std::string)item->getName() == "ToolItem")
2284 ToolItem *titem = (ToolItem*)item;
2285 std::string toolname = titem->getToolName();
2287 // Get digging properties for material and tool
2288 DiggingProperties prop =
2289 getDiggingProperties(material, toolname);
2291 if(prop.diggable == false)
2293 derr_server<<"Server: WARNING: Player digged"
2294 <<" with impossible material + tool"
2295 <<" combination"<<std::endl;
2298 bool weared_out = titem->addWear(prop.wear);
2302 mlist->deleteItem(item_i);
2308 Add dug item to inventory
2311 InventoryItem *item = NULL;
2313 if(mineral != MINERAL_NONE)
2314 item = getDiggedMineralItem(mineral);
2319 std::string &dug_s = content_features(material).dug_item;
2322 std::istringstream is(dug_s, std::ios::binary);
2323 item = InventoryItem::deSerialize(is);
2329 // Add a item to inventory
2330 player->inventory.addItem("main", item);
2333 UpdateCrafting(player->peer_id);
2334 SendInventory(player->peer_id);
2340 (this takes some time so it is done after the quick stuff)
2342 m_ignore_map_edit_events = true;
2343 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2344 m_ignore_map_edit_events = false;
2347 Set blocks not sent to far players
2349 for(core::list<u16>::Iterator
2350 i = far_players.begin();
2351 i != far_players.end(); i++)
2354 RemoteClient *client = getClient(peer_id);
2357 client->SetBlocksNotSent(modified_blocks);
2364 else if(action == 1)
2367 InventoryList *ilist = player->inventory.getList("main");
2372 InventoryItem *item = ilist->getItem(item_i);
2374 // If there is no item, it is not possible to add it anywhere
2379 Handle material items
2381 if(std::string("MaterialItem") == item->getName())
2384 // Don't add a node if this is not a free space
2385 MapNode n2 = m_env.getMap().getNode(p_over);
2386 if(content_buildable_to(n2.d) == false)
2388 // Client probably has wrong data.
2389 // Set block not sent, so that client will get
2391 dstream<<"Client "<<peer_id<<" tried to place"
2392 <<" node in invalid position; setting"
2393 <<" MapBlock not sent."<<std::endl;
2394 RemoteClient *client = getClient(peer_id);
2395 v3s16 blockpos = getNodeBlockPos(p_over);
2396 client->SetBlockNotSent(blockpos);
2400 catch(InvalidPositionException &e)
2402 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2403 <<" Adding block to emerge queue."
2405 m_emerge_queue.addBlock(peer_id,
2406 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2410 // Reset build time counter
2411 getClient(peer->id)->m_time_from_building = 0.0;
2414 MaterialItem *mitem = (MaterialItem*)item;
2416 n.d = mitem->getMaterial();
2417 if(content_features(n.d).wall_mounted)
2418 n.dir = packDir(p_under - p_over);
2423 core::list<u16> far_players;
2424 sendAddNode(p_over, n, 0, &far_players, 100);
2429 InventoryList *ilist = player->inventory.getList("main");
2430 if(g_settings.getBool("creative_mode") == false && ilist)
2432 // Remove from inventory and send inventory
2433 if(mitem->getCount() == 1)
2434 ilist->deleteItem(item_i);
2438 UpdateCrafting(peer_id);
2439 SendInventory(peer_id);
2445 This takes some time so it is done after the quick stuff
2447 core::map<v3s16, MapBlock*> modified_blocks;
2448 m_ignore_map_edit_events = true;
2449 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2450 m_ignore_map_edit_events = false;
2453 Set blocks not sent to far players
2455 for(core::list<u16>::Iterator
2456 i = far_players.begin();
2457 i != far_players.end(); i++)
2460 RemoteClient *client = getClient(peer_id);
2463 client->SetBlocksNotSent(modified_blocks);
2467 Calculate special events
2470 /*if(n.d == CONTENT_MESE)
2473 for(s16 z=-1; z<=1; z++)
2474 for(s16 y=-1; y<=1; y++)
2475 for(s16 x=-1; x<=1; x++)
2482 Place other item (not a block)
2486 v3s16 blockpos = getNodeBlockPos(p_over);
2489 Check that the block is loaded so that the item
2490 can properly be added to the static list too
2492 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2495 derr_server<<"Error while placing object: "
2496 "block not found"<<std::endl;
2500 dout_server<<"Placing a miscellaneous item on map"
2503 // Calculate a position for it
2504 v3f pos = intToFloat(p_over, BS);
2506 pos.Y -= BS*0.25; // let it drop a bit
2508 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2509 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2514 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2518 derr_server<<"WARNING: item resulted in NULL object, "
2519 <<"not placing onto map"
2524 // Add the object to the environment
2525 m_env.addActiveObject(obj);
2527 dout_server<<"Placed object"<<std::endl;
2529 if(g_settings.getBool("creative_mode") == false)
2531 // Delete the right amount of items from the slot
2532 u16 dropcount = item->getDropCount();
2534 // Delete item if all gone
2535 if(item->getCount() <= dropcount)
2537 if(item->getCount() < dropcount)
2538 dstream<<"WARNING: Server: dropped more items"
2539 <<" than the slot contains"<<std::endl;
2541 InventoryList *ilist = player->inventory.getList("main");
2543 // Remove from inventory and send inventory
2544 ilist->deleteItem(item_i);
2546 // Else decrement it
2548 item->remove(dropcount);
2551 UpdateCrafting(peer_id);
2552 SendInventory(peer_id);
2560 Catch invalid actions
2564 derr_server<<"WARNING: Server: Invalid action "
2565 <<action<<std::endl;
2569 else if(command == TOSERVER_RELEASE)
2578 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2581 else if(command == TOSERVER_SIGNTEXT)
2590 std::string datastring((char*)&data[2], datasize-2);
2591 std::istringstream is(datastring, std::ios_base::binary);
2594 is.read((char*)buf, 6);
2595 v3s16 blockpos = readV3S16(buf);
2596 is.read((char*)buf, 2);
2597 s16 id = readS16(buf);
2598 is.read((char*)buf, 2);
2599 u16 textlen = readU16(buf);
2601 for(u16 i=0; i<textlen; i++)
2603 is.read((char*)buf, 1);
2604 text += (char)buf[0];
2607 MapBlock *block = NULL;
2610 block = m_env.getMap().getBlockNoCreate(blockpos);
2612 catch(InvalidPositionException &e)
2614 derr_server<<"Error while setting sign text: "
2615 "block not found"<<std::endl;
2619 MapBlockObject *obj = block->getObject(id);
2622 derr_server<<"Error while setting sign text: "
2623 "object not found"<<std::endl;
2627 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2629 derr_server<<"Error while setting sign text: "
2630 "object is not a sign"<<std::endl;
2634 ((SignObject*)obj)->setText(text);
2636 obj->getBlock()->setChangedFlag();
2638 else if(command == TOSERVER_SIGNNODETEXT)
2646 std::string datastring((char*)&data[2], datasize-2);
2647 std::istringstream is(datastring, std::ios_base::binary);
2650 is.read((char*)buf, 6);
2651 v3s16 p = readV3S16(buf);
2652 is.read((char*)buf, 2);
2653 u16 textlen = readU16(buf);
2655 for(u16 i=0; i<textlen; i++)
2657 is.read((char*)buf, 1);
2658 text += (char)buf[0];
2661 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2664 if(meta->typeId() != CONTENT_SIGN_WALL)
2666 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2667 signmeta->setText(text);
2669 v3s16 blockpos = getNodeBlockPos(p);
2670 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2673 block->setChangedFlag();
2676 for(core::map<u16, RemoteClient*>::Iterator
2677 i = m_clients.getIterator();
2678 i.atEnd()==false; i++)
2680 RemoteClient *client = i.getNode()->getValue();
2681 client->SetBlockNotSent(blockpos);
2684 else if(command == TOSERVER_INVENTORY_ACTION)
2686 /*// Ignore inventory changes if in creative mode
2687 if(g_settings.getBool("creative_mode") == true)
2689 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2693 // Strip command and create a stream
2694 std::string datastring((char*)&data[2], datasize-2);
2695 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2696 std::istringstream is(datastring, std::ios_base::binary);
2698 InventoryAction *a = InventoryAction::deSerialize(is);
2703 c.current_player = player;
2706 Handle craftresult specially if not in creative mode
2708 bool disable_action = false;
2709 if(a->getType() == IACTION_MOVE
2710 && g_settings.getBool("creative_mode") == false)
2712 IMoveAction *ma = (IMoveAction*)a;
2713 if(ma->to_inv == "current_player" &&
2714 ma->from_inv == "current_player")
2716 InventoryList *rlist = player->inventory.getList("craftresult");
2718 InventoryList *clist = player->inventory.getList("craft");
2720 InventoryList *mlist = player->inventory.getList("main");
2723 Craftresult is no longer preview if something
2726 if(ma->to_list == "craftresult"
2727 && ma->from_list != "craftresult")
2729 // If it currently is a preview, remove
2731 if(player->craftresult_is_preview)
2733 rlist->deleteItem(0);
2735 player->craftresult_is_preview = false;
2738 Crafting takes place if this condition is true.
2740 if(player->craftresult_is_preview &&
2741 ma->from_list == "craftresult")
2743 player->craftresult_is_preview = false;
2744 clist->decrementMaterials(1);
2747 If the craftresult is placed on itself, move it to
2748 main inventory instead of doing the action
2750 if(ma->to_list == "craftresult"
2751 && ma->from_list == "craftresult")
2753 disable_action = true;
2755 InventoryItem *item1 = rlist->changeItem(0, NULL);
2756 mlist->addItem(item1);
2761 if(disable_action == false)
2763 // Feed action to player inventory
2771 UpdateCrafting(player->peer_id);
2772 SendInventory(player->peer_id);
2777 dstream<<"TOSERVER_INVENTORY_ACTION: "
2778 <<"InventoryAction::deSerialize() returned NULL"
2782 else if(command == TOSERVER_CHAT_MESSAGE)
2790 std::string datastring((char*)&data[2], datasize-2);
2791 std::istringstream is(datastring, std::ios_base::binary);
2794 is.read((char*)buf, 2);
2795 u16 len = readU16(buf);
2797 std::wstring message;
2798 for(u16 i=0; i<len; i++)
2800 is.read((char*)buf, 2);
2801 message += (wchar_t)readU16(buf);
2804 // Get player name of this client
2805 std::wstring name = narrow_to_wide(player->getName());
2807 // Line to send to players
2809 // Whether to send to the player that sent the line
2810 bool send_to_sender = false;
2811 // Whether to send to other players
2812 bool send_to_others = false;
2815 std::wstring commandprefix = L"/#";
2816 if(message.substr(0, commandprefix.size()) == commandprefix)
2818 line += L"Server: ";
2820 message = message.substr(commandprefix.size());
2821 // Get player name as narrow string
2822 std::string name_s = player->getName();
2823 // Convert message to narrow string
2824 std::string message_s = wide_to_narrow(message);
2825 // Operator is the single name defined in config.
2826 std::string operator_name = g_settings.get("name");
2827 bool is_operator = (operator_name != "" &&
2828 wide_to_narrow(name) == operator_name);
2829 bool valid_command = false;
2830 if(message_s == "help")
2832 line += L"-!- Available commands: ";
2836 line += L"shutdown setting ";
2841 send_to_sender = true;
2842 valid_command = true;
2844 else if(message_s == "status")
2846 line = getStatusString();
2847 send_to_sender = true;
2848 valid_command = true;
2850 else if(is_operator)
2852 if(message_s == "shutdown")
2854 dstream<<DTIME<<" Server: Operator requested shutdown."
2856 m_shutdown_requested.set(true);
2858 line += L"*** Server shutting down (operator request)";
2859 send_to_sender = true;
2860 valid_command = true;
2862 else if(message_s.substr(0,8) == "setting ")
2864 std::string confline = message_s.substr(8);
2865 g_settings.parseConfigLine(confline);
2866 line += L"-!- Setting changed.";
2867 send_to_sender = true;
2868 valid_command = true;
2872 if(valid_command == false)
2874 line += L"-!- Invalid command: " + message;
2875 send_to_sender = true;
2886 send_to_others = true;
2891 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2894 Send the message to clients
2896 for(core::map<u16, RemoteClient*>::Iterator
2897 i = m_clients.getIterator();
2898 i.atEnd() == false; i++)
2900 // Get client and check that it is valid
2901 RemoteClient *client = i.getNode()->getValue();
2902 assert(client->peer_id == i.getNode()->getKey());
2903 if(client->serialization_version == SER_FMT_VER_INVALID)
2907 bool sender_selected = (peer_id == client->peer_id);
2908 if(sender_selected == true && send_to_sender == false)
2910 if(sender_selected == false && send_to_others == false)
2913 SendChatMessage(client->peer_id, line);
2917 else if(command == TOSERVER_DAMAGE)
2919 if(g_settings.getBool("enable_damage"))
2921 std::string datastring((char*)&data[2], datasize-2);
2922 std::istringstream is(datastring, std::ios_base::binary);
2923 u8 damage = readU8(is);
2924 if(player->hp > damage)
2926 player->hp -= damage;
2932 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2935 v3f pos = findSpawnPos(m_env.getServerMap());
2936 player->setPosition(pos);
2938 SendMovePlayer(player);
2939 SendPlayerHP(player);
2941 //TODO: Throw items around
2945 SendPlayerHP(player);
2949 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2950 "unknown command "<<command<<std::endl;
2954 catch(SendFailedException &e)
2956 derr_server<<"Server::ProcessData(): SendFailedException: "
2962 void Server::onMapEditEvent(MapEditEvent *event)
2964 dstream<<"Server::onMapEditEvent()"<<std::endl;
2965 if(m_ignore_map_edit_events)
2967 MapEditEvent *e = event->clone();
2968 m_unsent_map_edit_queue.push_back(e);
2971 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2973 if(id == "current_player")
2975 assert(c->current_player);
2976 return &(c->current_player->inventory);
2980 std::string id0 = fn.next(":");
2982 if(id0 == "nodemeta")
2985 p.X = stoi(fn.next(","));
2986 p.Y = stoi(fn.next(","));
2987 p.Z = stoi(fn.next(","));
2988 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2990 return meta->getInventory();
2991 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2992 <<"no metadata found"<<std::endl;
2996 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2999 void Server::inventoryModified(InventoryContext *c, std::string id)
3001 if(id == "current_player")
3003 assert(c->current_player);
3005 UpdateCrafting(c->current_player->peer_id);
3006 SendInventory(c->current_player->peer_id);
3011 std::string id0 = fn.next(":");
3013 if(id0 == "nodemeta")
3016 p.X = stoi(fn.next(","));
3017 p.Y = stoi(fn.next(","));
3018 p.Z = stoi(fn.next(","));
3019 v3s16 blockpos = getNodeBlockPos(p);
3021 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3023 meta->inventoryModified();
3025 for(core::map<u16, RemoteClient*>::Iterator
3026 i = m_clients.getIterator();
3027 i.atEnd()==false; i++)
3029 RemoteClient *client = i.getNode()->getValue();
3030 client->SetBlockNotSent(blockpos);
3036 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3039 core::list<PlayerInfo> Server::getPlayerInfo()
3041 DSTACK(__FUNCTION_NAME);
3042 JMutexAutoLock envlock(m_env_mutex);
3043 JMutexAutoLock conlock(m_con_mutex);
3045 core::list<PlayerInfo> list;
3047 core::list<Player*> players = m_env.getPlayers();
3049 core::list<Player*>::Iterator i;
3050 for(i = players.begin();
3051 i != players.end(); i++)
3055 Player *player = *i;
3058 con::Peer *peer = m_con.GetPeer(player->peer_id);
3059 // Copy info from peer to info struct
3061 info.address = peer->address;
3062 info.avg_rtt = peer->avg_rtt;
3064 catch(con::PeerNotFoundException &e)
3066 // Set dummy peer info
3068 info.address = Address(0,0,0,0,0);
3072 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3073 info.position = player->getPosition();
3075 list.push_back(info);
3082 void Server::peerAdded(con::Peer *peer)
3084 DSTACK(__FUNCTION_NAME);
3085 dout_server<<"Server::peerAdded(): peer->id="
3086 <<peer->id<<std::endl;
3089 c.type = PEER_ADDED;
3090 c.peer_id = peer->id;
3092 m_peer_change_queue.push_back(c);
3095 void Server::deletingPeer(con::Peer *peer, bool timeout)
3097 DSTACK(__FUNCTION_NAME);
3098 dout_server<<"Server::deletingPeer(): peer->id="
3099 <<peer->id<<", timeout="<<timeout<<std::endl;
3102 c.type = PEER_REMOVED;
3103 c.peer_id = peer->id;
3104 c.timeout = timeout;
3105 m_peer_change_queue.push_back(c);
3112 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3114 DSTACK(__FUNCTION_NAME);
3115 std::ostringstream os(std::ios_base::binary);
3117 writeU16(os, TOCLIENT_HP);
3121 std::string s = os.str();
3122 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3124 con.Send(peer_id, 0, data, true);
3128 Non-static send methods
3131 void Server::SendObjectData(float dtime)
3133 DSTACK(__FUNCTION_NAME);
3135 core::map<v3s16, bool> stepped_blocks;
3137 for(core::map<u16, RemoteClient*>::Iterator
3138 i = m_clients.getIterator();
3139 i.atEnd() == false; i++)
3141 u16 peer_id = i.getNode()->getKey();
3142 RemoteClient *client = i.getNode()->getValue();
3143 assert(client->peer_id == peer_id);
3145 if(client->serialization_version == SER_FMT_VER_INVALID)
3148 client->SendObjectData(this, dtime, stepped_blocks);
3152 void Server::SendPlayerInfos()
3154 DSTACK(__FUNCTION_NAME);
3156 //JMutexAutoLock envlock(m_env_mutex);
3158 // Get connected players
3159 core::list<Player*> players = m_env.getPlayers(true);
3161 u32 player_count = players.getSize();
3162 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3164 SharedBuffer<u8> data(datasize);
3165 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3168 core::list<Player*>::Iterator i;
3169 for(i = players.begin();
3170 i != players.end(); i++)
3172 Player *player = *i;
3174 /*dstream<<"Server sending player info for player with "
3175 "peer_id="<<player->peer_id<<std::endl;*/
3177 writeU16(&data[start], player->peer_id);
3178 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3179 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3180 start += 2+PLAYERNAME_SIZE;
3183 //JMutexAutoLock conlock(m_con_mutex);
3186 m_con.SendToAll(0, data, true);
3189 void Server::SendInventory(u16 peer_id)
3191 DSTACK(__FUNCTION_NAME);
3193 Player* player = m_env.getPlayer(peer_id);
3200 std::ostringstream os;
3201 //os.imbue(std::locale("C"));
3203 player->inventory.serialize(os);
3205 std::string s = os.str();
3207 SharedBuffer<u8> data(s.size()+2);
3208 writeU16(&data[0], TOCLIENT_INVENTORY);
3209 memcpy(&data[2], s.c_str(), s.size());
3212 m_con.Send(peer_id, 0, data, true);
3215 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3217 DSTACK(__FUNCTION_NAME);
3219 std::ostringstream os(std::ios_base::binary);
3223 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3224 os.write((char*)buf, 2);
3227 writeU16(buf, message.size());
3228 os.write((char*)buf, 2);
3231 for(u32 i=0; i<message.size(); i++)
3235 os.write((char*)buf, 2);
3239 std::string s = os.str();
3240 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3242 m_con.Send(peer_id, 0, data, true);
3245 void Server::BroadcastChatMessage(const std::wstring &message)
3247 for(core::map<u16, RemoteClient*>::Iterator
3248 i = m_clients.getIterator();
3249 i.atEnd() == false; i++)
3251 // Get client and check that it is valid
3252 RemoteClient *client = i.getNode()->getValue();
3253 assert(client->peer_id == i.getNode()->getKey());
3254 if(client->serialization_version == SER_FMT_VER_INVALID)
3257 SendChatMessage(client->peer_id, message);
3261 void Server::SendPlayerHP(Player *player)
3263 SendHP(m_con, player->peer_id, player->hp);
3266 void Server::SendMovePlayer(Player *player)
3268 DSTACK(__FUNCTION_NAME);
3269 std::ostringstream os(std::ios_base::binary);
3271 writeU16(os, TOCLIENT_MOVE_PLAYER);
3272 writeV3F1000(os, player->getPosition());
3273 writeF1000(os, player->getPitch());
3274 writeF1000(os, player->getYaw());
3277 v3f pos = player->getPosition();
3278 f32 pitch = player->getPitch();
3279 f32 yaw = player->getYaw();
3280 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3281 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3288 std::string s = os.str();
3289 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3291 m_con.Send(player->peer_id, 0, data, true);
3294 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3295 core::list<u16> *far_players, float far_d_nodes)
3297 float maxd = far_d_nodes*BS;
3298 v3f p_f = intToFloat(p, BS);
3302 SharedBuffer<u8> reply(replysize);
3303 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3304 writeS16(&reply[2], p.X);
3305 writeS16(&reply[4], p.Y);
3306 writeS16(&reply[6], p.Z);
3308 for(core::map<u16, RemoteClient*>::Iterator
3309 i = m_clients.getIterator();
3310 i.atEnd() == false; i++)
3312 // Get client and check that it is valid
3313 RemoteClient *client = i.getNode()->getValue();
3314 assert(client->peer_id == i.getNode()->getKey());
3315 if(client->serialization_version == SER_FMT_VER_INVALID)
3318 // Don't send if it's the same one
3319 if(client->peer_id == ignore_id)
3325 Player *player = m_env.getPlayer(client->peer_id);
3328 // If player is far away, only set modified blocks not sent
3329 v3f player_pos = player->getPosition();
3330 if(player_pos.getDistanceFrom(p_f) > maxd)
3332 far_players->push_back(client->peer_id);
3339 m_con.Send(client->peer_id, 0, reply, true);
3343 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3344 core::list<u16> *far_players, float far_d_nodes)
3346 float maxd = far_d_nodes*BS;
3347 v3f p_f = intToFloat(p, BS);
3349 for(core::map<u16, RemoteClient*>::Iterator
3350 i = m_clients.getIterator();
3351 i.atEnd() == false; i++)
3353 // Get client and check that it is valid
3354 RemoteClient *client = i.getNode()->getValue();
3355 assert(client->peer_id == i.getNode()->getKey());
3356 if(client->serialization_version == SER_FMT_VER_INVALID)
3359 // Don't send if it's the same one
3360 if(client->peer_id == ignore_id)
3366 Player *player = m_env.getPlayer(client->peer_id);
3369 // If player is far away, only set modified blocks not sent
3370 v3f player_pos = player->getPosition();
3371 if(player_pos.getDistanceFrom(p_f) > maxd)
3373 far_players->push_back(client->peer_id);
3380 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3381 SharedBuffer<u8> reply(replysize);
3382 writeU16(&reply[0], TOCLIENT_ADDNODE);
3383 writeS16(&reply[2], p.X);
3384 writeS16(&reply[4], p.Y);
3385 writeS16(&reply[6], p.Z);
3386 n.serialize(&reply[8], client->serialization_version);
3389 m_con.Send(client->peer_id, 0, reply, true);
3393 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3395 DSTACK(__FUNCTION_NAME);
3397 Create a packet with the block in the right format
3400 std::ostringstream os(std::ios_base::binary);
3401 block->serialize(os, ver);
3402 std::string s = os.str();
3403 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3405 u32 replysize = 8 + blockdata.getSize();
3406 SharedBuffer<u8> reply(replysize);
3407 v3s16 p = block->getPos();
3408 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3409 writeS16(&reply[2], p.X);
3410 writeS16(&reply[4], p.Y);
3411 writeS16(&reply[6], p.Z);
3412 memcpy(&reply[8], *blockdata, blockdata.getSize());
3414 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3415 <<": \tpacket size: "<<replysize<<std::endl;*/
3420 m_con.Send(peer_id, 1, reply, true);
3423 void Server::SendBlocks(float dtime)
3425 DSTACK(__FUNCTION_NAME);
3427 JMutexAutoLock envlock(m_env_mutex);
3428 JMutexAutoLock conlock(m_con_mutex);
3430 //TimeTaker timer("Server::SendBlocks");
3432 core::array<PrioritySortedBlockTransfer> queue;
3434 s32 total_sending = 0;
3436 for(core::map<u16, RemoteClient*>::Iterator
3437 i = m_clients.getIterator();
3438 i.atEnd() == false; i++)
3440 RemoteClient *client = i.getNode()->getValue();
3441 assert(client->peer_id == i.getNode()->getKey());
3443 total_sending += client->SendingCount();
3445 if(client->serialization_version == SER_FMT_VER_INVALID)
3448 client->GetNextBlocks(this, dtime, queue);
3452 // Lowest priority number comes first.
3453 // Lowest is most important.
3456 for(u32 i=0; i<queue.size(); i++)
3458 //TODO: Calculate limit dynamically
3459 if(total_sending >= g_settings.getS32
3460 ("max_simultaneous_block_sends_server_total"))
3463 PrioritySortedBlockTransfer q = queue[i];
3465 MapBlock *block = NULL;
3468 block = m_env.getMap().getBlockNoCreate(q.pos);
3470 catch(InvalidPositionException &e)
3475 RemoteClient *client = getClient(q.peer_id);
3477 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3479 client->SentBlock(q.pos);
3489 void Server::UpdateCrafting(u16 peer_id)
3491 DSTACK(__FUNCTION_NAME);
3493 Player* player = m_env.getPlayer(peer_id);
3497 Calculate crafting stuff
3499 if(g_settings.getBool("creative_mode") == false)
3501 InventoryList *clist = player->inventory.getList("craft");
3502 InventoryList *rlist = player->inventory.getList("craftresult");
3504 if(rlist->getUsedSlots() == 0)
3505 player->craftresult_is_preview = true;
3507 if(rlist && player->craftresult_is_preview)
3509 rlist->clearItems();
3511 if(clist && rlist && player->craftresult_is_preview)
3513 InventoryItem *items[9];
3514 for(u16 i=0; i<9; i++)
3516 items[i] = clist->getItem(i);
3525 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3526 if(checkItemCombination(items, specs))
3528 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3537 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3538 if(checkItemCombination(items, specs))
3540 rlist->addItem(new CraftItem("Stick", 4));
3549 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3550 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3551 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3552 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3553 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3554 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3555 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3556 if(checkItemCombination(items, specs))
3558 //rlist->addItem(new MapBlockObjectItem("Sign"));
3559 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3568 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3569 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3570 if(checkItemCombination(items, specs))
3572 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3581 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3582 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3583 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3584 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3585 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3586 if(checkItemCombination(items, specs))
3588 rlist->addItem(new ToolItem("WPick", 0));
3597 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3598 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3599 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3600 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3601 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3602 if(checkItemCombination(items, specs))
3604 rlist->addItem(new ToolItem("STPick", 0));
3613 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3614 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3615 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3616 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3617 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3618 if(checkItemCombination(items, specs))
3620 rlist->addItem(new ToolItem("SteelPick", 0));
3629 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3630 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3631 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3632 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3633 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3634 if(checkItemCombination(items, specs))
3636 rlist->addItem(new ToolItem("MesePick", 0));
3645 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3646 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3647 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3648 if(checkItemCombination(items, specs))
3650 rlist->addItem(new ToolItem("WShovel", 0));
3659 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3660 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3661 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3662 if(checkItemCombination(items, specs))
3664 rlist->addItem(new ToolItem("STShovel", 0));
3673 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3674 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3675 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3676 if(checkItemCombination(items, specs))
3678 rlist->addItem(new ToolItem("SteelShovel", 0));
3687 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3688 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3689 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3690 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3691 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3692 if(checkItemCombination(items, specs))
3694 rlist->addItem(new ToolItem("WAxe", 0));
3703 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3704 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3705 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3706 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3707 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3708 if(checkItemCombination(items, specs))
3710 rlist->addItem(new ToolItem("STAxe", 0));
3719 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3720 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3721 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3722 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3723 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3724 if(checkItemCombination(items, specs))
3726 rlist->addItem(new ToolItem("SteelAxe", 0));
3735 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3736 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3737 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3738 if(checkItemCombination(items, specs))
3740 rlist->addItem(new ToolItem("WSword", 0));
3749 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3750 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3751 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3752 if(checkItemCombination(items, specs))
3754 rlist->addItem(new ToolItem("STSword", 0));
3763 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3764 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3765 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3766 if(checkItemCombination(items, specs))
3768 rlist->addItem(new ToolItem("SteelSword", 0));
3777 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3778 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3779 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3780 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3781 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3782 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3783 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3784 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3785 if(checkItemCombination(items, specs))
3787 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3796 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3797 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3798 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3799 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3800 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3801 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3802 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3803 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3804 if(checkItemCombination(items, specs))
3806 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3815 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3816 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3817 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3818 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3819 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3820 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3821 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3822 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3823 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3824 if(checkItemCombination(items, specs))
3826 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3832 } // if creative_mode == false
3835 RemoteClient* Server::getClient(u16 peer_id)
3837 DSTACK(__FUNCTION_NAME);
3838 //JMutexAutoLock lock(m_con_mutex);
3839 core::map<u16, RemoteClient*>::Node *n;
3840 n = m_clients.find(peer_id);
3841 // A client should exist for all peers
3843 return n->getValue();
3846 std::wstring Server::getStatusString()
3848 std::wostringstream os(std::ios_base::binary);
3851 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3853 os<<L", uptime="<<m_uptime.get();
3854 // Information about clients
3856 for(core::map<u16, RemoteClient*>::Iterator
3857 i = m_clients.getIterator();
3858 i.atEnd() == false; i++)
3860 // Get client and check that it is valid
3861 RemoteClient *client = i.getNode()->getValue();
3862 assert(client->peer_id == i.getNode()->getKey());
3863 if(client->serialization_version == SER_FMT_VER_INVALID)
3866 Player *player = m_env.getPlayer(client->peer_id);
3867 // Get name of player
3868 std::wstring name = L"unknown";
3870 name = narrow_to_wide(player->getName());
3871 // Add name to information string
3875 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3876 os<<" WARNING: Map saving is disabled."<<std::endl;
3881 void setCreativeInventory(Player *player)
3883 player->resetInventory();
3885 // Give some good tools
3887 InventoryItem *item = new ToolItem("MesePick", 0);
3888 void* r = player->inventory.addItem("main", item);
3892 InventoryItem *item = new ToolItem("SteelPick", 0);
3893 void* r = player->inventory.addItem("main", item);
3897 InventoryItem *item = new ToolItem("SteelAxe", 0);
3898 void* r = player->inventory.addItem("main", item);
3902 InventoryItem *item = new ToolItem("SteelShovel", 0);
3903 void* r = player->inventory.addItem("main", item);
3911 // CONTENT_IGNORE-terminated list
3912 u8 material_items[] = {
3921 CONTENT_WATERSOURCE,
3929 u8 *mip = material_items;
3930 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3932 if(*mip == CONTENT_IGNORE)
3935 InventoryItem *item = new MaterialItem(*mip, 1);
3936 player->inventory.addItem("main", item);
3942 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3945 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3946 player->inventory.addItem("main", item);
3949 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3951 // Skip some materials
3952 if(i == CONTENT_WATER || i == CONTENT_TORCH
3953 || i == CONTENT_COALSTONE)
3956 InventoryItem *item = new MaterialItem(i, 1);
3957 player->inventory.addItem("main", item);
3963 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3964 void* r = player->inventory.addItem("main", item);
3969 v3f findSpawnPos(ServerMap &map)
3972 s16 groundheight = 0;
3974 // Try to find a good place a few times
3975 for(s32 i=0; i<1000; i++)
3978 // We're going to try to throw the player to this position
3979 nodepos = v2s16(-range + (myrand()%(range*2)),
3980 -range + (myrand()%(range*2)));
3981 v2s16 sectorpos = getNodeSectorPos(nodepos);
3982 // Get sector (NOTE: Don't get because it's slow)
3983 //m_env.getMap().emergeSector(sectorpos);
3984 // Get ground height at point (fallbacks to heightmap function)
3985 groundheight = map.findGroundLevel(nodepos);
3986 // Don't go underwater
3987 if(groundheight < WATER_LEVEL)
3989 //dstream<<"-> Underwater"<<std::endl;
3992 // Don't go to high places
3993 if(groundheight > WATER_LEVEL + 4)
3995 //dstream<<"-> Underwater"<<std::endl;
3999 // Found a good place
4000 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4004 // If no suitable place was not found, go above water at least.
4005 if(groundheight < WATER_LEVEL)
4006 groundheight = WATER_LEVEL;
4008 return intToFloat(v3s16(
4015 Player *Server::emergePlayer(const char *name, const char *password,
4019 Try to get an existing player
4021 Player *player = m_env.getPlayer(name);
4024 // If player is already connected, cancel
4025 if(player->peer_id != 0)
4027 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4032 player->peer_id = peer_id;
4034 // Reset inventory to creative if in creative mode
4035 if(g_settings.getBool("creative_mode"))
4037 setCreativeInventory(player);
4044 If player with the wanted peer_id already exists, cancel.
4046 if(m_env.getPlayer(peer_id) != NULL)
4048 dstream<<"emergePlayer(): Player with wrong name but same"
4049 " peer_id already exists"<<std::endl;
4057 player = new ServerRemotePlayer();
4058 //player->peer_id = c.peer_id;
4059 //player->peer_id = PEER_ID_INEXISTENT;
4060 player->peer_id = peer_id;
4061 player->updateName(name);
4067 dstream<<"Server: Finding spawn place for player \""
4068 <<player->getName()<<"\""<<std::endl;
4070 v3f pos = findSpawnPos(m_env.getServerMap());
4072 player->setPosition(pos);
4075 Add player to environment
4078 m_env.addPlayer(player);
4081 Add stuff to inventory
4084 if(g_settings.getBool("creative_mode"))
4086 setCreativeInventory(player);
4088 else if(g_settings.getBool("give_initial_stuff"))
4091 InventoryItem *item = new ToolItem("SteelPick", 0);
4092 void* r = player->inventory.addItem("main", item);
4096 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4097 void* r = player->inventory.addItem("main", item);
4101 InventoryItem *item = new ToolItem("SteelAxe", 0);
4102 void* r = player->inventory.addItem("main", item);
4106 InventoryItem *item = new ToolItem("SteelShovel", 0);
4107 void* r = player->inventory.addItem("main", item);
4111 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4112 void* r = player->inventory.addItem("main", item);
4116 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4117 void* r = player->inventory.addItem("main", item);
4121 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4122 void* r = player->inventory.addItem("main", item);
4126 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4127 void* r = player->inventory.addItem("main", item);
4131 InventoryItem *item = new CraftItem("Stick", 4);
4132 void* r = player->inventory.addItem("main", item);
4136 InventoryItem *item = new ToolItem("WPick", 32000);
4137 void* r = player->inventory.addItem("main", item);
4141 InventoryItem *item = new ToolItem("STPick", 32000);
4142 void* r = player->inventory.addItem("main", item);
4146 for(u16 i=0; i<4; i++)
4148 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4149 bool r = player->inventory.addItem("main", item);
4152 /*// Give some other stuff
4154 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4155 bool r = player->inventory.addItem("main", item);
4162 } // create new player
4165 void Server::handlePeerChange(PeerChange &c)
4167 JMutexAutoLock envlock(m_env_mutex);
4168 JMutexAutoLock conlock(m_con_mutex);
4170 if(c.type == PEER_ADDED)
4177 core::map<u16, RemoteClient*>::Node *n;
4178 n = m_clients.find(c.peer_id);
4179 // The client shouldn't already exist
4183 RemoteClient *client = new RemoteClient();
4184 client->peer_id = c.peer_id;
4185 m_clients.insert(client->peer_id, client);
4188 else if(c.type == PEER_REMOVED)
4195 core::map<u16, RemoteClient*>::Node *n;
4196 n = m_clients.find(c.peer_id);
4197 // The client should exist
4200 // Collect information about leaving in chat
4201 std::wstring message;
4203 std::wstring name = L"unknown";
4204 Player *player = m_env.getPlayer(c.peer_id);
4206 name = narrow_to_wide(player->getName());
4210 message += L" left game";
4212 message += L" (timed out)";
4217 m_env.removePlayer(c.peer_id);
4220 // Set player client disconnected
4222 Player *player = m_env.getPlayer(c.peer_id);
4224 player->peer_id = 0;
4228 delete m_clients[c.peer_id];
4229 m_clients.remove(c.peer_id);
4231 // Send player info to all remaining clients
4234 // Send leave chat message to all remaining clients
4235 BroadcastChatMessage(message);
4244 void Server::handlePeerChanges()
4246 while(m_peer_change_queue.size() > 0)
4248 PeerChange c = m_peer_change_queue.pop_front();
4250 dout_server<<"Server: Handling peer change: "
4251 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4254 handlePeerChange(c);
4258 void dedicated_server_loop(Server &server, bool &kill)
4260 DSTACK(__FUNCTION_NAME);
4262 std::cout<<DTIME<<std::endl;
4263 std::cout<<"========================"<<std::endl;
4264 std::cout<<"Running dedicated server"<<std::endl;
4265 std::cout<<"========================"<<std::endl;
4266 std::cout<<std::endl;
4270 // This is kind of a hack but can be done like this
4271 // because server.step() is very light
4275 if(server.getShutdownRequested() || kill)
4277 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4281 static int counter = 0;
4287 core::list<PlayerInfo> list = server.getPlayerInfo();
4288 core::list<PlayerInfo>::Iterator i;
4289 static u32 sum_old = 0;
4290 u32 sum = PIChecksum(list);
4293 std::cout<<DTIME<<"Player info:"<<std::endl;
4294 for(i=list.begin(); i!=list.end(); i++)
4296 i->PrintLine(&std::cout);