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"
32 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
34 void * ServerThread::Thread()
38 DSTACK(__FUNCTION_NAME);
40 BEGIN_DEBUG_EXCEPTION_HANDLER
45 //TimeTaker timer("AsyncRunStep() + Receive()");
48 //TimeTaker timer("AsyncRunStep()");
49 m_server->AsyncRunStep();
52 //dout_server<<"Running m_server->Receive()"<<std::endl;
55 catch(con::NoIncomingDataException &e)
58 catch(con::PeerNotFoundException &e)
60 dout_server<<"Server: PeerNotFoundException"<<std::endl;
64 END_DEBUG_EXCEPTION_HANDLER
69 void * EmergeThread::Thread()
73 DSTACK(__FUNCTION_NAME);
77 BEGIN_DEBUG_EXCEPTION_HANDLER
80 Get block info from queue, emerge them and send them
83 After queue is empty, exit.
87 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
91 SharedPtr<QueuedBlockEmerge> q(qptr);
97 Do not generate over-limit
99 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
100 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
101 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
107 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
109 //TimeTaker timer("block emerge");
112 Try to emerge it from somewhere.
114 If it is only wanted as optional, only loading from disk
119 Check if any peer wants it as non-optional. In that case it
122 Also decrement the emerge queue count in clients.
125 bool optional = true;
128 core::map<u16, u8>::Iterator i;
129 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
131 //u16 peer_id = i.getNode()->getKey();
134 u8 flags = i.getNode()->getValue();
135 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
141 /*dstream<<"EmergeThread: p="
142 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
143 <<"optional="<<optional<<std::endl;*/
145 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
147 core::map<v3s16, MapBlock*> changed_blocks;
148 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
150 MapBlock *block = NULL;
151 bool got_block = true;
152 core::map<v3s16, MapBlock*> modified_blocks;
154 bool only_from_disk = false;
157 only_from_disk = true;
159 v2s16 chunkpos = map.sector_to_chunk(p2d);
161 bool generate_chunk = false;
162 if(only_from_disk == false)
164 JMutexAutoLock envlock(m_server->m_env_mutex);
165 if(map.chunkNonVolatile(chunkpos) == false)
166 generate_chunk = true;
173 JMutexAutoLock envlock(m_server->m_env_mutex);
174 map.initChunkMake(data, chunkpos);
180 JMutexAutoLock envlock(m_server->m_env_mutex);
181 map.finishChunkMake(data, changed_blocks);
186 Fetch block from map or generate a single block
189 JMutexAutoLock envlock(m_server->m_env_mutex);
191 // Load sector if it isn't loaded
192 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
193 map.loadSectorFull(p2d);
195 block = map.getBlockNoCreateNoEx(p);
196 if(!block || block->isDummy())
204 // Get, load or create sector
205 ServerMapSector *sector =
206 (ServerMapSector*)map.createSector(p2d);
208 block = map.generateBlock(p, block, sector, changed_blocks,
209 lighting_invalidated_blocks);
216 if(block->getLightingExpired()){
217 lighting_invalidated_blocks[block->getPos()] = block;
221 // TODO: Some additional checking and lighting updating,
226 JMutexAutoLock envlock(m_server->m_env_mutex);
231 Collect a list of blocks that have been modified in
232 addition to the fetched one.
235 if(lighting_invalidated_blocks.size() > 0)
237 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
238 <<" blocks"<<std::endl;*/
240 // 50-100ms for single block generation
241 //TimeTaker timer("** EmergeThread updateLighting");
243 // Update lighting without locking the environment mutex,
244 // add modified blocks to changed blocks
245 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
248 // Add all from changed_blocks to modified_blocks
249 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
250 i.atEnd() == false; i++)
252 MapBlock *block = i.getNode()->getValue();
253 modified_blocks.insert(block->getPos(), block);
256 // If we got no block, there should be no invalidated blocks
259 assert(lighting_invalidated_blocks.size() == 0);
265 Set sent status of modified blocks on clients
268 // NOTE: Server's clients are also behind the connection mutex
269 JMutexAutoLock lock(m_server->m_con_mutex);
272 Add the originally fetched block to the modified list
276 modified_blocks.insert(p, block);
280 Set the modified blocks unsent for all the clients
283 for(core::map<u16, RemoteClient*>::Iterator
284 i = m_server->m_clients.getIterator();
285 i.atEnd() == false; i++)
287 RemoteClient *client = i.getNode()->getValue();
289 if(modified_blocks.size() > 0)
291 // Remove block from sent history
292 client->SetBlocksNotSent(modified_blocks);
298 END_DEBUG_EXCEPTION_HANDLER
303 void RemoteClient::GetNextBlocks(Server *server, float dtime,
304 core::array<PrioritySortedBlockTransfer> &dest)
306 DSTACK(__FUNCTION_NAME);
309 m_nearest_unsent_reset_timer += dtime;
311 // Won't send anything if already sending
312 if(m_blocks_sending.size() >= g_settings.getU16
313 ("max_simultaneous_block_sends_per_client"))
315 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
319 Player *player = server->m_env.getPlayer(peer_id);
321 assert(player != NULL);
323 v3f playerpos = player->getPosition();
324 v3f playerspeed = player->getSpeed();
326 v3s16 center_nodepos = floatToInt(playerpos, BS);
328 v3s16 center = getNodeBlockPos(center_nodepos);
330 // Camera position and direction
332 playerpos + v3f(0, BS+BS/2, 0);
333 v3f camera_dir = v3f(0,0,1);
334 camera_dir.rotateYZBy(player->getPitch());
335 camera_dir.rotateXZBy(player->getYaw());
338 Get the starting value of the block finder radius.
340 s16 last_nearest_unsent_d;
343 if(m_last_center != center)
345 m_nearest_unsent_d = 0;
346 m_last_center = center;
349 /*dstream<<"m_nearest_unsent_reset_timer="
350 <<m_nearest_unsent_reset_timer<<std::endl;*/
351 if(m_nearest_unsent_reset_timer > 5.0)
353 m_nearest_unsent_reset_timer = 0;
354 m_nearest_unsent_d = 0;
355 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
358 last_nearest_unsent_d = m_nearest_unsent_d;
360 d_start = m_nearest_unsent_d;
362 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
363 ("max_simultaneous_block_sends_per_client");
364 u16 maximum_simultaneous_block_sends =
365 maximum_simultaneous_block_sends_setting;
368 Check the time from last addNode/removeNode.
370 Decrease send rate if player is building stuff.
372 m_time_from_building += dtime;
373 if(m_time_from_building < g_settings.getFloat(
374 "full_block_send_enable_min_time_from_building"))
376 maximum_simultaneous_block_sends
377 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
380 u32 num_blocks_selected = m_blocks_sending.size();
383 next time d will be continued from the d from which the nearest
384 unsent block was found this time.
386 This is because not necessarily any of the blocks found this
387 time are actually sent.
389 s32 new_nearest_unsent_d = -1;
391 s16 d_max = g_settings.getS16("max_block_send_distance");
392 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
394 //dstream<<"Starting from "<<d_start<<std::endl;
396 for(s16 d = d_start; d <= d_max; d++)
398 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
401 If m_nearest_unsent_d was changed by the EmergeThread
402 (it can change it to 0 through SetBlockNotSent),
404 Else update m_nearest_unsent_d
406 if(m_nearest_unsent_d != last_nearest_unsent_d)
408 d = m_nearest_unsent_d;
409 last_nearest_unsent_d = m_nearest_unsent_d;
413 Get the border/face dot coordinates of a "d-radiused"
416 core::list<v3s16> list;
417 getFacePositions(list, d);
419 core::list<v3s16>::Iterator li;
420 for(li=list.begin(); li!=list.end(); li++)
422 v3s16 p = *li + center;
426 - Don't allow too many simultaneous transfers
427 - EXCEPT when the blocks are very close
429 Also, don't send blocks that are already flying.
432 u16 maximum_simultaneous_block_sends_now =
433 maximum_simultaneous_block_sends;
435 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
437 maximum_simultaneous_block_sends_now =
438 maximum_simultaneous_block_sends_setting;
441 // Limit is dynamically lowered when building
442 if(num_blocks_selected
443 >= maximum_simultaneous_block_sends_now)
445 /*dstream<<"Not sending more blocks. Queue full. "
446 <<m_blocks_sending.size()
451 if(m_blocks_sending.find(p) != NULL)
457 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
458 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
459 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
460 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
461 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
462 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
465 // If this is true, inexistent block will be made from scratch
466 bool generate = d <= d_max_gen;
469 /*// Limit the generating area vertically to 2/3
470 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
473 // Limit the send area vertically to 2/3
474 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
480 If block is far away, don't generate it unless it is
483 NOTE: We can't know the ground level this way with the
489 MapSector *sector = NULL;
492 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
494 catch(InvalidPositionException &e)
500 // Get center ground height in nodes
501 f32 gh = sector->getGroundHeight(
502 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
503 // Block center y in nodes
504 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
505 // If differs a lot, don't generate
506 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
513 Don't generate or send if not in sight
516 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
522 Don't send already sent blocks
525 if(m_blocks_sent.find(p) != NULL)
530 Check if map has this block
532 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
534 bool surely_not_found_on_disk = false;
535 bool block_is_invalid = false;
540 surely_not_found_on_disk = true;
543 if(block->isValid() == false)
545 block_is_invalid = true;
548 /*if(block->isFullyGenerated() == false)
550 block_is_invalid = true;
554 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
555 v2s16 chunkpos = map->sector_to_chunk(p2d);
556 if(map->chunkNonVolatile(chunkpos) == false)
557 block_is_invalid = true;
561 If block has been marked to not exist on disk (dummy)
562 and generating new ones is not wanted, skip block.
564 if(generate == false && surely_not_found_on_disk == true)
571 Record the lowest d from which a a block has been
572 found being not sent and possibly to exist
574 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
576 new_nearest_unsent_d = d;
580 Add inexistent block to emerge queue.
582 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
584 //TODO: Get value from somewhere
585 // Allow only one block in emerge queue
586 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
587 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
589 //dstream<<"Adding block to emerge queue"<<std::endl;
591 // Add it to the emerge queue and trigger the thread
594 if(generate == false)
595 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
597 server->m_emerge_queue.addBlock(peer_id, p, flags);
598 server->m_emergethread.trigger();
606 Add block to send queue
609 PrioritySortedBlockTransfer q((float)d, p, peer_id);
613 num_blocks_selected += 1;
618 if(new_nearest_unsent_d != -1)
620 m_nearest_unsent_d = new_nearest_unsent_d;
624 void RemoteClient::SendObjectData(
627 core::map<v3s16, bool> &stepped_blocks
630 DSTACK(__FUNCTION_NAME);
632 // Can't send anything without knowing version
633 if(serialization_version == SER_FMT_VER_INVALID)
635 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
641 Send a TOCLIENT_OBJECTDATA packet.
645 u16 number of player positions
656 std::ostringstream os(std::ios_base::binary);
660 writeU16(buf, TOCLIENT_OBJECTDATA);
661 os.write((char*)buf, 2);
664 Get and write player data
667 // Get connected players
668 core::list<Player*> players = server->m_env.getPlayers(true);
670 // Write player count
671 u16 playercount = players.size();
672 writeU16(buf, playercount);
673 os.write((char*)buf, 2);
675 core::list<Player*>::Iterator i;
676 for(i = players.begin();
677 i != players.end(); i++)
681 v3f pf = player->getPosition();
682 v3f sf = player->getSpeed();
684 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
685 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
686 s32 pitch_i (player->getPitch() * 100);
687 s32 yaw_i (player->getYaw() * 100);
689 writeU16(buf, player->peer_id);
690 os.write((char*)buf, 2);
691 writeV3S32(buf, position_i);
692 os.write((char*)buf, 12);
693 writeV3S32(buf, speed_i);
694 os.write((char*)buf, 12);
695 writeS32(buf, pitch_i);
696 os.write((char*)buf, 4);
697 writeS32(buf, yaw_i);
698 os.write((char*)buf, 4);
702 Get and write object data
708 For making players to be able to build to their nearby
709 environment (building is not possible on blocks that are not
712 - Add blocks to emerge queue if they are not found
714 SUGGESTION: These could be ignored from the backside of the player
717 Player *player = server->m_env.getPlayer(peer_id);
721 v3f playerpos = player->getPosition();
722 v3f playerspeed = player->getSpeed();
724 v3s16 center_nodepos = floatToInt(playerpos, BS);
725 v3s16 center = getNodeBlockPos(center_nodepos);
727 s16 d_max = g_settings.getS16("active_object_range");
729 // Number of blocks whose objects were written to bos
732 std::ostringstream bos(std::ios_base::binary);
734 for(s16 d = 0; d <= d_max; d++)
736 core::list<v3s16> list;
737 getFacePositions(list, d);
739 core::list<v3s16>::Iterator li;
740 for(li=list.begin(); li!=list.end(); li++)
742 v3s16 p = *li + center;
745 Ignore blocks that haven't been sent to the client
748 if(m_blocks_sent.find(p) == NULL)
752 // Try stepping block and add it to a send queue
757 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
760 Step block if not in stepped_blocks and add to stepped_blocks.
762 if(stepped_blocks.find(p) == NULL)
764 block->stepObjects(dtime, true, server->getDayNightRatio());
765 stepped_blocks.insert(p, true);
766 block->setChangedFlag();
769 // Skip block if there are no objects
770 if(block->getObjectCount() == 0)
779 bos.write((char*)buf, 6);
782 block->serializeObjects(bos, serialization_version);
787 Stop collecting objects if data is already too big
789 // Sum of player and object data sizes
790 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
791 // break out if data too big
792 if(sum > MAX_OBJECTDATA_SIZE)
794 goto skip_subsequent;
798 catch(InvalidPositionException &e)
801 // Add it to the emerge queue and trigger the thread.
802 // Fetch the block only if it is on disk.
804 // Grab and increment counter
805 /*SharedPtr<JMutexAutoLock> lock
806 (m_num_blocks_in_emerge_queue.getLock());
807 m_num_blocks_in_emerge_queue.m_value++;*/
809 // Add to queue as an anonymous fetch from disk
810 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
811 server->m_emerge_queue.addBlock(0, p, flags);
812 server->m_emergethread.trigger();
820 writeU16(buf, blockcount);
821 os.write((char*)buf, 2);
823 // Write block objects
830 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
833 std::string s = os.str();
834 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
835 // Send as unreliable
836 server->m_con.Send(peer_id, 0, data, false);
839 void RemoteClient::GotBlock(v3s16 p)
841 if(m_blocks_sending.find(p) != NULL)
842 m_blocks_sending.remove(p);
845 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
846 " m_blocks_sending"<<std::endl;*/
847 m_excess_gotblocks++;
849 m_blocks_sent.insert(p, true);
852 void RemoteClient::SentBlock(v3s16 p)
854 if(m_blocks_sending.find(p) == NULL)
855 m_blocks_sending.insert(p, 0.0);
857 dstream<<"RemoteClient::SentBlock(): Sent block"
858 " already in m_blocks_sending"<<std::endl;
861 void RemoteClient::SetBlockNotSent(v3s16 p)
863 m_nearest_unsent_d = 0;
865 if(m_blocks_sending.find(p) != NULL)
866 m_blocks_sending.remove(p);
867 if(m_blocks_sent.find(p) != NULL)
868 m_blocks_sent.remove(p);
871 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
873 m_nearest_unsent_d = 0;
875 for(core::map<v3s16, MapBlock*>::Iterator
876 i = blocks.getIterator();
877 i.atEnd()==false; i++)
879 v3s16 p = i.getNode()->getKey();
881 if(m_blocks_sending.find(p) != NULL)
882 m_blocks_sending.remove(p);
883 if(m_blocks_sent.find(p) != NULL)
884 m_blocks_sent.remove(p);
892 PlayerInfo::PlayerInfo()
897 void PlayerInfo::PrintLine(std::ostream *s)
900 (*s)<<"\""<<name<<"\" ("
901 <<(position.X/10)<<","<<(position.Y/10)
902 <<","<<(position.Z/10)<<") ";
904 (*s)<<" avg_rtt="<<avg_rtt;
908 u32 PIChecksum(core::list<PlayerInfo> &l)
910 core::list<PlayerInfo>::Iterator i;
913 for(i=l.begin(); i!=l.end(); i++)
915 checksum += a * (i->id+1);
916 checksum ^= 0x435aafcd;
927 std::string mapsavedir
929 m_env(new ServerMap(mapsavedir), this),
930 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
932 m_emergethread(this),
935 m_time_of_day_send_timer(0),
937 m_mapsavedir(mapsavedir),
938 m_shutdown_requested(false),
939 m_ignore_map_edit_events(false),
940 m_ignore_map_edit_events_peer_id(0)
942 m_liquid_transform_timer = 0.0;
943 m_print_info_timer = 0.0;
944 m_objectdata_timer = 0.0;
945 m_emergethread_trigger_timer = 0.0;
946 m_savemap_timer = 0.0;
950 m_step_dtime_mutex.Init();
953 m_env.getMap().addEventReceiver(this);
956 m_env.deSerializePlayers(m_mapsavedir);
962 Send shutdown message
965 JMutexAutoLock conlock(m_con_mutex);
967 std::wstring line = L"*** Server shutting down";
970 Send the message to clients
972 for(core::map<u16, RemoteClient*>::Iterator
973 i = m_clients.getIterator();
974 i.atEnd() == false; i++)
976 // Get client and check that it is valid
977 RemoteClient *client = i.getNode()->getValue();
978 assert(client->peer_id == i.getNode()->getKey());
979 if(client->serialization_version == SER_FMT_VER_INVALID)
982 SendChatMessage(client->peer_id, line);
989 m_env.serializePlayers(m_mapsavedir);
1000 JMutexAutoLock clientslock(m_con_mutex);
1002 for(core::map<u16, RemoteClient*>::Iterator
1003 i = m_clients.getIterator();
1004 i.atEnd() == false; i++)
1007 // NOTE: These are removed by env destructor
1009 u16 peer_id = i.getNode()->getKey();
1010 JMutexAutoLock envlock(m_env_mutex);
1011 m_env.removePlayer(peer_id);
1015 delete i.getNode()->getValue();
1020 void Server::start(unsigned short port)
1022 DSTACK(__FUNCTION_NAME);
1023 // Stop thread if already running
1026 // Initialize connection
1027 m_con.setTimeoutMs(30);
1031 m_thread.setRun(true);
1034 dout_server<<"Server: Started on port "<<port<<std::endl;
1039 DSTACK(__FUNCTION_NAME);
1041 // Stop threads (set run=false first so both start stopping)
1042 m_thread.setRun(false);
1043 m_emergethread.setRun(false);
1045 m_emergethread.stop();
1047 dout_server<<"Server: Threads stopped"<<std::endl;
1049 dout_server<<"Server: Saving players"<<std::endl;
1051 // FIXME: Apparently this does not do anything here
1052 //m_env.serializePlayers(m_mapsavedir);
1055 void Server::step(float dtime)
1057 DSTACK(__FUNCTION_NAME);
1062 JMutexAutoLock lock(m_step_dtime_mutex);
1063 m_step_dtime += dtime;
1067 void Server::AsyncRunStep()
1069 DSTACK(__FUNCTION_NAME);
1073 JMutexAutoLock lock1(m_step_dtime_mutex);
1074 dtime = m_step_dtime;
1077 // Send blocks to clients
1083 //dstream<<"Server steps "<<dtime<<std::endl;
1084 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1087 JMutexAutoLock lock1(m_step_dtime_mutex);
1088 m_step_dtime -= dtime;
1095 m_uptime.set(m_uptime.get() + dtime);
1099 Update m_time_of_day
1102 m_time_counter += dtime;
1103 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1104 u32 units = (u32)(m_time_counter*speed);
1105 m_time_counter -= (f32)units / speed;
1106 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1108 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1111 Send to clients at constant intervals
1114 m_time_of_day_send_timer -= dtime;
1115 if(m_time_of_day_send_timer < 0.0)
1117 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1119 //JMutexAutoLock envlock(m_env_mutex);
1120 JMutexAutoLock conlock(m_con_mutex);
1122 for(core::map<u16, RemoteClient*>::Iterator
1123 i = m_clients.getIterator();
1124 i.atEnd() == false; i++)
1126 RemoteClient *client = i.getNode()->getValue();
1127 //Player *player = m_env.getPlayer(client->peer_id);
1129 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1130 m_time_of_day.get());
1132 m_con.Send(client->peer_id, 0, data, true);
1138 // Process connection's timeouts
1139 JMutexAutoLock lock2(m_con_mutex);
1140 m_con.RunTimeouts(dtime);
1144 // This has to be called so that the client list gets synced
1145 // with the peer list of the connection
1146 handlePeerChanges();
1151 // This also runs Map's timers
1152 JMutexAutoLock lock(m_env_mutex);
1163 m_liquid_transform_timer += dtime;
1164 if(m_liquid_transform_timer >= 1.00)
1166 m_liquid_transform_timer -= 1.00;
1168 JMutexAutoLock lock(m_env_mutex);
1170 core::map<v3s16, MapBlock*> modified_blocks;
1171 m_env.getMap().transformLiquids(modified_blocks);
1176 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1177 ServerMap &map = ((ServerMap&)m_env.getMap());
1178 map.updateLighting(modified_blocks, lighting_modified_blocks);
1180 // Add blocks modified by lighting to modified_blocks
1181 for(core::map<v3s16, MapBlock*>::Iterator
1182 i = lighting_modified_blocks.getIterator();
1183 i.atEnd() == false; i++)
1185 MapBlock *block = i.getNode()->getValue();
1186 modified_blocks.insert(block->getPos(), block);
1190 Set the modified blocks unsent for all the clients
1193 JMutexAutoLock lock2(m_con_mutex);
1195 for(core::map<u16, RemoteClient*>::Iterator
1196 i = m_clients.getIterator();
1197 i.atEnd() == false; i++)
1199 RemoteClient *client = i.getNode()->getValue();
1201 if(modified_blocks.size() > 0)
1203 // Remove block from sent history
1204 client->SetBlocksNotSent(modified_blocks);
1209 // Periodically print some info
1211 float &counter = m_print_info_timer;
1217 JMutexAutoLock lock2(m_con_mutex);
1219 for(core::map<u16, RemoteClient*>::Iterator
1220 i = m_clients.getIterator();
1221 i.atEnd() == false; i++)
1223 //u16 peer_id = i.getNode()->getKey();
1224 RemoteClient *client = i.getNode()->getValue();
1225 Player *player = m_env.getPlayer(client->peer_id);
1226 std::cout<<player->getName()<<"\t";
1227 client->PrintInfo(std::cout);
1232 //if(g_settings.getBool("enable_experimental"))
1236 Check added and deleted active objects
1239 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1241 JMutexAutoLock envlock(m_env_mutex);
1242 JMutexAutoLock conlock(m_con_mutex);
1244 // Radius inside which objects are active
1247 for(core::map<u16, RemoteClient*>::Iterator
1248 i = m_clients.getIterator();
1249 i.atEnd() == false; i++)
1251 RemoteClient *client = i.getNode()->getValue();
1252 Player *player = m_env.getPlayer(client->peer_id);
1255 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1256 <<" has no associated player"<<std::endl;
1259 v3s16 pos = floatToInt(player->getPosition(), BS);
1261 core::map<u16, bool> removed_objects;
1262 core::map<u16, bool> added_objects;
1263 m_env.getRemovedActiveObjects(pos, radius,
1264 client->m_known_objects, removed_objects);
1265 m_env.getAddedActiveObjects(pos, radius,
1266 client->m_known_objects, added_objects);
1268 // Ignore if nothing happened
1269 if(removed_objects.size() == 0 && added_objects.size() == 0)
1271 //dstream<<"INFO: active objects: none changed"<<std::endl;
1275 std::string data_buffer;
1279 // Handle removed objects
1280 writeU16((u8*)buf, removed_objects.size());
1281 data_buffer.append(buf, 2);
1282 for(core::map<u16, bool>::Iterator
1283 i = removed_objects.getIterator();
1284 i.atEnd()==false; i++)
1287 u16 id = i.getNode()->getKey();
1288 ServerActiveObject* obj = m_env.getActiveObject(id);
1290 // Add to data buffer for sending
1291 writeU16((u8*)buf, i.getNode()->getKey());
1292 data_buffer.append(buf, 2);
1294 // Remove from known objects
1295 client->m_known_objects.remove(i.getNode()->getKey());
1297 if(obj && obj->m_known_by_count > 0)
1298 obj->m_known_by_count--;
1301 // Handle added objects
1302 writeU16((u8*)buf, added_objects.size());
1303 data_buffer.append(buf, 2);
1304 for(core::map<u16, bool>::Iterator
1305 i = added_objects.getIterator();
1306 i.atEnd()==false; i++)
1309 u16 id = i.getNode()->getKey();
1310 ServerActiveObject* obj = m_env.getActiveObject(id);
1313 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1315 dstream<<"WARNING: "<<__FUNCTION_NAME
1316 <<": NULL object"<<std::endl;
1318 type = obj->getType();
1320 // Add to data buffer for sending
1321 writeU16((u8*)buf, id);
1322 data_buffer.append(buf, 2);
1323 writeU8((u8*)buf, type);
1324 data_buffer.append(buf, 1);
1326 data_buffer.append(serializeLongString(
1327 obj->getClientInitializationData()));
1329 // Add to known objects
1330 client->m_known_objects.insert(i.getNode()->getKey(), false);
1333 obj->m_known_by_count++;
1337 SharedBuffer<u8> reply(2 + data_buffer.size());
1338 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1339 memcpy((char*)&reply[2], data_buffer.c_str(),
1340 data_buffer.size());
1342 m_con.Send(client->peer_id, 0, reply, true);
1344 dstream<<"INFO: Server: Sent object remove/add: "
1345 <<removed_objects.size()<<" removed, "
1346 <<added_objects.size()<<" added, "
1347 <<"packet size is "<<reply.getSize()<<std::endl;
1352 Collect a list of all the objects known by the clients
1353 and report it back to the environment.
1356 core::map<u16, bool> all_known_objects;
1358 for(core::map<u16, RemoteClient*>::Iterator
1359 i = m_clients.getIterator();
1360 i.atEnd() == false; i++)
1362 RemoteClient *client = i.getNode()->getValue();
1363 // Go through all known objects of client
1364 for(core::map<u16, bool>::Iterator
1365 i = client->m_known_objects.getIterator();
1366 i.atEnd()==false; i++)
1368 u16 id = i.getNode()->getKey();
1369 all_known_objects[id] = true;
1373 m_env.setKnownActiveObjects(whatever);
1379 Send object messages
1382 JMutexAutoLock envlock(m_env_mutex);
1383 JMutexAutoLock conlock(m_con_mutex);
1386 // Value = data sent by object
1387 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1389 // Get active object messages from environment
1392 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1396 core::list<ActiveObjectMessage>* message_list = NULL;
1397 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1398 n = buffered_messages.find(aom.id);
1401 message_list = new core::list<ActiveObjectMessage>;
1402 buffered_messages.insert(aom.id, message_list);
1406 message_list = n->getValue();
1408 message_list->push_back(aom);
1411 // Route data to every client
1412 for(core::map<u16, RemoteClient*>::Iterator
1413 i = m_clients.getIterator();
1414 i.atEnd()==false; i++)
1416 RemoteClient *client = i.getNode()->getValue();
1417 std::string reliable_data;
1418 std::string unreliable_data;
1419 // Go through all objects in message buffer
1420 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1421 j = buffered_messages.getIterator();
1422 j.atEnd()==false; j++)
1424 // If object is not known by client, skip it
1425 u16 id = j.getNode()->getKey();
1426 if(client->m_known_objects.find(id) == NULL)
1428 // Get message list of object
1429 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1430 // Go through every message
1431 for(core::list<ActiveObjectMessage>::Iterator
1432 k = list->begin(); k != list->end(); k++)
1434 // Compose the full new data with header
1435 ActiveObjectMessage aom = *k;
1436 std::string new_data;
1439 writeU16((u8*)&buf[0], aom.id);
1440 new_data.append(buf, 2);
1442 new_data += serializeString(aom.datastring);
1443 // Add data to buffer
1445 reliable_data += new_data;
1447 unreliable_data += new_data;
1451 reliable_data and unreliable_data are now ready.
1454 if(reliable_data.size() > 0)
1456 SharedBuffer<u8> reply(2 + reliable_data.size());
1457 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1458 memcpy((char*)&reply[2], reliable_data.c_str(),
1459 reliable_data.size());
1461 m_con.Send(client->peer_id, 0, reply, true);
1463 if(unreliable_data.size() > 0)
1465 SharedBuffer<u8> reply(2 + unreliable_data.size());
1466 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1467 memcpy((char*)&reply[2], unreliable_data.c_str(),
1468 unreliable_data.size());
1469 // Send as unreliable
1470 m_con.Send(client->peer_id, 0, reply, false);
1473 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1475 dstream<<"INFO: Server: Size of object message data: "
1476 <<"reliable: "<<reliable_data.size()
1477 <<", unreliable: "<<unreliable_data.size()
1482 // Clear buffered_messages
1483 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1484 i = buffered_messages.getIterator();
1485 i.atEnd()==false; i++)
1487 delete i.getNode()->getValue();
1491 } // enable_experimental
1494 Send queued-for-sending map edit events.
1497 while(m_unsent_map_edit_queue.size() != 0)
1499 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1501 if(event->type == MEET_ADDNODE)
1503 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1504 sendAddNode(event->p, event->n, event->already_known_by_peer);
1506 else if(event->type == MEET_REMOVENODE)
1508 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1509 sendRemoveNode(event->p, event->already_known_by_peer);
1511 else if(event->type == MEET_OTHER)
1513 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1518 dstream<<"WARNING: Server: Unknown MapEditEvent "
1519 <<((u32)event->type)<<std::endl;
1527 Send object positions
1528 TODO: Get rid of MapBlockObjects
1531 float &counter = m_objectdata_timer;
1533 if(counter >= g_settings.getFloat("objectdata_interval"))
1535 JMutexAutoLock lock1(m_env_mutex);
1536 JMutexAutoLock lock2(m_con_mutex);
1537 SendObjectData(counter);
1547 JMutexAutoLock envlock(m_env_mutex);
1548 JMutexAutoLock conlock(m_con_mutex);
1550 core::map<v3s16, MapBlock*> changed_blocks;
1551 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1553 for(core::map<v3s16, MapBlock*>::Iterator
1554 i = changed_blocks.getIterator();
1555 i.atEnd() == false; i++)
1557 MapBlock *block = i.getNode()->getValue();
1559 for(core::map<u16, RemoteClient*>::Iterator
1560 i = m_clients.getIterator();
1561 i.atEnd()==false; i++)
1563 RemoteClient *client = i.getNode()->getValue();
1564 client->SetBlockNotSent(block->getPos());
1570 Trigger emergethread (it somehow gets to a non-triggered but
1571 bysy state sometimes)
1574 float &counter = m_emergethread_trigger_timer;
1580 m_emergethread.trigger();
1586 float &counter = m_savemap_timer;
1588 if(counter >= g_settings.getFloat("server_map_save_interval"))
1592 JMutexAutoLock lock(m_env_mutex);
1594 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1596 // Save only changed parts
1597 m_env.getMap().save(true);
1599 // Delete unused sectors
1600 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1601 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1602 if(deleted_count > 0)
1604 dout_server<<"Server: Unloaded "<<deleted_count
1605 <<" sectors from memory"<<std::endl;
1609 m_env.serializePlayers(m_mapsavedir);
1615 void Server::Receive()
1617 DSTACK(__FUNCTION_NAME);
1618 u32 data_maxsize = 10000;
1619 Buffer<u8> data(data_maxsize);
1624 JMutexAutoLock conlock(m_con_mutex);
1625 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1628 // This has to be called so that the client list gets synced
1629 // with the peer list of the connection
1630 handlePeerChanges();
1632 ProcessData(*data, datasize, peer_id);
1634 catch(con::InvalidIncomingDataException &e)
1636 derr_server<<"Server::Receive(): "
1637 "InvalidIncomingDataException: what()="
1638 <<e.what()<<std::endl;
1640 catch(con::PeerNotFoundException &e)
1642 //NOTE: This is not needed anymore
1644 // The peer has been disconnected.
1645 // Find the associated player and remove it.
1647 /*JMutexAutoLock envlock(m_env_mutex);
1649 dout_server<<"ServerThread: peer_id="<<peer_id
1650 <<" has apparently closed connection. "
1651 <<"Removing player."<<std::endl;
1653 m_env.removePlayer(peer_id);*/
1657 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1659 DSTACK(__FUNCTION_NAME);
1660 // Environment is locked first.
1661 JMutexAutoLock envlock(m_env_mutex);
1662 JMutexAutoLock conlock(m_con_mutex);
1666 peer = m_con.GetPeer(peer_id);
1668 catch(con::PeerNotFoundException &e)
1670 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1671 <<peer_id<<" not found"<<std::endl;
1675 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1683 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1685 if(command == TOSERVER_INIT)
1687 // [0] u16 TOSERVER_INIT
1688 // [2] u8 SER_FMT_VER_HIGHEST
1689 // [3] u8[20] player_name
1694 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1695 <<peer->id<<std::endl;
1697 // First byte after command is maximum supported
1698 // serialization version
1699 u8 client_max = data[2];
1700 u8 our_max = SER_FMT_VER_HIGHEST;
1701 // Use the highest version supported by both
1702 u8 deployed = core::min_(client_max, our_max);
1703 // If it's lower than the lowest supported, give up.
1704 if(deployed < SER_FMT_VER_LOWEST)
1705 deployed = SER_FMT_VER_INVALID;
1707 //peer->serialization_version = deployed;
1708 getClient(peer->id)->pending_serialization_version = deployed;
1710 if(deployed == SER_FMT_VER_INVALID)
1712 derr_server<<DTIME<<"Server: Cannot negotiate "
1713 "serialization version with peer "
1714 <<peer_id<<std::endl;
1723 const u32 playername_size = 20;
1724 char playername[playername_size];
1725 for(u32 i=0; i<playername_size-1; i++)
1727 playername[i] = data[3+i];
1729 playername[playername_size-1] = 0;
1732 Player *player = emergePlayer(playername, "", peer_id);
1733 //Player *player = m_env.getPlayer(peer_id);
1736 // DEBUG: Test serialization
1737 std::ostringstream test_os;
1738 player->serialize(test_os);
1739 dstream<<"Player serialization test: \""<<test_os.str()
1741 std::istringstream test_is(test_os.str());
1742 player->deSerialize(test_is);
1745 // If failed, cancel
1748 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1749 <<": failed to emerge player"<<std::endl;
1754 // If a client is already connected to the player, cancel
1755 if(player->peer_id != 0)
1757 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1758 <<" tried to connect to "
1759 "an already connected player (peer_id="
1760 <<player->peer_id<<")"<<std::endl;
1763 // Set client of player
1764 player->peer_id = peer_id;
1767 // Check if player doesn't exist
1769 throw con::InvalidIncomingDataException
1770 ("Server::ProcessData(): INIT: Player doesn't exist");
1772 /*// update name if it was supplied
1773 if(datasize >= 20+3)
1776 player->updateName((const char*)&data[3]);
1779 // Now answer with a TOCLIENT_INIT
1781 SharedBuffer<u8> reply(2+1+6+8);
1782 writeU16(&reply[0], TOCLIENT_INIT);
1783 writeU8(&reply[2], deployed);
1784 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1785 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1788 m_con.Send(peer_id, 0, reply, true);
1792 if(command == TOSERVER_INIT2)
1794 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1795 <<peer->id<<std::endl;
1798 getClient(peer->id)->serialization_version
1799 = getClient(peer->id)->pending_serialization_version;
1802 Send some initialization data
1805 // Send player info to all players
1808 // Send inventory to player
1809 SendInventory(peer->id);
1813 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1814 m_time_of_day.get());
1815 m_con.Send(peer->id, 0, data, true);
1818 // Send information about server to player in chat
1819 SendChatMessage(peer_id, getStatusString());
1821 // Send information about joining in chat
1823 std::wstring name = L"unknown";
1824 Player *player = m_env.getPlayer(peer_id);
1826 name = narrow_to_wide(player->getName());
1828 std::wstring message;
1831 message += L" joined game";
1832 BroadcastChatMessage(message);
1838 if(peer_ser_ver == SER_FMT_VER_INVALID)
1840 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1841 " serialization format invalid or not initialized."
1842 " Skipping incoming command="<<command<<std::endl;
1846 Player *player = m_env.getPlayer(peer_id);
1849 derr_server<<"Server::ProcessData(): Cancelling: "
1850 "No player for peer_id="<<peer_id
1854 if(command == TOSERVER_PLAYERPOS)
1856 if(datasize < 2+12+12+4+4)
1860 v3s32 ps = readV3S32(&data[start+2]);
1861 v3s32 ss = readV3S32(&data[start+2+12]);
1862 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1863 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1864 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1865 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1866 pitch = wrapDegrees(pitch);
1867 yaw = wrapDegrees(yaw);
1868 player->setPosition(position);
1869 player->setSpeed(speed);
1870 player->setPitch(pitch);
1871 player->setYaw(yaw);
1873 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1874 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1875 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1877 else if(command == TOSERVER_GOTBLOCKS)
1890 u16 count = data[2];
1891 for(u16 i=0; i<count; i++)
1893 if((s16)datasize < 2+1+(i+1)*6)
1894 throw con::InvalidIncomingDataException
1895 ("GOTBLOCKS length is too short");
1896 v3s16 p = readV3S16(&data[2+1+i*6]);
1897 /*dstream<<"Server: GOTBLOCKS ("
1898 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1899 RemoteClient *client = getClient(peer_id);
1900 client->GotBlock(p);
1903 else if(command == TOSERVER_DELETEDBLOCKS)
1916 u16 count = data[2];
1917 for(u16 i=0; i<count; i++)
1919 if((s16)datasize < 2+1+(i+1)*6)
1920 throw con::InvalidIncomingDataException
1921 ("DELETEDBLOCKS length is too short");
1922 v3s16 p = readV3S16(&data[2+1+i*6]);
1923 /*dstream<<"Server: DELETEDBLOCKS ("
1924 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1925 RemoteClient *client = getClient(peer_id);
1926 client->SetBlockNotSent(p);
1929 else if(command == TOSERVER_CLICK_OBJECT)
1936 [2] u8 button (0=left, 1=right)
1941 u8 button = readU8(&data[2]);
1943 p.X = readS16(&data[3]);
1944 p.Y = readS16(&data[5]);
1945 p.Z = readS16(&data[7]);
1946 s16 id = readS16(&data[9]);
1947 //u16 item_i = readU16(&data[11]);
1949 MapBlock *block = NULL;
1952 block = m_env.getMap().getBlockNoCreate(p);
1954 catch(InvalidPositionException &e)
1956 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1960 MapBlockObject *obj = block->getObject(id);
1964 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1968 //TODO: Check that object is reasonably close
1973 InventoryList *ilist = player->inventory.getList("main");
1974 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1977 // Skip if inventory has no free space
1978 if(ilist->getUsedSlots() == ilist->getSize())
1980 dout_server<<"Player inventory has no free space"<<std::endl;
1985 Create the inventory item
1987 InventoryItem *item = NULL;
1988 // If it is an item-object, take the item from it
1989 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1991 item = ((ItemObject*)obj)->createInventoryItem();
1993 // Else create an item of the object
1996 item = new MapBlockObjectItem
1997 (obj->getInventoryString());
2000 // Add to inventory and send inventory
2001 ilist->addItem(item);
2002 SendInventory(player->peer_id);
2005 // Remove from block
2006 block->removeObject(id);
2009 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2017 [2] u8 button (0=left, 1=right)
2021 u8 button = readU8(&data[2]);
2022 u16 id = readS16(&data[3]);
2023 //u16 item_i = readU16(&data[11]);
2025 ServerActiveObject *obj = m_env.getActiveObject(id);
2029 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2034 //TODO: Check that object is reasonably close
2036 // Left click, pick object up (usually)
2039 InventoryList *ilist = player->inventory.getList("main");
2040 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2043 // Skip if inventory has no free space
2044 if(ilist->getUsedSlots() == ilist->getSize())
2046 dout_server<<"Player inventory has no free space"<<std::endl;
2050 // Skip if object has been removed
2055 Create the inventory item
2057 InventoryItem *item = obj->createPickedUpItem();
2061 // Add to inventory and send inventory
2062 ilist->addItem(item);
2063 SendInventory(player->peer_id);
2065 // Remove object from environment
2066 obj->m_removed = true;
2071 else if(command == TOSERVER_GROUND_ACTION)
2079 [3] v3s16 nodepos_undersurface
2080 [9] v3s16 nodepos_abovesurface
2085 2: stop digging (all parameters ignored)
2086 3: digging completed
2088 u8 action = readU8(&data[2]);
2090 p_under.X = readS16(&data[3]);
2091 p_under.Y = readS16(&data[5]);
2092 p_under.Z = readS16(&data[7]);
2094 p_over.X = readS16(&data[9]);
2095 p_over.Y = readS16(&data[11]);
2096 p_over.Z = readS16(&data[13]);
2097 u16 item_i = readU16(&data[15]);
2099 //TODO: Check that target is reasonably close
2107 NOTE: This can be used in the future to check if
2108 somebody is cheating, by checking the timing.
2115 else if(action == 2)
2118 RemoteClient *client = getClient(peer->id);
2119 JMutexAutoLock digmutex(client->m_dig_mutex);
2120 client->m_dig_tool_item = -1;
2125 3: Digging completed
2127 else if(action == 3)
2129 // Mandatory parameter; actually used for nothing
2130 core::map<v3s16, MapBlock*> modified_blocks;
2133 u8 mineral = MINERAL_NONE;
2135 bool cannot_remove_node = false;
2139 MapNode n = m_env.getMap().getNode(p_under);
2141 mineral = n.getMineral();
2142 // Get material at position
2144 // If not yet cancelled
2145 if(cannot_remove_node == false)
2147 // If it's not diggable, do nothing
2148 if(content_diggable(material) == false)
2150 derr_server<<"Server: Not finishing digging: "
2151 <<"Node not diggable"
2153 cannot_remove_node = true;
2156 // If not yet cancelled
2157 if(cannot_remove_node == false)
2159 // Get node metadata
2160 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2161 if(meta && meta->nodeRemovalDisabled() == true)
2163 derr_server<<"Server: Not finishing digging: "
2164 <<"Node metadata disables removal"
2166 cannot_remove_node = true;
2170 catch(InvalidPositionException &e)
2172 derr_server<<"Server: Not finishing digging: Node not found."
2173 <<" Adding block to emerge queue."
2175 m_emerge_queue.addBlock(peer_id,
2176 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2177 cannot_remove_node = true;
2181 If node can't be removed, set block to be re-sent to
2184 if(cannot_remove_node)
2186 derr_server<<"Server: Not finishing digging."<<std::endl;
2188 // Client probably has wrong data.
2189 // Set block not sent, so that client will get
2191 dstream<<"Client "<<peer_id<<" tried to dig "
2192 <<"node; but node cannot be removed."
2193 <<" setting MapBlock not sent."<<std::endl;
2194 RemoteClient *client = getClient(peer_id);
2195 v3s16 blockpos = getNodeBlockPos(p_under);
2196 client->SetBlockNotSent(blockpos);
2202 Send the removal to all other clients.
2203 - If other player is close, send REMOVENODE
2204 - Otherwise set blocks not sent
2206 core::list<u16> far_players;
2207 sendRemoveNode(p_under, peer_id, &far_players, 100);
2210 Update and send inventory
2213 if(g_settings.getBool("creative_mode") == false)
2218 InventoryList *mlist = player->inventory.getList("main");
2221 InventoryItem *item = mlist->getItem(item_i);
2222 if(item && (std::string)item->getName() == "ToolItem")
2224 ToolItem *titem = (ToolItem*)item;
2225 std::string toolname = titem->getToolName();
2227 // Get digging properties for material and tool
2228 DiggingProperties prop =
2229 getDiggingProperties(material, toolname);
2231 if(prop.diggable == false)
2233 derr_server<<"Server: WARNING: Player digged"
2234 <<" with impossible material + tool"
2235 <<" combination"<<std::endl;
2238 bool weared_out = titem->addWear(prop.wear);
2242 mlist->deleteItem(item_i);
2248 Add dug item to inventory
2251 InventoryItem *item = NULL;
2253 if(mineral != MINERAL_NONE)
2254 item = getDiggedMineralItem(mineral);
2259 std::string &dug_s = content_features(material).dug_item;
2262 std::istringstream is(dug_s, std::ios::binary);
2263 item = InventoryItem::deSerialize(is);
2269 // Add a item to inventory
2270 player->inventory.addItem("main", item);
2273 SendInventory(player->peer_id);
2279 (this takes some time so it is done after the quick stuff)
2281 m_ignore_map_edit_events = true;
2282 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2283 m_ignore_map_edit_events = false;
2286 Set blocks not sent to far players
2288 for(core::list<u16>::Iterator
2289 i = far_players.begin();
2290 i != far_players.end(); i++)
2293 RemoteClient *client = getClient(peer_id);
2296 client->SetBlocksNotSent(modified_blocks);
2303 else if(action == 1)
2306 InventoryList *ilist = player->inventory.getList("main");
2311 InventoryItem *item = ilist->getItem(item_i);
2313 // If there is no item, it is not possible to add it anywhere
2318 Handle material items
2320 if(std::string("MaterialItem") == item->getName())
2323 // Don't add a node if this is not a free space
2324 MapNode n2 = m_env.getMap().getNode(p_over);
2325 if(content_buildable_to(n2.d) == false)
2327 // Client probably has wrong data.
2328 // Set block not sent, so that client will get
2330 dstream<<"Client "<<peer_id<<" tried to place"
2331 <<" node in invalid position; setting"
2332 <<" MapBlock not sent."<<std::endl;
2333 RemoteClient *client = getClient(peer_id);
2334 v3s16 blockpos = getNodeBlockPos(p_over);
2335 client->SetBlockNotSent(blockpos);
2339 catch(InvalidPositionException &e)
2341 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2342 <<" Adding block to emerge queue."
2344 m_emerge_queue.addBlock(peer_id,
2345 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2349 // Reset build time counter
2350 getClient(peer->id)->m_time_from_building = 0.0;
2353 MaterialItem *mitem = (MaterialItem*)item;
2355 n.d = mitem->getMaterial();
2356 if(content_features(n.d).wall_mounted)
2357 n.dir = packDir(p_under - p_over);
2362 core::list<u16> far_players;
2363 sendAddNode(p_over, n, 0, &far_players, 100);
2368 InventoryList *ilist = player->inventory.getList("main");
2369 if(g_settings.getBool("creative_mode") == false && ilist)
2371 // Remove from inventory and send inventory
2372 if(mitem->getCount() == 1)
2373 ilist->deleteItem(item_i);
2377 SendInventory(peer_id);
2383 This takes some time so it is done after the quick stuff
2385 core::map<v3s16, MapBlock*> modified_blocks;
2386 m_ignore_map_edit_events = true;
2387 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2388 m_ignore_map_edit_events = false;
2391 Set blocks not sent to far players
2393 for(core::list<u16>::Iterator
2394 i = far_players.begin();
2395 i != far_players.end(); i++)
2398 RemoteClient *client = getClient(peer_id);
2401 client->SetBlocksNotSent(modified_blocks);
2405 Calculate special events
2408 /*if(n.d == CONTENT_MESE)
2411 for(s16 z=-1; z<=1; z++)
2412 for(s16 y=-1; y<=1; y++)
2413 for(s16 x=-1; x<=1; x++)
2420 Place other item (not a block)
2424 v3s16 blockpos = getNodeBlockPos(p_over);
2427 Check that the block is loaded so that the item
2428 can properly be added to the static list too
2430 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2433 derr_server<<"Error while placing object: "
2434 "block not found"<<std::endl;
2438 dout_server<<"Placing a miscellaneous item on map"
2441 // Calculate a position for it
2442 v3f pos = intToFloat(p_over, BS);
2444 pos.Y -= BS*0.25; // let it drop a bit
2446 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2447 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2452 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2456 derr_server<<"WARNING: item resulted in NULL object, "
2457 <<"not placing onto map"
2462 // Add the object to the environment
2463 m_env.addActiveObject(obj);
2465 dout_server<<"Placed object"<<std::endl;
2467 // If item has count<=1, delete it
2468 if(item->getCount() <= 1)
2470 InventoryList *ilist = player->inventory.getList("main");
2471 if(g_settings.getBool("creative_mode") == false && ilist)
2473 // Remove from inventory and send inventory
2474 ilist->deleteItem(item_i);
2476 SendInventory(peer_id);
2479 // Else decrement it
2484 SendInventory(peer_id);
2492 Catch invalid actions
2496 derr_server<<"WARNING: Server: Invalid action "
2497 <<action<<std::endl;
2501 else if(command == TOSERVER_RELEASE)
2510 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2513 else if(command == TOSERVER_SIGNTEXT)
2522 std::string datastring((char*)&data[2], datasize-2);
2523 std::istringstream is(datastring, std::ios_base::binary);
2526 is.read((char*)buf, 6);
2527 v3s16 blockpos = readV3S16(buf);
2528 is.read((char*)buf, 2);
2529 s16 id = readS16(buf);
2530 is.read((char*)buf, 2);
2531 u16 textlen = readU16(buf);
2533 for(u16 i=0; i<textlen; i++)
2535 is.read((char*)buf, 1);
2536 text += (char)buf[0];
2539 MapBlock *block = NULL;
2542 block = m_env.getMap().getBlockNoCreate(blockpos);
2544 catch(InvalidPositionException &e)
2546 derr_server<<"Error while setting sign text: "
2547 "block not found"<<std::endl;
2551 MapBlockObject *obj = block->getObject(id);
2554 derr_server<<"Error while setting sign text: "
2555 "object not found"<<std::endl;
2559 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2561 derr_server<<"Error while setting sign text: "
2562 "object is not a sign"<<std::endl;
2566 ((SignObject*)obj)->setText(text);
2568 obj->getBlock()->setChangedFlag();
2570 else if(command == TOSERVER_SIGNNODETEXT)
2578 std::string datastring((char*)&data[2], datasize-2);
2579 std::istringstream is(datastring, std::ios_base::binary);
2582 is.read((char*)buf, 6);
2583 v3s16 p = readV3S16(buf);
2584 is.read((char*)buf, 2);
2585 u16 textlen = readU16(buf);
2587 for(u16 i=0; i<textlen; i++)
2589 is.read((char*)buf, 1);
2590 text += (char)buf[0];
2593 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2596 if(meta->typeId() != CONTENT_SIGN_WALL)
2598 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2599 signmeta->setText(text);
2601 v3s16 blockpos = getNodeBlockPos(p);
2602 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2605 block->setChangedFlag();
2608 for(core::map<u16, RemoteClient*>::Iterator
2609 i = m_clients.getIterator();
2610 i.atEnd()==false; i++)
2612 RemoteClient *client = i.getNode()->getValue();
2613 client->SetBlockNotSent(blockpos);
2616 else if(command == TOSERVER_INVENTORY_ACTION)
2618 /*// Ignore inventory changes if in creative mode
2619 if(g_settings.getBool("creative_mode") == true)
2621 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2625 // Strip command and create a stream
2626 std::string datastring((char*)&data[2], datasize-2);
2627 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2628 std::istringstream is(datastring, std::ios_base::binary);
2630 InventoryAction *a = InventoryAction::deSerialize(is);
2635 c.current_player = player;
2638 Handle craftresult specially if not in creative mode
2640 bool disable_action = false;
2641 if(a->getType() == IACTION_MOVE
2642 && g_settings.getBool("creative_mode") == false)
2644 IMoveAction *ma = (IMoveAction*)a;
2645 if(ma->to_inv == "current_player" &&
2646 ma->from_inv == "current_player")
2648 // Don't allow moving anything to craftresult
2649 if(ma->to_list == "craftresult")
2652 disable_action = true;
2654 // When something is removed from craftresult
2655 if(ma->from_list == "craftresult")
2657 disable_action = true;
2658 // Remove stuff from craft
2659 InventoryList *clist = player->inventory.getList("craft");
2662 u16 count = ma->count;
2665 clist->decrementMaterials(count);
2668 // Feed action to player inventory
2669 //a->apply(&player->inventory);
2673 // If something appeared in craftresult, throw it
2675 InventoryList *rlist = player->inventory.getList("craftresult");
2676 InventoryList *mlist = player->inventory.getList("main");
2677 if(rlist && mlist && rlist->getUsedSlots() == 1)
2679 InventoryItem *item1 = rlist->changeItem(0, NULL);
2680 mlist->addItem(item1);
2686 if(disable_action == false)
2688 // Feed action to player inventory
2689 //a->apply(&player->inventory);
2697 SendInventory(player->peer_id);
2702 dstream<<"TOSERVER_INVENTORY_ACTION: "
2703 <<"InventoryAction::deSerialize() returned NULL"
2707 else if(command == TOSERVER_CHAT_MESSAGE)
2715 std::string datastring((char*)&data[2], datasize-2);
2716 std::istringstream is(datastring, std::ios_base::binary);
2719 is.read((char*)buf, 2);
2720 u16 len = readU16(buf);
2722 std::wstring message;
2723 for(u16 i=0; i<len; i++)
2725 is.read((char*)buf, 2);
2726 message += (wchar_t)readU16(buf);
2729 // Get player name of this client
2730 std::wstring name = narrow_to_wide(player->getName());
2732 // Line to send to players
2734 // Whether to send to the player that sent the line
2735 bool send_to_sender = false;
2736 // Whether to send to other players
2737 bool send_to_others = false;
2740 std::wstring commandprefix = L"/#";
2741 if(message.substr(0, commandprefix.size()) == commandprefix)
2743 line += L"Server: ";
2745 message = message.substr(commandprefix.size());
2746 // Get player name as narrow string
2747 std::string name_s = player->getName();
2748 // Convert message to narrow string
2749 std::string message_s = wide_to_narrow(message);
2750 // Operator is the single name defined in config.
2751 std::string operator_name = g_settings.get("name");
2752 bool is_operator = (operator_name != "" &&
2753 wide_to_narrow(name) == operator_name);
2754 bool valid_command = false;
2755 if(message_s == "help")
2757 line += L"-!- Available commands: ";
2761 line += L"shutdown setting ";
2766 send_to_sender = true;
2767 valid_command = true;
2769 else if(message_s == "status")
2771 line = getStatusString();
2772 send_to_sender = true;
2773 valid_command = true;
2775 else if(is_operator)
2777 if(message_s == "shutdown")
2779 dstream<<DTIME<<" Server: Operator requested shutdown."
2781 m_shutdown_requested.set(true);
2783 line += L"*** Server shutting down (operator request)";
2784 send_to_sender = true;
2785 valid_command = true;
2787 else if(message_s.substr(0,8) == "setting ")
2789 std::string confline = message_s.substr(8);
2790 g_settings.parseConfigLine(confline);
2791 line += L"-!- Setting changed.";
2792 send_to_sender = true;
2793 valid_command = true;
2797 if(valid_command == false)
2799 line += L"-!- Invalid command: " + message;
2800 send_to_sender = true;
2811 send_to_others = true;
2816 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2819 Send the message to clients
2821 for(core::map<u16, RemoteClient*>::Iterator
2822 i = m_clients.getIterator();
2823 i.atEnd() == false; i++)
2825 // Get client and check that it is valid
2826 RemoteClient *client = i.getNode()->getValue();
2827 assert(client->peer_id == i.getNode()->getKey());
2828 if(client->serialization_version == SER_FMT_VER_INVALID)
2832 bool sender_selected = (peer_id == client->peer_id);
2833 if(sender_selected == true && send_to_sender == false)
2835 if(sender_selected == false && send_to_others == false)
2838 SendChatMessage(client->peer_id, line);
2844 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2845 "unknown command "<<command<<std::endl;
2849 catch(SendFailedException &e)
2851 derr_server<<"Server::ProcessData(): SendFailedException: "
2857 void Server::onMapEditEvent(MapEditEvent *event)
2859 dstream<<"Server::onMapEditEvent()"<<std::endl;
2860 if(m_ignore_map_edit_events)
2862 MapEditEvent *e = event->clone();
2863 m_unsent_map_edit_queue.push_back(e);
2866 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2868 if(id == "current_player")
2870 assert(c->current_player);
2871 return &(c->current_player->inventory);
2875 std::string id0 = fn.next(":");
2877 if(id0 == "nodemeta")
2880 p.X = stoi(fn.next(","));
2881 p.Y = stoi(fn.next(","));
2882 p.Z = stoi(fn.next(","));
2883 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2885 return meta->getInventory();
2886 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2887 <<"no metadata found"<<std::endl;
2891 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2894 void Server::inventoryModified(InventoryContext *c, std::string id)
2896 if(id == "current_player")
2898 assert(c->current_player);
2900 SendInventory(c->current_player->peer_id);
2905 std::string id0 = fn.next(":");
2907 if(id0 == "nodemeta")
2910 p.X = stoi(fn.next(","));
2911 p.Y = stoi(fn.next(","));
2912 p.Z = stoi(fn.next(","));
2913 v3s16 blockpos = getNodeBlockPos(p);
2915 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2917 meta->inventoryModified();
2919 for(core::map<u16, RemoteClient*>::Iterator
2920 i = m_clients.getIterator();
2921 i.atEnd()==false; i++)
2923 RemoteClient *client = i.getNode()->getValue();
2924 client->SetBlockNotSent(blockpos);
2930 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2933 core::list<PlayerInfo> Server::getPlayerInfo()
2935 DSTACK(__FUNCTION_NAME);
2936 JMutexAutoLock envlock(m_env_mutex);
2937 JMutexAutoLock conlock(m_con_mutex);
2939 core::list<PlayerInfo> list;
2941 core::list<Player*> players = m_env.getPlayers();
2943 core::list<Player*>::Iterator i;
2944 for(i = players.begin();
2945 i != players.end(); i++)
2949 Player *player = *i;
2952 con::Peer *peer = m_con.GetPeer(player->peer_id);
2953 // Copy info from peer to info struct
2955 info.address = peer->address;
2956 info.avg_rtt = peer->avg_rtt;
2958 catch(con::PeerNotFoundException &e)
2960 // Set dummy peer info
2962 info.address = Address(0,0,0,0,0);
2966 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2967 info.position = player->getPosition();
2969 list.push_back(info);
2976 void Server::peerAdded(con::Peer *peer)
2978 DSTACK(__FUNCTION_NAME);
2979 dout_server<<"Server::peerAdded(): peer->id="
2980 <<peer->id<<std::endl;
2983 c.type = PEER_ADDED;
2984 c.peer_id = peer->id;
2986 m_peer_change_queue.push_back(c);
2989 void Server::deletingPeer(con::Peer *peer, bool timeout)
2991 DSTACK(__FUNCTION_NAME);
2992 dout_server<<"Server::deletingPeer(): peer->id="
2993 <<peer->id<<", timeout="<<timeout<<std::endl;
2996 c.type = PEER_REMOVED;
2997 c.peer_id = peer->id;
2998 c.timeout = timeout;
2999 m_peer_change_queue.push_back(c);
3002 void Server::SendObjectData(float dtime)
3004 DSTACK(__FUNCTION_NAME);
3006 core::map<v3s16, bool> stepped_blocks;
3008 for(core::map<u16, RemoteClient*>::Iterator
3009 i = m_clients.getIterator();
3010 i.atEnd() == false; i++)
3012 u16 peer_id = i.getNode()->getKey();
3013 RemoteClient *client = i.getNode()->getValue();
3014 assert(client->peer_id == peer_id);
3016 if(client->serialization_version == SER_FMT_VER_INVALID)
3019 client->SendObjectData(this, dtime, stepped_blocks);
3023 void Server::SendPlayerInfos()
3025 DSTACK(__FUNCTION_NAME);
3027 //JMutexAutoLock envlock(m_env_mutex);
3029 // Get connected players
3030 core::list<Player*> players = m_env.getPlayers(true);
3032 u32 player_count = players.getSize();
3033 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3035 SharedBuffer<u8> data(datasize);
3036 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3039 core::list<Player*>::Iterator i;
3040 for(i = players.begin();
3041 i != players.end(); i++)
3043 Player *player = *i;
3045 /*dstream<<"Server sending player info for player with "
3046 "peer_id="<<player->peer_id<<std::endl;*/
3048 writeU16(&data[start], player->peer_id);
3049 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3050 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3051 start += 2+PLAYERNAME_SIZE;
3054 //JMutexAutoLock conlock(m_con_mutex);
3057 m_con.SendToAll(0, data, true);
3060 void Server::SendInventory(u16 peer_id)
3062 DSTACK(__FUNCTION_NAME);
3064 Player* player = m_env.getPlayer(peer_id);
3067 Calculate crafting stuff
3069 if(g_settings.getBool("creative_mode") == false)
3071 InventoryList *clist = player->inventory.getList("craft");
3072 InventoryList *rlist = player->inventory.getList("craftresult");
3075 rlist->clearItems();
3079 InventoryItem *items[9];
3080 for(u16 i=0; i<9; i++)
3082 items[i] = clist->getItem(i);
3091 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3092 if(checkItemCombination(items, specs))
3094 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3103 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3104 if(checkItemCombination(items, specs))
3106 rlist->addItem(new CraftItem("Stick", 4));
3115 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3116 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3117 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3118 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3119 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3120 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3121 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3122 if(checkItemCombination(items, specs))
3124 //rlist->addItem(new MapBlockObjectItem("Sign"));
3125 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3134 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3135 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3136 if(checkItemCombination(items, specs))
3138 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3147 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3148 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3149 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3150 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3151 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3152 if(checkItemCombination(items, specs))
3154 rlist->addItem(new ToolItem("WPick", 0));
3163 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3164 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3165 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3166 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3167 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3168 if(checkItemCombination(items, specs))
3170 rlist->addItem(new ToolItem("STPick", 0));
3179 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3180 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3181 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3182 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3183 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3184 if(checkItemCombination(items, specs))
3186 rlist->addItem(new ToolItem("SteelPick", 0));
3195 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3196 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3197 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3198 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3199 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3200 if(checkItemCombination(items, specs))
3202 rlist->addItem(new ToolItem("MesePick", 0));
3211 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3212 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3213 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3214 if(checkItemCombination(items, specs))
3216 rlist->addItem(new ToolItem("WShovel", 0));
3225 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3226 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3227 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3228 if(checkItemCombination(items, specs))
3230 rlist->addItem(new ToolItem("STShovel", 0));
3239 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3240 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3241 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3242 if(checkItemCombination(items, specs))
3244 rlist->addItem(new ToolItem("SteelShovel", 0));
3253 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3254 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3255 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3256 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3257 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3258 if(checkItemCombination(items, specs))
3260 rlist->addItem(new ToolItem("WAxe", 0));
3269 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3270 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3271 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3272 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3273 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3274 if(checkItemCombination(items, specs))
3276 rlist->addItem(new ToolItem("STAxe", 0));
3285 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3286 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3287 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3288 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3289 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3290 if(checkItemCombination(items, specs))
3292 rlist->addItem(new ToolItem("SteelAxe", 0));
3301 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3302 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3303 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3304 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3305 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3306 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3307 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3308 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3309 if(checkItemCombination(items, specs))
3311 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3320 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3321 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3322 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3323 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3324 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3325 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3326 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3327 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3328 if(checkItemCombination(items, specs))
3330 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3339 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3340 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3341 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3342 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3343 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3344 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3345 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3346 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3347 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3348 if(checkItemCombination(items, specs))
3350 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3356 } // if creative_mode == false
3362 std::ostringstream os;
3363 //os.imbue(std::locale("C"));
3365 player->inventory.serialize(os);
3367 std::string s = os.str();
3369 SharedBuffer<u8> data(s.size()+2);
3370 writeU16(&data[0], TOCLIENT_INVENTORY);
3371 memcpy(&data[2], s.c_str(), s.size());
3374 m_con.Send(peer_id, 0, data, true);
3377 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3379 DSTACK(__FUNCTION_NAME);
3381 std::ostringstream os(std::ios_base::binary);
3385 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3386 os.write((char*)buf, 2);
3389 writeU16(buf, message.size());
3390 os.write((char*)buf, 2);
3393 for(u32 i=0; i<message.size(); i++)
3397 os.write((char*)buf, 2);
3401 std::string s = os.str();
3402 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3404 m_con.Send(peer_id, 0, data, true);
3407 void Server::BroadcastChatMessage(const std::wstring &message)
3409 for(core::map<u16, RemoteClient*>::Iterator
3410 i = m_clients.getIterator();
3411 i.atEnd() == false; i++)
3413 // Get client and check that it is valid
3414 RemoteClient *client = i.getNode()->getValue();
3415 assert(client->peer_id == i.getNode()->getKey());
3416 if(client->serialization_version == SER_FMT_VER_INVALID)
3419 SendChatMessage(client->peer_id, message);
3423 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3424 core::list<u16> *far_players, float far_d_nodes)
3426 float maxd = far_d_nodes*BS;
3427 v3f p_f = intToFloat(p, BS);
3431 SharedBuffer<u8> reply(replysize);
3432 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3433 writeS16(&reply[2], p.X);
3434 writeS16(&reply[4], p.Y);
3435 writeS16(&reply[6], p.Z);
3437 for(core::map<u16, RemoteClient*>::Iterator
3438 i = m_clients.getIterator();
3439 i.atEnd() == false; i++)
3441 // Get client and check that it is valid
3442 RemoteClient *client = i.getNode()->getValue();
3443 assert(client->peer_id == i.getNode()->getKey());
3444 if(client->serialization_version == SER_FMT_VER_INVALID)
3447 // Don't send if it's the same one
3448 if(client->peer_id == ignore_id)
3454 Player *player = m_env.getPlayer(client->peer_id);
3457 // If player is far away, only set modified blocks not sent
3458 v3f player_pos = player->getPosition();
3459 if(player_pos.getDistanceFrom(p_f) > maxd)
3461 far_players->push_back(client->peer_id);
3468 m_con.Send(client->peer_id, 0, reply, true);
3472 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3473 core::list<u16> *far_players, float far_d_nodes)
3475 float maxd = far_d_nodes*BS;
3476 v3f p_f = intToFloat(p, BS);
3478 for(core::map<u16, RemoteClient*>::Iterator
3479 i = m_clients.getIterator();
3480 i.atEnd() == false; i++)
3482 // Get client and check that it is valid
3483 RemoteClient *client = i.getNode()->getValue();
3484 assert(client->peer_id == i.getNode()->getKey());
3485 if(client->serialization_version == SER_FMT_VER_INVALID)
3488 // Don't send if it's the same one
3489 if(client->peer_id == ignore_id)
3495 Player *player = m_env.getPlayer(client->peer_id);
3498 // If player is far away, only set modified blocks not sent
3499 v3f player_pos = player->getPosition();
3500 if(player_pos.getDistanceFrom(p_f) > maxd)
3502 far_players->push_back(client->peer_id);
3509 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3510 SharedBuffer<u8> reply(replysize);
3511 writeU16(&reply[0], TOCLIENT_ADDNODE);
3512 writeS16(&reply[2], p.X);
3513 writeS16(&reply[4], p.Y);
3514 writeS16(&reply[6], p.Z);
3515 n.serialize(&reply[8], client->serialization_version);
3518 m_con.Send(client->peer_id, 0, reply, true);
3522 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3524 DSTACK(__FUNCTION_NAME);
3526 Create a packet with the block in the right format
3529 std::ostringstream os(std::ios_base::binary);
3530 block->serialize(os, ver);
3531 std::string s = os.str();
3532 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3534 u32 replysize = 8 + blockdata.getSize();
3535 SharedBuffer<u8> reply(replysize);
3536 v3s16 p = block->getPos();
3537 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3538 writeS16(&reply[2], p.X);
3539 writeS16(&reply[4], p.Y);
3540 writeS16(&reply[6], p.Z);
3541 memcpy(&reply[8], *blockdata, blockdata.getSize());
3543 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3544 <<": \tpacket size: "<<replysize<<std::endl;*/
3549 m_con.Send(peer_id, 1, reply, true);
3552 void Server::SendBlocks(float dtime)
3554 DSTACK(__FUNCTION_NAME);
3556 JMutexAutoLock envlock(m_env_mutex);
3557 JMutexAutoLock conlock(m_con_mutex);
3559 //TimeTaker timer("Server::SendBlocks");
3561 core::array<PrioritySortedBlockTransfer> queue;
3563 s32 total_sending = 0;
3565 for(core::map<u16, RemoteClient*>::Iterator
3566 i = m_clients.getIterator();
3567 i.atEnd() == false; i++)
3569 RemoteClient *client = i.getNode()->getValue();
3570 assert(client->peer_id == i.getNode()->getKey());
3572 total_sending += client->SendingCount();
3574 if(client->serialization_version == SER_FMT_VER_INVALID)
3577 client->GetNextBlocks(this, dtime, queue);
3581 // Lowest priority number comes first.
3582 // Lowest is most important.
3585 for(u32 i=0; i<queue.size(); i++)
3587 //TODO: Calculate limit dynamically
3588 if(total_sending >= g_settings.getS32
3589 ("max_simultaneous_block_sends_server_total"))
3592 PrioritySortedBlockTransfer q = queue[i];
3594 MapBlock *block = NULL;
3597 block = m_env.getMap().getBlockNoCreate(q.pos);
3599 catch(InvalidPositionException &e)
3604 RemoteClient *client = getClient(q.peer_id);
3606 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3608 client->SentBlock(q.pos);
3615 RemoteClient* Server::getClient(u16 peer_id)
3617 DSTACK(__FUNCTION_NAME);
3618 //JMutexAutoLock lock(m_con_mutex);
3619 core::map<u16, RemoteClient*>::Node *n;
3620 n = m_clients.find(peer_id);
3621 // A client should exist for all peers
3623 return n->getValue();
3626 std::wstring Server::getStatusString()
3628 std::wostringstream os(std::ios_base::binary);
3631 os<<L"uptime="<<m_uptime.get();
3632 // Information about clients
3634 for(core::map<u16, RemoteClient*>::Iterator
3635 i = m_clients.getIterator();
3636 i.atEnd() == false; i++)
3638 // Get client and check that it is valid
3639 RemoteClient *client = i.getNode()->getValue();
3640 assert(client->peer_id == i.getNode()->getKey());
3641 if(client->serialization_version == SER_FMT_VER_INVALID)
3644 Player *player = m_env.getPlayer(client->peer_id);
3645 // Get name of player
3646 std::wstring name = L"unknown";
3648 name = narrow_to_wide(player->getName());
3649 // Add name to information string
3653 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3654 os<<" WARNING: Map saving is disabled."<<std::endl;
3659 void setCreativeInventory(Player *player)
3661 player->resetInventory();
3663 // Give some good picks
3665 InventoryItem *item = new ToolItem("STPick", 0);
3666 void* r = player->inventory.addItem("main", item);
3670 InventoryItem *item = new ToolItem("MesePick", 0);
3671 void* r = player->inventory.addItem("main", item);
3679 // CONTENT_IGNORE-terminated list
3680 u8 material_items[] = {
3689 CONTENT_WATERSOURCE,
3697 u8 *mip = material_items;
3698 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3700 if(*mip == CONTENT_IGNORE)
3703 InventoryItem *item = new MaterialItem(*mip, 1);
3704 player->inventory.addItem("main", item);
3710 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3713 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3714 player->inventory.addItem("main", item);
3717 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3719 // Skip some materials
3720 if(i == CONTENT_WATER || i == CONTENT_TORCH
3721 || i == CONTENT_COALSTONE)
3724 InventoryItem *item = new MaterialItem(i, 1);
3725 player->inventory.addItem("main", item);
3731 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3732 void* r = player->inventory.addItem("main", item);
3737 Player *Server::emergePlayer(const char *name, const char *password,
3741 Try to get an existing player
3743 Player *player = m_env.getPlayer(name);
3746 // If player is already connected, cancel
3747 if(player->peer_id != 0)
3749 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3754 player->peer_id = peer_id;
3756 // Reset inventory to creative if in creative mode
3757 if(g_settings.getBool("creative_mode"))
3759 setCreativeInventory(player);
3766 If player with the wanted peer_id already exists, cancel.
3768 if(m_env.getPlayer(peer_id) != NULL)
3770 dstream<<"emergePlayer(): Player with wrong name but same"
3771 " peer_id already exists"<<std::endl;
3779 player = new ServerRemotePlayer();
3780 //player->peer_id = c.peer_id;
3781 //player->peer_id = PEER_ID_INEXISTENT;
3782 player->peer_id = peer_id;
3783 player->updateName(name);
3789 dstream<<"Server: Finding spawn place for player \""
3790 <<player->getName()<<"\""<<std::endl;
3794 player->setPosition(intToFloat(v3s16(
3801 s16 groundheight = 0;
3803 // Try to find a good place a few times
3804 for(s32 i=0; i<1000; i++)
3807 // We're going to try to throw the player to this position
3808 nodepos = v2s16(-range + (myrand()%(range*2)),
3809 -range + (myrand()%(range*2)));
3810 v2s16 sectorpos = getNodeSectorPos(nodepos);
3811 // Get sector (NOTE: Don't get because it's slow)
3812 //m_env.getMap().emergeSector(sectorpos);
3813 // Get ground height at point (fallbacks to heightmap function)
3814 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3815 // Don't go underwater
3816 if(groundheight < WATER_LEVEL)
3818 //dstream<<"-> Underwater"<<std::endl;
3821 // Don't go to high places
3822 if(groundheight > WATER_LEVEL + 4)
3824 //dstream<<"-> Underwater"<<std::endl;
3828 // Found a good place
3829 dstream<<"Searched through "<<i<<" places."<<std::endl;
3834 // If no suitable place was not found, go above water at least.
3835 if(groundheight < WATER_LEVEL)
3836 groundheight = WATER_LEVEL;
3838 player->setPosition(intToFloat(v3s16(
3840 groundheight + 5, // Accomodate mud
3846 Add player to environment
3849 m_env.addPlayer(player);
3852 Add stuff to inventory
3855 if(g_settings.getBool("creative_mode"))
3857 setCreativeInventory(player);
3862 InventoryItem *item = new ToolItem("WPick", 32000);
3863 void* r = player->inventory.addItem("main", item);
3867 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3868 void* r = player->inventory.addItem("main", item);
3872 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3873 void* r = player->inventory.addItem("main", item);
3877 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3878 void* r = player->inventory.addItem("main", item);
3882 InventoryItem *item = new CraftItem("Stick", 4);
3883 void* r = player->inventory.addItem("main", item);
3887 InventoryItem *item = new ToolItem("WPick", 32000);
3888 void* r = player->inventory.addItem("main", item);
3892 InventoryItem *item = new ToolItem("STPick", 32000);
3893 void* r = player->inventory.addItem("main", item);
3896 /*// Give some lights
3898 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3899 bool r = player->inventory.addItem("main", item);
3903 for(u16 i=0; i<4; i++)
3905 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3906 bool r = player->inventory.addItem("main", item);
3909 /*// Give some other stuff
3911 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3912 bool r = player->inventory.addItem("main", item);
3919 } // create new player
3922 void Server::handlePeerChange(PeerChange &c)
3924 JMutexAutoLock envlock(m_env_mutex);
3925 JMutexAutoLock conlock(m_con_mutex);
3927 if(c.type == PEER_ADDED)
3934 core::map<u16, RemoteClient*>::Node *n;
3935 n = m_clients.find(c.peer_id);
3936 // The client shouldn't already exist
3940 RemoteClient *client = new RemoteClient();
3941 client->peer_id = c.peer_id;
3942 m_clients.insert(client->peer_id, client);
3945 else if(c.type == PEER_REMOVED)
3952 core::map<u16, RemoteClient*>::Node *n;
3953 n = m_clients.find(c.peer_id);
3954 // The client should exist
3957 // Collect information about leaving in chat
3958 std::wstring message;
3960 std::wstring name = L"unknown";
3961 Player *player = m_env.getPlayer(c.peer_id);
3963 name = narrow_to_wide(player->getName());
3967 message += L" left game";
3969 message += L" (timed out)";
3974 m_env.removePlayer(c.peer_id);
3977 // Set player client disconnected
3979 Player *player = m_env.getPlayer(c.peer_id);
3981 player->peer_id = 0;
3985 delete m_clients[c.peer_id];
3986 m_clients.remove(c.peer_id);
3988 // Send player info to all remaining clients
3991 // Send leave chat message to all remaining clients
3992 BroadcastChatMessage(message);
4001 void Server::handlePeerChanges()
4003 while(m_peer_change_queue.size() > 0)
4005 PeerChange c = m_peer_change_queue.pop_front();
4007 dout_server<<"Server: Handling peer change: "
4008 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4011 handlePeerChange(c);
4015 void dedicated_server_loop(Server &server, bool &kill)
4017 DSTACK(__FUNCTION_NAME);
4019 std::cout<<DTIME<<std::endl;
4020 std::cout<<"========================"<<std::endl;
4021 std::cout<<"Running dedicated server"<<std::endl;
4022 std::cout<<"========================"<<std::endl;
4023 std::cout<<std::endl;
4027 // This is kind of a hack but can be done like this
4028 // because server.step() is very light
4032 if(server.getShutdownRequested() || kill)
4034 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4038 static int counter = 0;
4044 core::list<PlayerInfo> list = server.getPlayerInfo();
4045 core::list<PlayerInfo>::Iterator i;
4046 static u32 sum_old = 0;
4047 u32 sum = PIChecksum(list);
4050 std::cout<<DTIME<<"Player info:"<<std::endl;
4051 for(i=list.begin(); i!=list.end(); i++)
4053 i->PrintLine(&std::cout);