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);
1327 data_buffer.append(serializeLongString(
1328 obj->getClientInitializationData()));
1330 data_buffer.append(serializeLongString(""));
1332 // Add to known objects
1333 client->m_known_objects.insert(i.getNode()->getKey(), false);
1336 obj->m_known_by_count++;
1340 SharedBuffer<u8> reply(2 + data_buffer.size());
1341 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1342 memcpy((char*)&reply[2], data_buffer.c_str(),
1343 data_buffer.size());
1345 m_con.Send(client->peer_id, 0, reply, true);
1347 dstream<<"INFO: Server: Sent object remove/add: "
1348 <<removed_objects.size()<<" removed, "
1349 <<added_objects.size()<<" added, "
1350 <<"packet size is "<<reply.getSize()<<std::endl;
1355 Collect a list of all the objects known by the clients
1356 and report it back to the environment.
1359 core::map<u16, bool> all_known_objects;
1361 for(core::map<u16, RemoteClient*>::Iterator
1362 i = m_clients.getIterator();
1363 i.atEnd() == false; i++)
1365 RemoteClient *client = i.getNode()->getValue();
1366 // Go through all known objects of client
1367 for(core::map<u16, bool>::Iterator
1368 i = client->m_known_objects.getIterator();
1369 i.atEnd()==false; i++)
1371 u16 id = i.getNode()->getKey();
1372 all_known_objects[id] = true;
1376 m_env.setKnownActiveObjects(whatever);
1382 Send object messages
1385 JMutexAutoLock envlock(m_env_mutex);
1386 JMutexAutoLock conlock(m_con_mutex);
1389 // Value = data sent by object
1390 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1392 // Get active object messages from environment
1395 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1399 core::list<ActiveObjectMessage>* message_list = NULL;
1400 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1401 n = buffered_messages.find(aom.id);
1404 message_list = new core::list<ActiveObjectMessage>;
1405 buffered_messages.insert(aom.id, message_list);
1409 message_list = n->getValue();
1411 message_list->push_back(aom);
1414 // Route data to every client
1415 for(core::map<u16, RemoteClient*>::Iterator
1416 i = m_clients.getIterator();
1417 i.atEnd()==false; i++)
1419 RemoteClient *client = i.getNode()->getValue();
1420 std::string reliable_data;
1421 std::string unreliable_data;
1422 // Go through all objects in message buffer
1423 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1424 j = buffered_messages.getIterator();
1425 j.atEnd()==false; j++)
1427 // If object is not known by client, skip it
1428 u16 id = j.getNode()->getKey();
1429 if(client->m_known_objects.find(id) == NULL)
1431 // Get message list of object
1432 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1433 // Go through every message
1434 for(core::list<ActiveObjectMessage>::Iterator
1435 k = list->begin(); k != list->end(); k++)
1437 // Compose the full new data with header
1438 ActiveObjectMessage aom = *k;
1439 std::string new_data;
1442 writeU16((u8*)&buf[0], aom.id);
1443 new_data.append(buf, 2);
1445 new_data += serializeString(aom.datastring);
1446 // Add data to buffer
1448 reliable_data += new_data;
1450 unreliable_data += new_data;
1454 reliable_data and unreliable_data are now ready.
1457 if(reliable_data.size() > 0)
1459 SharedBuffer<u8> reply(2 + reliable_data.size());
1460 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1461 memcpy((char*)&reply[2], reliable_data.c_str(),
1462 reliable_data.size());
1464 m_con.Send(client->peer_id, 0, reply, true);
1466 if(unreliable_data.size() > 0)
1468 SharedBuffer<u8> reply(2 + unreliable_data.size());
1469 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1470 memcpy((char*)&reply[2], unreliable_data.c_str(),
1471 unreliable_data.size());
1472 // Send as unreliable
1473 m_con.Send(client->peer_id, 0, reply, false);
1476 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1478 dstream<<"INFO: Server: Size of object message data: "
1479 <<"reliable: "<<reliable_data.size()
1480 <<", unreliable: "<<unreliable_data.size()
1485 // Clear buffered_messages
1486 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1487 i = buffered_messages.getIterator();
1488 i.atEnd()==false; i++)
1490 delete i.getNode()->getValue();
1494 } // enable_experimental
1497 Send queued-for-sending map edit events.
1500 while(m_unsent_map_edit_queue.size() != 0)
1502 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1504 if(event->type == MEET_ADDNODE)
1506 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1507 sendAddNode(event->p, event->n, event->already_known_by_peer);
1509 else if(event->type == MEET_REMOVENODE)
1511 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1512 sendRemoveNode(event->p, event->already_known_by_peer);
1514 else if(event->type == MEET_OTHER)
1516 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1521 dstream<<"WARNING: Server: Unknown MapEditEvent "
1522 <<((u32)event->type)<<std::endl;
1530 Send object positions
1531 TODO: Get rid of MapBlockObjects
1534 float &counter = m_objectdata_timer;
1536 if(counter >= g_settings.getFloat("objectdata_interval"))
1538 JMutexAutoLock lock1(m_env_mutex);
1539 JMutexAutoLock lock2(m_con_mutex);
1540 SendObjectData(counter);
1550 JMutexAutoLock envlock(m_env_mutex);
1551 JMutexAutoLock conlock(m_con_mutex);
1553 core::map<v3s16, MapBlock*> changed_blocks;
1554 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1556 for(core::map<v3s16, MapBlock*>::Iterator
1557 i = changed_blocks.getIterator();
1558 i.atEnd() == false; i++)
1560 MapBlock *block = i.getNode()->getValue();
1562 for(core::map<u16, RemoteClient*>::Iterator
1563 i = m_clients.getIterator();
1564 i.atEnd()==false; i++)
1566 RemoteClient *client = i.getNode()->getValue();
1567 client->SetBlockNotSent(block->getPos());
1573 Trigger emergethread (it somehow gets to a non-triggered but
1574 bysy state sometimes)
1577 float &counter = m_emergethread_trigger_timer;
1583 m_emergethread.trigger();
1589 float &counter = m_savemap_timer;
1591 if(counter >= g_settings.getFloat("server_map_save_interval"))
1595 JMutexAutoLock lock(m_env_mutex);
1597 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1599 // Save only changed parts
1600 m_env.getMap().save(true);
1602 // Delete unused sectors
1603 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1604 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1605 if(deleted_count > 0)
1607 dout_server<<"Server: Unloaded "<<deleted_count
1608 <<" sectors from memory"<<std::endl;
1612 m_env.serializePlayers(m_mapsavedir);
1618 void Server::Receive()
1620 DSTACK(__FUNCTION_NAME);
1621 u32 data_maxsize = 10000;
1622 Buffer<u8> data(data_maxsize);
1627 JMutexAutoLock conlock(m_con_mutex);
1628 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1631 // This has to be called so that the client list gets synced
1632 // with the peer list of the connection
1633 handlePeerChanges();
1635 ProcessData(*data, datasize, peer_id);
1637 catch(con::InvalidIncomingDataException &e)
1639 derr_server<<"Server::Receive(): "
1640 "InvalidIncomingDataException: what()="
1641 <<e.what()<<std::endl;
1643 catch(con::PeerNotFoundException &e)
1645 //NOTE: This is not needed anymore
1647 // The peer has been disconnected.
1648 // Find the associated player and remove it.
1650 /*JMutexAutoLock envlock(m_env_mutex);
1652 dout_server<<"ServerThread: peer_id="<<peer_id
1653 <<" has apparently closed connection. "
1654 <<"Removing player."<<std::endl;
1656 m_env.removePlayer(peer_id);*/
1660 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1662 DSTACK(__FUNCTION_NAME);
1663 // Environment is locked first.
1664 JMutexAutoLock envlock(m_env_mutex);
1665 JMutexAutoLock conlock(m_con_mutex);
1669 peer = m_con.GetPeer(peer_id);
1671 catch(con::PeerNotFoundException &e)
1673 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1674 <<peer_id<<" not found"<<std::endl;
1678 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1686 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1688 if(command == TOSERVER_INIT)
1690 // [0] u16 TOSERVER_INIT
1691 // [2] u8 SER_FMT_VER_HIGHEST
1692 // [3] u8[20] player_name
1697 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1698 <<peer->id<<std::endl;
1700 // First byte after command is maximum supported
1701 // serialization version
1702 u8 client_max = data[2];
1703 u8 our_max = SER_FMT_VER_HIGHEST;
1704 // Use the highest version supported by both
1705 u8 deployed = core::min_(client_max, our_max);
1706 // If it's lower than the lowest supported, give up.
1707 if(deployed < SER_FMT_VER_LOWEST)
1708 deployed = SER_FMT_VER_INVALID;
1710 //peer->serialization_version = deployed;
1711 getClient(peer->id)->pending_serialization_version = deployed;
1713 if(deployed == SER_FMT_VER_INVALID)
1715 derr_server<<DTIME<<"Server: Cannot negotiate "
1716 "serialization version with peer "
1717 <<peer_id<<std::endl;
1726 const u32 playername_size = 20;
1727 char playername[playername_size];
1728 for(u32 i=0; i<playername_size-1; i++)
1730 playername[i] = data[3+i];
1732 playername[playername_size-1] = 0;
1735 Player *player = emergePlayer(playername, "", peer_id);
1736 //Player *player = m_env.getPlayer(peer_id);
1739 // DEBUG: Test serialization
1740 std::ostringstream test_os;
1741 player->serialize(test_os);
1742 dstream<<"Player serialization test: \""<<test_os.str()
1744 std::istringstream test_is(test_os.str());
1745 player->deSerialize(test_is);
1748 // If failed, cancel
1751 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1752 <<": failed to emerge player"<<std::endl;
1757 // If a client is already connected to the player, cancel
1758 if(player->peer_id != 0)
1760 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1761 <<" tried to connect to "
1762 "an already connected player (peer_id="
1763 <<player->peer_id<<")"<<std::endl;
1766 // Set client of player
1767 player->peer_id = peer_id;
1770 // Check if player doesn't exist
1772 throw con::InvalidIncomingDataException
1773 ("Server::ProcessData(): INIT: Player doesn't exist");
1775 /*// update name if it was supplied
1776 if(datasize >= 20+3)
1779 player->updateName((const char*)&data[3]);
1782 // Now answer with a TOCLIENT_INIT
1784 SharedBuffer<u8> reply(2+1+6+8);
1785 writeU16(&reply[0], TOCLIENT_INIT);
1786 writeU8(&reply[2], deployed);
1787 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1788 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1791 m_con.Send(peer_id, 0, reply, true);
1795 if(command == TOSERVER_INIT2)
1797 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1798 <<peer->id<<std::endl;
1801 getClient(peer->id)->serialization_version
1802 = getClient(peer->id)->pending_serialization_version;
1805 Send some initialization data
1808 // Send player info to all players
1811 // Send inventory to player
1812 SendInventory(peer->id);
1816 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1817 m_time_of_day.get());
1818 m_con.Send(peer->id, 0, data, true);
1821 // Send information about server to player in chat
1822 SendChatMessage(peer_id, getStatusString());
1824 // Send information about joining in chat
1826 std::wstring name = L"unknown";
1827 Player *player = m_env.getPlayer(peer_id);
1829 name = narrow_to_wide(player->getName());
1831 std::wstring message;
1834 message += L" joined game";
1835 BroadcastChatMessage(message);
1841 if(peer_ser_ver == SER_FMT_VER_INVALID)
1843 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1844 " serialization format invalid or not initialized."
1845 " Skipping incoming command="<<command<<std::endl;
1849 Player *player = m_env.getPlayer(peer_id);
1852 derr_server<<"Server::ProcessData(): Cancelling: "
1853 "No player for peer_id="<<peer_id
1857 if(command == TOSERVER_PLAYERPOS)
1859 if(datasize < 2+12+12+4+4)
1863 v3s32 ps = readV3S32(&data[start+2]);
1864 v3s32 ss = readV3S32(&data[start+2+12]);
1865 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1866 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1867 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1868 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1869 pitch = wrapDegrees(pitch);
1870 yaw = wrapDegrees(yaw);
1871 player->setPosition(position);
1872 player->setSpeed(speed);
1873 player->setPitch(pitch);
1874 player->setYaw(yaw);
1876 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1877 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1878 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1880 else if(command == TOSERVER_GOTBLOCKS)
1893 u16 count = data[2];
1894 for(u16 i=0; i<count; i++)
1896 if((s16)datasize < 2+1+(i+1)*6)
1897 throw con::InvalidIncomingDataException
1898 ("GOTBLOCKS length is too short");
1899 v3s16 p = readV3S16(&data[2+1+i*6]);
1900 /*dstream<<"Server: GOTBLOCKS ("
1901 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1902 RemoteClient *client = getClient(peer_id);
1903 client->GotBlock(p);
1906 else if(command == TOSERVER_DELETEDBLOCKS)
1919 u16 count = data[2];
1920 for(u16 i=0; i<count; i++)
1922 if((s16)datasize < 2+1+(i+1)*6)
1923 throw con::InvalidIncomingDataException
1924 ("DELETEDBLOCKS length is too short");
1925 v3s16 p = readV3S16(&data[2+1+i*6]);
1926 /*dstream<<"Server: DELETEDBLOCKS ("
1927 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1928 RemoteClient *client = getClient(peer_id);
1929 client->SetBlockNotSent(p);
1932 else if(command == TOSERVER_CLICK_OBJECT)
1939 [2] u8 button (0=left, 1=right)
1944 u8 button = readU8(&data[2]);
1946 p.X = readS16(&data[3]);
1947 p.Y = readS16(&data[5]);
1948 p.Z = readS16(&data[7]);
1949 s16 id = readS16(&data[9]);
1950 //u16 item_i = readU16(&data[11]);
1952 MapBlock *block = NULL;
1955 block = m_env.getMap().getBlockNoCreate(p);
1957 catch(InvalidPositionException &e)
1959 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1963 MapBlockObject *obj = block->getObject(id);
1967 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1971 //TODO: Check that object is reasonably close
1976 InventoryList *ilist = player->inventory.getList("main");
1977 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1980 // Skip if inventory has no free space
1981 if(ilist->getUsedSlots() == ilist->getSize())
1983 dout_server<<"Player inventory has no free space"<<std::endl;
1988 Create the inventory item
1990 InventoryItem *item = NULL;
1991 // If it is an item-object, take the item from it
1992 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1994 item = ((ItemObject*)obj)->createInventoryItem();
1996 // Else create an item of the object
1999 item = new MapBlockObjectItem
2000 (obj->getInventoryString());
2003 // Add to inventory and send inventory
2004 ilist->addItem(item);
2005 SendInventory(player->peer_id);
2008 // Remove from block
2009 block->removeObject(id);
2012 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2020 [2] u8 button (0=left, 1=right)
2024 u8 button = readU8(&data[2]);
2025 u16 id = readS16(&data[3]);
2026 //u16 item_i = readU16(&data[11]);
2028 ServerActiveObject *obj = m_env.getActiveObject(id);
2032 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2037 //TODO: Check that object is reasonably close
2039 // Left click, pick object up (usually)
2042 InventoryList *ilist = player->inventory.getList("main");
2043 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2046 // Skip if inventory has no free space
2047 if(ilist->getUsedSlots() == ilist->getSize())
2049 dout_server<<"Player inventory has no free space"<<std::endl;
2053 // Skip if object has been removed
2058 Create the inventory item
2060 InventoryItem *item = obj->createPickedUpItem();
2064 // Add to inventory and send inventory
2065 ilist->addItem(item);
2066 SendInventory(player->peer_id);
2068 // Remove object from environment
2069 obj->m_removed = true;
2074 else if(command == TOSERVER_GROUND_ACTION)
2082 [3] v3s16 nodepos_undersurface
2083 [9] v3s16 nodepos_abovesurface
2088 2: stop digging (all parameters ignored)
2089 3: digging completed
2091 u8 action = readU8(&data[2]);
2093 p_under.X = readS16(&data[3]);
2094 p_under.Y = readS16(&data[5]);
2095 p_under.Z = readS16(&data[7]);
2097 p_over.X = readS16(&data[9]);
2098 p_over.Y = readS16(&data[11]);
2099 p_over.Z = readS16(&data[13]);
2100 u16 item_i = readU16(&data[15]);
2102 //TODO: Check that target is reasonably close
2110 NOTE: This can be used in the future to check if
2111 somebody is cheating, by checking the timing.
2118 else if(action == 2)
2121 RemoteClient *client = getClient(peer->id);
2122 JMutexAutoLock digmutex(client->m_dig_mutex);
2123 client->m_dig_tool_item = -1;
2128 3: Digging completed
2130 else if(action == 3)
2132 // Mandatory parameter; actually used for nothing
2133 core::map<v3s16, MapBlock*> modified_blocks;
2136 u8 mineral = MINERAL_NONE;
2138 bool cannot_remove_node = false;
2142 MapNode n = m_env.getMap().getNode(p_under);
2144 mineral = n.getMineral();
2145 // Get material at position
2147 // If not yet cancelled
2148 if(cannot_remove_node == false)
2150 // If it's not diggable, do nothing
2151 if(content_diggable(material) == false)
2153 derr_server<<"Server: Not finishing digging: "
2154 <<"Node not diggable"
2156 cannot_remove_node = true;
2159 // If not yet cancelled
2160 if(cannot_remove_node == false)
2162 // Get node metadata
2163 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2164 if(meta && meta->nodeRemovalDisabled() == true)
2166 derr_server<<"Server: Not finishing digging: "
2167 <<"Node metadata disables removal"
2169 cannot_remove_node = true;
2173 catch(InvalidPositionException &e)
2175 derr_server<<"Server: Not finishing digging: Node not found."
2176 <<" Adding block to emerge queue."
2178 m_emerge_queue.addBlock(peer_id,
2179 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2180 cannot_remove_node = true;
2184 If node can't be removed, set block to be re-sent to
2187 if(cannot_remove_node)
2189 derr_server<<"Server: Not finishing digging."<<std::endl;
2191 // Client probably has wrong data.
2192 // Set block not sent, so that client will get
2194 dstream<<"Client "<<peer_id<<" tried to dig "
2195 <<"node; but node cannot be removed."
2196 <<" setting MapBlock not sent."<<std::endl;
2197 RemoteClient *client = getClient(peer_id);
2198 v3s16 blockpos = getNodeBlockPos(p_under);
2199 client->SetBlockNotSent(blockpos);
2205 Send the removal to all other clients.
2206 - If other player is close, send REMOVENODE
2207 - Otherwise set blocks not sent
2209 core::list<u16> far_players;
2210 sendRemoveNode(p_under, peer_id, &far_players, 100);
2213 Update and send inventory
2216 if(g_settings.getBool("creative_mode") == false)
2221 InventoryList *mlist = player->inventory.getList("main");
2224 InventoryItem *item = mlist->getItem(item_i);
2225 if(item && (std::string)item->getName() == "ToolItem")
2227 ToolItem *titem = (ToolItem*)item;
2228 std::string toolname = titem->getToolName();
2230 // Get digging properties for material and tool
2231 DiggingProperties prop =
2232 getDiggingProperties(material, toolname);
2234 if(prop.diggable == false)
2236 derr_server<<"Server: WARNING: Player digged"
2237 <<" with impossible material + tool"
2238 <<" combination"<<std::endl;
2241 bool weared_out = titem->addWear(prop.wear);
2245 mlist->deleteItem(item_i);
2251 Add dug item to inventory
2254 InventoryItem *item = NULL;
2256 if(mineral != MINERAL_NONE)
2257 item = getDiggedMineralItem(mineral);
2262 std::string &dug_s = content_features(material).dug_item;
2265 std::istringstream is(dug_s, std::ios::binary);
2266 item = InventoryItem::deSerialize(is);
2272 // Add a item to inventory
2273 player->inventory.addItem("main", item);
2276 SendInventory(player->peer_id);
2282 (this takes some time so it is done after the quick stuff)
2284 m_ignore_map_edit_events = true;
2285 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2286 m_ignore_map_edit_events = false;
2289 Set blocks not sent to far players
2291 for(core::list<u16>::Iterator
2292 i = far_players.begin();
2293 i != far_players.end(); i++)
2296 RemoteClient *client = getClient(peer_id);
2299 client->SetBlocksNotSent(modified_blocks);
2306 else if(action == 1)
2309 InventoryList *ilist = player->inventory.getList("main");
2314 InventoryItem *item = ilist->getItem(item_i);
2316 // If there is no item, it is not possible to add it anywhere
2321 Handle material items
2323 if(std::string("MaterialItem") == item->getName())
2326 // Don't add a node if this is not a free space
2327 MapNode n2 = m_env.getMap().getNode(p_over);
2328 if(content_buildable_to(n2.d) == false)
2330 // Client probably has wrong data.
2331 // Set block not sent, so that client will get
2333 dstream<<"Client "<<peer_id<<" tried to place"
2334 <<" node in invalid position; setting"
2335 <<" MapBlock not sent."<<std::endl;
2336 RemoteClient *client = getClient(peer_id);
2337 v3s16 blockpos = getNodeBlockPos(p_over);
2338 client->SetBlockNotSent(blockpos);
2342 catch(InvalidPositionException &e)
2344 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2345 <<" Adding block to emerge queue."
2347 m_emerge_queue.addBlock(peer_id,
2348 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2352 // Reset build time counter
2353 getClient(peer->id)->m_time_from_building = 0.0;
2356 MaterialItem *mitem = (MaterialItem*)item;
2358 n.d = mitem->getMaterial();
2359 if(content_features(n.d).wall_mounted)
2360 n.dir = packDir(p_under - p_over);
2365 core::list<u16> far_players;
2366 sendAddNode(p_over, n, 0, &far_players, 100);
2371 InventoryList *ilist = player->inventory.getList("main");
2372 if(g_settings.getBool("creative_mode") == false && ilist)
2374 // Remove from inventory and send inventory
2375 if(mitem->getCount() == 1)
2376 ilist->deleteItem(item_i);
2380 SendInventory(peer_id);
2386 This takes some time so it is done after the quick stuff
2388 core::map<v3s16, MapBlock*> modified_blocks;
2389 m_ignore_map_edit_events = true;
2390 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2391 m_ignore_map_edit_events = false;
2394 Set blocks not sent to far players
2396 for(core::list<u16>::Iterator
2397 i = far_players.begin();
2398 i != far_players.end(); i++)
2401 RemoteClient *client = getClient(peer_id);
2404 client->SetBlocksNotSent(modified_blocks);
2408 Calculate special events
2411 /*if(n.d == CONTENT_MESE)
2414 for(s16 z=-1; z<=1; z++)
2415 for(s16 y=-1; y<=1; y++)
2416 for(s16 x=-1; x<=1; x++)
2423 Place other item (not a block)
2427 v3s16 blockpos = getNodeBlockPos(p_over);
2430 Check that the block is loaded so that the item
2431 can properly be added to the static list too
2433 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2436 derr_server<<"Error while placing object: "
2437 "block not found"<<std::endl;
2441 dout_server<<"Placing a miscellaneous item on map"
2444 // Calculate a position for it
2445 v3f pos = intToFloat(p_over, BS);
2447 pos.Y -= BS*0.25; // let it drop a bit
2449 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2450 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2455 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2459 derr_server<<"WARNING: item resulted in NULL object, "
2460 <<"not placing onto map"
2465 // Add the object to the environment
2466 m_env.addActiveObject(obj);
2468 dout_server<<"Placed object"<<std::endl;
2470 // If item has count<=1, delete it
2471 if(item->getCount() <= 1)
2473 InventoryList *ilist = player->inventory.getList("main");
2474 if(g_settings.getBool("creative_mode") == false && ilist)
2476 // Remove from inventory and send inventory
2477 ilist->deleteItem(item_i);
2479 SendInventory(peer_id);
2482 // Else decrement it
2487 SendInventory(peer_id);
2495 Catch invalid actions
2499 derr_server<<"WARNING: Server: Invalid action "
2500 <<action<<std::endl;
2504 else if(command == TOSERVER_RELEASE)
2513 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2516 else if(command == TOSERVER_SIGNTEXT)
2525 std::string datastring((char*)&data[2], datasize-2);
2526 std::istringstream is(datastring, std::ios_base::binary);
2529 is.read((char*)buf, 6);
2530 v3s16 blockpos = readV3S16(buf);
2531 is.read((char*)buf, 2);
2532 s16 id = readS16(buf);
2533 is.read((char*)buf, 2);
2534 u16 textlen = readU16(buf);
2536 for(u16 i=0; i<textlen; i++)
2538 is.read((char*)buf, 1);
2539 text += (char)buf[0];
2542 MapBlock *block = NULL;
2545 block = m_env.getMap().getBlockNoCreate(blockpos);
2547 catch(InvalidPositionException &e)
2549 derr_server<<"Error while setting sign text: "
2550 "block not found"<<std::endl;
2554 MapBlockObject *obj = block->getObject(id);
2557 derr_server<<"Error while setting sign text: "
2558 "object not found"<<std::endl;
2562 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2564 derr_server<<"Error while setting sign text: "
2565 "object is not a sign"<<std::endl;
2569 ((SignObject*)obj)->setText(text);
2571 obj->getBlock()->setChangedFlag();
2573 else if(command == TOSERVER_SIGNNODETEXT)
2581 std::string datastring((char*)&data[2], datasize-2);
2582 std::istringstream is(datastring, std::ios_base::binary);
2585 is.read((char*)buf, 6);
2586 v3s16 p = readV3S16(buf);
2587 is.read((char*)buf, 2);
2588 u16 textlen = readU16(buf);
2590 for(u16 i=0; i<textlen; i++)
2592 is.read((char*)buf, 1);
2593 text += (char)buf[0];
2596 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2599 if(meta->typeId() != CONTENT_SIGN_WALL)
2601 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2602 signmeta->setText(text);
2604 v3s16 blockpos = getNodeBlockPos(p);
2605 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2608 block->setChangedFlag();
2611 for(core::map<u16, RemoteClient*>::Iterator
2612 i = m_clients.getIterator();
2613 i.atEnd()==false; i++)
2615 RemoteClient *client = i.getNode()->getValue();
2616 client->SetBlockNotSent(blockpos);
2619 else if(command == TOSERVER_INVENTORY_ACTION)
2621 /*// Ignore inventory changes if in creative mode
2622 if(g_settings.getBool("creative_mode") == true)
2624 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2628 // Strip command and create a stream
2629 std::string datastring((char*)&data[2], datasize-2);
2630 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2631 std::istringstream is(datastring, std::ios_base::binary);
2633 InventoryAction *a = InventoryAction::deSerialize(is);
2638 c.current_player = player;
2641 Handle craftresult specially if not in creative mode
2643 bool disable_action = false;
2644 if(a->getType() == IACTION_MOVE
2645 && g_settings.getBool("creative_mode") == false)
2647 IMoveAction *ma = (IMoveAction*)a;
2648 if(ma->to_inv == "current_player" &&
2649 ma->from_inv == "current_player")
2651 // Don't allow moving anything to craftresult
2652 if(ma->to_list == "craftresult")
2655 disable_action = true;
2657 // When something is removed from craftresult
2658 if(ma->from_list == "craftresult")
2660 disable_action = true;
2661 // Remove stuff from craft
2662 InventoryList *clist = player->inventory.getList("craft");
2665 u16 count = ma->count;
2668 clist->decrementMaterials(count);
2671 // Feed action to player inventory
2672 //a->apply(&player->inventory);
2676 // If something appeared in craftresult, throw it
2678 InventoryList *rlist = player->inventory.getList("craftresult");
2679 InventoryList *mlist = player->inventory.getList("main");
2680 if(rlist && mlist && rlist->getUsedSlots() == 1)
2682 InventoryItem *item1 = rlist->changeItem(0, NULL);
2683 mlist->addItem(item1);
2689 if(disable_action == false)
2691 // Feed action to player inventory
2692 //a->apply(&player->inventory);
2700 SendInventory(player->peer_id);
2705 dstream<<"TOSERVER_INVENTORY_ACTION: "
2706 <<"InventoryAction::deSerialize() returned NULL"
2710 else if(command == TOSERVER_CHAT_MESSAGE)
2718 std::string datastring((char*)&data[2], datasize-2);
2719 std::istringstream is(datastring, std::ios_base::binary);
2722 is.read((char*)buf, 2);
2723 u16 len = readU16(buf);
2725 std::wstring message;
2726 for(u16 i=0; i<len; i++)
2728 is.read((char*)buf, 2);
2729 message += (wchar_t)readU16(buf);
2732 // Get player name of this client
2733 std::wstring name = narrow_to_wide(player->getName());
2735 // Line to send to players
2737 // Whether to send to the player that sent the line
2738 bool send_to_sender = false;
2739 // Whether to send to other players
2740 bool send_to_others = false;
2743 std::wstring commandprefix = L"/#";
2744 if(message.substr(0, commandprefix.size()) == commandprefix)
2746 line += L"Server: ";
2748 message = message.substr(commandprefix.size());
2749 // Get player name as narrow string
2750 std::string name_s = player->getName();
2751 // Convert message to narrow string
2752 std::string message_s = wide_to_narrow(message);
2753 // Operator is the single name defined in config.
2754 std::string operator_name = g_settings.get("name");
2755 bool is_operator = (operator_name != "" &&
2756 wide_to_narrow(name) == operator_name);
2757 bool valid_command = false;
2758 if(message_s == "help")
2760 line += L"-!- Available commands: ";
2764 line += L"shutdown setting ";
2769 send_to_sender = true;
2770 valid_command = true;
2772 else if(message_s == "status")
2774 line = getStatusString();
2775 send_to_sender = true;
2776 valid_command = true;
2778 else if(is_operator)
2780 if(message_s == "shutdown")
2782 dstream<<DTIME<<" Server: Operator requested shutdown."
2784 m_shutdown_requested.set(true);
2786 line += L"*** Server shutting down (operator request)";
2787 send_to_sender = true;
2788 valid_command = true;
2790 else if(message_s.substr(0,8) == "setting ")
2792 std::string confline = message_s.substr(8);
2793 g_settings.parseConfigLine(confline);
2794 line += L"-!- Setting changed.";
2795 send_to_sender = true;
2796 valid_command = true;
2800 if(valid_command == false)
2802 line += L"-!- Invalid command: " + message;
2803 send_to_sender = true;
2814 send_to_others = true;
2819 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2822 Send the message to clients
2824 for(core::map<u16, RemoteClient*>::Iterator
2825 i = m_clients.getIterator();
2826 i.atEnd() == false; i++)
2828 // Get client and check that it is valid
2829 RemoteClient *client = i.getNode()->getValue();
2830 assert(client->peer_id == i.getNode()->getKey());
2831 if(client->serialization_version == SER_FMT_VER_INVALID)
2835 bool sender_selected = (peer_id == client->peer_id);
2836 if(sender_selected == true && send_to_sender == false)
2838 if(sender_selected == false && send_to_others == false)
2841 SendChatMessage(client->peer_id, line);
2847 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2848 "unknown command "<<command<<std::endl;
2852 catch(SendFailedException &e)
2854 derr_server<<"Server::ProcessData(): SendFailedException: "
2860 void Server::onMapEditEvent(MapEditEvent *event)
2862 dstream<<"Server::onMapEditEvent()"<<std::endl;
2863 if(m_ignore_map_edit_events)
2865 MapEditEvent *e = event->clone();
2866 m_unsent_map_edit_queue.push_back(e);
2869 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2871 if(id == "current_player")
2873 assert(c->current_player);
2874 return &(c->current_player->inventory);
2878 std::string id0 = fn.next(":");
2880 if(id0 == "nodemeta")
2883 p.X = stoi(fn.next(","));
2884 p.Y = stoi(fn.next(","));
2885 p.Z = stoi(fn.next(","));
2886 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2888 return meta->getInventory();
2889 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2890 <<"no metadata found"<<std::endl;
2894 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2897 void Server::inventoryModified(InventoryContext *c, std::string id)
2899 if(id == "current_player")
2901 assert(c->current_player);
2903 SendInventory(c->current_player->peer_id);
2908 std::string id0 = fn.next(":");
2910 if(id0 == "nodemeta")
2913 p.X = stoi(fn.next(","));
2914 p.Y = stoi(fn.next(","));
2915 p.Z = stoi(fn.next(","));
2916 v3s16 blockpos = getNodeBlockPos(p);
2918 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2920 meta->inventoryModified();
2922 for(core::map<u16, RemoteClient*>::Iterator
2923 i = m_clients.getIterator();
2924 i.atEnd()==false; i++)
2926 RemoteClient *client = i.getNode()->getValue();
2927 client->SetBlockNotSent(blockpos);
2933 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2936 core::list<PlayerInfo> Server::getPlayerInfo()
2938 DSTACK(__FUNCTION_NAME);
2939 JMutexAutoLock envlock(m_env_mutex);
2940 JMutexAutoLock conlock(m_con_mutex);
2942 core::list<PlayerInfo> list;
2944 core::list<Player*> players = m_env.getPlayers();
2946 core::list<Player*>::Iterator i;
2947 for(i = players.begin();
2948 i != players.end(); i++)
2952 Player *player = *i;
2955 con::Peer *peer = m_con.GetPeer(player->peer_id);
2956 // Copy info from peer to info struct
2958 info.address = peer->address;
2959 info.avg_rtt = peer->avg_rtt;
2961 catch(con::PeerNotFoundException &e)
2963 // Set dummy peer info
2965 info.address = Address(0,0,0,0,0);
2969 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2970 info.position = player->getPosition();
2972 list.push_back(info);
2979 void Server::peerAdded(con::Peer *peer)
2981 DSTACK(__FUNCTION_NAME);
2982 dout_server<<"Server::peerAdded(): peer->id="
2983 <<peer->id<<std::endl;
2986 c.type = PEER_ADDED;
2987 c.peer_id = peer->id;
2989 m_peer_change_queue.push_back(c);
2992 void Server::deletingPeer(con::Peer *peer, bool timeout)
2994 DSTACK(__FUNCTION_NAME);
2995 dout_server<<"Server::deletingPeer(): peer->id="
2996 <<peer->id<<", timeout="<<timeout<<std::endl;
2999 c.type = PEER_REMOVED;
3000 c.peer_id = peer->id;
3001 c.timeout = timeout;
3002 m_peer_change_queue.push_back(c);
3005 void Server::SendObjectData(float dtime)
3007 DSTACK(__FUNCTION_NAME);
3009 core::map<v3s16, bool> stepped_blocks;
3011 for(core::map<u16, RemoteClient*>::Iterator
3012 i = m_clients.getIterator();
3013 i.atEnd() == false; i++)
3015 u16 peer_id = i.getNode()->getKey();
3016 RemoteClient *client = i.getNode()->getValue();
3017 assert(client->peer_id == peer_id);
3019 if(client->serialization_version == SER_FMT_VER_INVALID)
3022 client->SendObjectData(this, dtime, stepped_blocks);
3026 void Server::SendPlayerInfos()
3028 DSTACK(__FUNCTION_NAME);
3030 //JMutexAutoLock envlock(m_env_mutex);
3032 // Get connected players
3033 core::list<Player*> players = m_env.getPlayers(true);
3035 u32 player_count = players.getSize();
3036 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3038 SharedBuffer<u8> data(datasize);
3039 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3042 core::list<Player*>::Iterator i;
3043 for(i = players.begin();
3044 i != players.end(); i++)
3046 Player *player = *i;
3048 /*dstream<<"Server sending player info for player with "
3049 "peer_id="<<player->peer_id<<std::endl;*/
3051 writeU16(&data[start], player->peer_id);
3052 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3053 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3054 start += 2+PLAYERNAME_SIZE;
3057 //JMutexAutoLock conlock(m_con_mutex);
3060 m_con.SendToAll(0, data, true);
3063 void Server::SendInventory(u16 peer_id)
3065 DSTACK(__FUNCTION_NAME);
3067 Player* player = m_env.getPlayer(peer_id);
3070 Calculate crafting stuff
3072 if(g_settings.getBool("creative_mode") == false)
3074 InventoryList *clist = player->inventory.getList("craft");
3075 InventoryList *rlist = player->inventory.getList("craftresult");
3078 rlist->clearItems();
3082 InventoryItem *items[9];
3083 for(u16 i=0; i<9; i++)
3085 items[i] = clist->getItem(i);
3094 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3095 if(checkItemCombination(items, specs))
3097 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3106 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3107 if(checkItemCombination(items, specs))
3109 rlist->addItem(new CraftItem("Stick", 4));
3118 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3119 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3120 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3121 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3122 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3123 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3124 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3125 if(checkItemCombination(items, specs))
3127 //rlist->addItem(new MapBlockObjectItem("Sign"));
3128 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3137 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3138 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3139 if(checkItemCombination(items, specs))
3141 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3150 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3151 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3152 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3153 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3154 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3155 if(checkItemCombination(items, specs))
3157 rlist->addItem(new ToolItem("WPick", 0));
3166 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3167 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3168 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3169 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3170 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3171 if(checkItemCombination(items, specs))
3173 rlist->addItem(new ToolItem("STPick", 0));
3182 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3183 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3184 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3185 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3186 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3187 if(checkItemCombination(items, specs))
3189 rlist->addItem(new ToolItem("SteelPick", 0));
3198 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3199 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3200 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3201 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3202 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3203 if(checkItemCombination(items, specs))
3205 rlist->addItem(new ToolItem("MesePick", 0));
3214 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3215 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3216 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3217 if(checkItemCombination(items, specs))
3219 rlist->addItem(new ToolItem("WShovel", 0));
3228 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3229 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3230 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3231 if(checkItemCombination(items, specs))
3233 rlist->addItem(new ToolItem("STShovel", 0));
3242 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3243 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3244 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3245 if(checkItemCombination(items, specs))
3247 rlist->addItem(new ToolItem("SteelShovel", 0));
3256 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3257 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3258 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3259 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3260 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3261 if(checkItemCombination(items, specs))
3263 rlist->addItem(new ToolItem("WAxe", 0));
3272 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3273 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3274 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3275 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3276 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3277 if(checkItemCombination(items, specs))
3279 rlist->addItem(new ToolItem("STAxe", 0));
3288 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3289 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3290 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3291 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3292 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3293 if(checkItemCombination(items, specs))
3295 rlist->addItem(new ToolItem("SteelAxe", 0));
3304 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3305 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3306 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3307 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3308 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3309 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3310 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3311 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3312 if(checkItemCombination(items, specs))
3314 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3323 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3324 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3325 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3326 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3327 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3328 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3329 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3330 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3331 if(checkItemCombination(items, specs))
3333 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3342 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3343 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3344 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3345 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3346 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3347 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3348 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3349 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3350 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3351 if(checkItemCombination(items, specs))
3353 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3359 } // if creative_mode == false
3365 std::ostringstream os;
3366 //os.imbue(std::locale("C"));
3368 player->inventory.serialize(os);
3370 std::string s = os.str();
3372 SharedBuffer<u8> data(s.size()+2);
3373 writeU16(&data[0], TOCLIENT_INVENTORY);
3374 memcpy(&data[2], s.c_str(), s.size());
3377 m_con.Send(peer_id, 0, data, true);
3380 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3382 DSTACK(__FUNCTION_NAME);
3384 std::ostringstream os(std::ios_base::binary);
3388 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3389 os.write((char*)buf, 2);
3392 writeU16(buf, message.size());
3393 os.write((char*)buf, 2);
3396 for(u32 i=0; i<message.size(); i++)
3400 os.write((char*)buf, 2);
3404 std::string s = os.str();
3405 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3407 m_con.Send(peer_id, 0, data, true);
3410 void Server::BroadcastChatMessage(const std::wstring &message)
3412 for(core::map<u16, RemoteClient*>::Iterator
3413 i = m_clients.getIterator();
3414 i.atEnd() == false; i++)
3416 // Get client and check that it is valid
3417 RemoteClient *client = i.getNode()->getValue();
3418 assert(client->peer_id == i.getNode()->getKey());
3419 if(client->serialization_version == SER_FMT_VER_INVALID)
3422 SendChatMessage(client->peer_id, message);
3426 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3427 core::list<u16> *far_players, float far_d_nodes)
3429 float maxd = far_d_nodes*BS;
3430 v3f p_f = intToFloat(p, BS);
3434 SharedBuffer<u8> reply(replysize);
3435 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3436 writeS16(&reply[2], p.X);
3437 writeS16(&reply[4], p.Y);
3438 writeS16(&reply[6], p.Z);
3440 for(core::map<u16, RemoteClient*>::Iterator
3441 i = m_clients.getIterator();
3442 i.atEnd() == false; i++)
3444 // Get client and check that it is valid
3445 RemoteClient *client = i.getNode()->getValue();
3446 assert(client->peer_id == i.getNode()->getKey());
3447 if(client->serialization_version == SER_FMT_VER_INVALID)
3450 // Don't send if it's the same one
3451 if(client->peer_id == ignore_id)
3457 Player *player = m_env.getPlayer(client->peer_id);
3460 // If player is far away, only set modified blocks not sent
3461 v3f player_pos = player->getPosition();
3462 if(player_pos.getDistanceFrom(p_f) > maxd)
3464 far_players->push_back(client->peer_id);
3471 m_con.Send(client->peer_id, 0, reply, true);
3475 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3476 core::list<u16> *far_players, float far_d_nodes)
3478 float maxd = far_d_nodes*BS;
3479 v3f p_f = intToFloat(p, BS);
3481 for(core::map<u16, RemoteClient*>::Iterator
3482 i = m_clients.getIterator();
3483 i.atEnd() == false; i++)
3485 // Get client and check that it is valid
3486 RemoteClient *client = i.getNode()->getValue();
3487 assert(client->peer_id == i.getNode()->getKey());
3488 if(client->serialization_version == SER_FMT_VER_INVALID)
3491 // Don't send if it's the same one
3492 if(client->peer_id == ignore_id)
3498 Player *player = m_env.getPlayer(client->peer_id);
3501 // If player is far away, only set modified blocks not sent
3502 v3f player_pos = player->getPosition();
3503 if(player_pos.getDistanceFrom(p_f) > maxd)
3505 far_players->push_back(client->peer_id);
3512 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3513 SharedBuffer<u8> reply(replysize);
3514 writeU16(&reply[0], TOCLIENT_ADDNODE);
3515 writeS16(&reply[2], p.X);
3516 writeS16(&reply[4], p.Y);
3517 writeS16(&reply[6], p.Z);
3518 n.serialize(&reply[8], client->serialization_version);
3521 m_con.Send(client->peer_id, 0, reply, true);
3525 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3527 DSTACK(__FUNCTION_NAME);
3529 Create a packet with the block in the right format
3532 std::ostringstream os(std::ios_base::binary);
3533 block->serialize(os, ver);
3534 std::string s = os.str();
3535 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3537 u32 replysize = 8 + blockdata.getSize();
3538 SharedBuffer<u8> reply(replysize);
3539 v3s16 p = block->getPos();
3540 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3541 writeS16(&reply[2], p.X);
3542 writeS16(&reply[4], p.Y);
3543 writeS16(&reply[6], p.Z);
3544 memcpy(&reply[8], *blockdata, blockdata.getSize());
3546 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3547 <<": \tpacket size: "<<replysize<<std::endl;*/
3552 m_con.Send(peer_id, 1, reply, true);
3555 void Server::SendBlocks(float dtime)
3557 DSTACK(__FUNCTION_NAME);
3559 JMutexAutoLock envlock(m_env_mutex);
3560 JMutexAutoLock conlock(m_con_mutex);
3562 //TimeTaker timer("Server::SendBlocks");
3564 core::array<PrioritySortedBlockTransfer> queue;
3566 s32 total_sending = 0;
3568 for(core::map<u16, RemoteClient*>::Iterator
3569 i = m_clients.getIterator();
3570 i.atEnd() == false; i++)
3572 RemoteClient *client = i.getNode()->getValue();
3573 assert(client->peer_id == i.getNode()->getKey());
3575 total_sending += client->SendingCount();
3577 if(client->serialization_version == SER_FMT_VER_INVALID)
3580 client->GetNextBlocks(this, dtime, queue);
3584 // Lowest priority number comes first.
3585 // Lowest is most important.
3588 for(u32 i=0; i<queue.size(); i++)
3590 //TODO: Calculate limit dynamically
3591 if(total_sending >= g_settings.getS32
3592 ("max_simultaneous_block_sends_server_total"))
3595 PrioritySortedBlockTransfer q = queue[i];
3597 MapBlock *block = NULL;
3600 block = m_env.getMap().getBlockNoCreate(q.pos);
3602 catch(InvalidPositionException &e)
3607 RemoteClient *client = getClient(q.peer_id);
3609 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3611 client->SentBlock(q.pos);
3618 RemoteClient* Server::getClient(u16 peer_id)
3620 DSTACK(__FUNCTION_NAME);
3621 //JMutexAutoLock lock(m_con_mutex);
3622 core::map<u16, RemoteClient*>::Node *n;
3623 n = m_clients.find(peer_id);
3624 // A client should exist for all peers
3626 return n->getValue();
3629 std::wstring Server::getStatusString()
3631 std::wostringstream os(std::ios_base::binary);
3634 os<<L"uptime="<<m_uptime.get();
3635 // Information about clients
3637 for(core::map<u16, RemoteClient*>::Iterator
3638 i = m_clients.getIterator();
3639 i.atEnd() == false; i++)
3641 // Get client and check that it is valid
3642 RemoteClient *client = i.getNode()->getValue();
3643 assert(client->peer_id == i.getNode()->getKey());
3644 if(client->serialization_version == SER_FMT_VER_INVALID)
3647 Player *player = m_env.getPlayer(client->peer_id);
3648 // Get name of player
3649 std::wstring name = L"unknown";
3651 name = narrow_to_wide(player->getName());
3652 // Add name to information string
3656 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3657 os<<" WARNING: Map saving is disabled."<<std::endl;
3662 void setCreativeInventory(Player *player)
3664 player->resetInventory();
3666 // Give some good picks
3668 InventoryItem *item = new ToolItem("STPick", 0);
3669 void* r = player->inventory.addItem("main", item);
3673 InventoryItem *item = new ToolItem("MesePick", 0);
3674 void* r = player->inventory.addItem("main", item);
3682 // CONTENT_IGNORE-terminated list
3683 u8 material_items[] = {
3692 CONTENT_WATERSOURCE,
3700 u8 *mip = material_items;
3701 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3703 if(*mip == CONTENT_IGNORE)
3706 InventoryItem *item = new MaterialItem(*mip, 1);
3707 player->inventory.addItem("main", item);
3713 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3716 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3717 player->inventory.addItem("main", item);
3720 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3722 // Skip some materials
3723 if(i == CONTENT_WATER || i == CONTENT_TORCH
3724 || i == CONTENT_COALSTONE)
3727 InventoryItem *item = new MaterialItem(i, 1);
3728 player->inventory.addItem("main", item);
3734 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3735 void* r = player->inventory.addItem("main", item);
3740 Player *Server::emergePlayer(const char *name, const char *password,
3744 Try to get an existing player
3746 Player *player = m_env.getPlayer(name);
3749 // If player is already connected, cancel
3750 if(player->peer_id != 0)
3752 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3757 player->peer_id = peer_id;
3759 // Reset inventory to creative if in creative mode
3760 if(g_settings.getBool("creative_mode"))
3762 setCreativeInventory(player);
3769 If player with the wanted peer_id already exists, cancel.
3771 if(m_env.getPlayer(peer_id) != NULL)
3773 dstream<<"emergePlayer(): Player with wrong name but same"
3774 " peer_id already exists"<<std::endl;
3782 player = new ServerRemotePlayer();
3783 //player->peer_id = c.peer_id;
3784 //player->peer_id = PEER_ID_INEXISTENT;
3785 player->peer_id = peer_id;
3786 player->updateName(name);
3792 dstream<<"Server: Finding spawn place for player \""
3793 <<player->getName()<<"\""<<std::endl;
3797 player->setPosition(intToFloat(v3s16(
3804 s16 groundheight = 0;
3806 // Try to find a good place a few times
3807 for(s32 i=0; i<1000; i++)
3810 // We're going to try to throw the player to this position
3811 nodepos = v2s16(-range + (myrand()%(range*2)),
3812 -range + (myrand()%(range*2)));
3813 v2s16 sectorpos = getNodeSectorPos(nodepos);
3814 // Get sector (NOTE: Don't get because it's slow)
3815 //m_env.getMap().emergeSector(sectorpos);
3816 // Get ground height at point (fallbacks to heightmap function)
3817 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3818 // Don't go underwater
3819 if(groundheight < WATER_LEVEL)
3821 //dstream<<"-> Underwater"<<std::endl;
3824 // Don't go to high places
3825 if(groundheight > WATER_LEVEL + 4)
3827 //dstream<<"-> Underwater"<<std::endl;
3831 // Found a good place
3832 dstream<<"Searched through "<<i<<" places."<<std::endl;
3837 // If no suitable place was not found, go above water at least.
3838 if(groundheight < WATER_LEVEL)
3839 groundheight = WATER_LEVEL;
3841 player->setPosition(intToFloat(v3s16(
3843 groundheight + 5, // Accomodate mud
3849 Add player to environment
3852 m_env.addPlayer(player);
3855 Add stuff to inventory
3858 if(g_settings.getBool("creative_mode"))
3860 setCreativeInventory(player);
3865 InventoryItem *item = new ToolItem("WPick", 32000);
3866 void* r = player->inventory.addItem("main", item);
3870 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3871 void* r = player->inventory.addItem("main", item);
3875 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3876 void* r = player->inventory.addItem("main", item);
3880 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3881 void* r = player->inventory.addItem("main", item);
3885 InventoryItem *item = new CraftItem("Stick", 4);
3886 void* r = player->inventory.addItem("main", item);
3890 InventoryItem *item = new ToolItem("WPick", 32000);
3891 void* r = player->inventory.addItem("main", item);
3895 InventoryItem *item = new ToolItem("STPick", 32000);
3896 void* r = player->inventory.addItem("main", item);
3899 /*// Give some lights
3901 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3902 bool r = player->inventory.addItem("main", item);
3906 for(u16 i=0; i<4; i++)
3908 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3909 bool r = player->inventory.addItem("main", item);
3912 /*// Give some other stuff
3914 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3915 bool r = player->inventory.addItem("main", item);
3922 } // create new player
3925 void Server::handlePeerChange(PeerChange &c)
3927 JMutexAutoLock envlock(m_env_mutex);
3928 JMutexAutoLock conlock(m_con_mutex);
3930 if(c.type == PEER_ADDED)
3937 core::map<u16, RemoteClient*>::Node *n;
3938 n = m_clients.find(c.peer_id);
3939 // The client shouldn't already exist
3943 RemoteClient *client = new RemoteClient();
3944 client->peer_id = c.peer_id;
3945 m_clients.insert(client->peer_id, client);
3948 else if(c.type == PEER_REMOVED)
3955 core::map<u16, RemoteClient*>::Node *n;
3956 n = m_clients.find(c.peer_id);
3957 // The client should exist
3960 // Collect information about leaving in chat
3961 std::wstring message;
3963 std::wstring name = L"unknown";
3964 Player *player = m_env.getPlayer(c.peer_id);
3966 name = narrow_to_wide(player->getName());
3970 message += L" left game";
3972 message += L" (timed out)";
3977 m_env.removePlayer(c.peer_id);
3980 // Set player client disconnected
3982 Player *player = m_env.getPlayer(c.peer_id);
3984 player->peer_id = 0;
3988 delete m_clients[c.peer_id];
3989 m_clients.remove(c.peer_id);
3991 // Send player info to all remaining clients
3994 // Send leave chat message to all remaining clients
3995 BroadcastChatMessage(message);
4004 void Server::handlePeerChanges()
4006 while(m_peer_change_queue.size() > 0)
4008 PeerChange c = m_peer_change_queue.pop_front();
4010 dout_server<<"Server: Handling peer change: "
4011 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4014 handlePeerChange(c);
4018 void dedicated_server_loop(Server &server, bool &kill)
4020 DSTACK(__FUNCTION_NAME);
4022 std::cout<<DTIME<<std::endl;
4023 std::cout<<"========================"<<std::endl;
4024 std::cout<<"Running dedicated server"<<std::endl;
4025 std::cout<<"========================"<<std::endl;
4026 std::cout<<std::endl;
4030 // This is kind of a hack but can be done like this
4031 // because server.step() is very light
4035 if(server.getShutdownRequested() || kill)
4037 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4041 static int counter = 0;
4047 core::list<PlayerInfo> list = server.getPlayerInfo();
4048 core::list<PlayerInfo>::Iterator i;
4049 static u32 sum_old = 0;
4050 u32 sum = PIChecksum(list);
4053 std::cout<<DTIME<<"Player info:"<<std::endl;
4054 for(i=list.begin(); i!=list.end(); i++)
4056 i->PrintLine(&std::cout);