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()
898 void PlayerInfo::PrintLine(std::ostream *s)
901 (*s)<<"\""<<name<<"\" ("
902 <<(position.X/10)<<","<<(position.Y/10)
903 <<","<<(position.Z/10)<<") ";
905 (*s)<<" avg_rtt="<<avg_rtt;
909 u32 PIChecksum(core::list<PlayerInfo> &l)
911 core::list<PlayerInfo>::Iterator i;
914 for(i=l.begin(); i!=l.end(); i++)
916 checksum += a * (i->id+1);
917 checksum ^= 0x435aafcd;
928 std::string mapsavedir
930 m_env(new ServerMap(mapsavedir), this),
931 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
933 m_emergethread(this),
936 m_time_of_day_send_timer(0),
938 m_mapsavedir(mapsavedir),
939 m_shutdown_requested(false),
940 m_ignore_map_edit_events(false),
941 m_ignore_map_edit_events_peer_id(0)
943 m_liquid_transform_timer = 0.0;
944 m_print_info_timer = 0.0;
945 m_objectdata_timer = 0.0;
946 m_emergethread_trigger_timer = 0.0;
947 m_savemap_timer = 0.0;
951 m_step_dtime_mutex.Init();
954 m_env.getMap().addEventReceiver(this);
957 m_env.deSerializePlayers(m_mapsavedir);
963 Send shutdown message
966 JMutexAutoLock conlock(m_con_mutex);
968 std::wstring line = L"*** Server shutting down";
971 Send the message to clients
973 for(core::map<u16, RemoteClient*>::Iterator
974 i = m_clients.getIterator();
975 i.atEnd() == false; i++)
977 // Get client and check that it is valid
978 RemoteClient *client = i.getNode()->getValue();
979 assert(client->peer_id == i.getNode()->getKey());
980 if(client->serialization_version == SER_FMT_VER_INVALID)
983 SendChatMessage(client->peer_id, line);
990 m_env.serializePlayers(m_mapsavedir);
1001 JMutexAutoLock clientslock(m_con_mutex);
1003 for(core::map<u16, RemoteClient*>::Iterator
1004 i = m_clients.getIterator();
1005 i.atEnd() == false; i++)
1008 // NOTE: These are removed by env destructor
1010 u16 peer_id = i.getNode()->getKey();
1011 JMutexAutoLock envlock(m_env_mutex);
1012 m_env.removePlayer(peer_id);
1016 delete i.getNode()->getValue();
1021 void Server::start(unsigned short port)
1023 DSTACK(__FUNCTION_NAME);
1024 // Stop thread if already running
1027 // Initialize connection
1028 m_con.setTimeoutMs(30);
1032 m_thread.setRun(true);
1035 dout_server<<"Server: Started on port "<<port<<std::endl;
1040 DSTACK(__FUNCTION_NAME);
1042 // Stop threads (set run=false first so both start stopping)
1043 m_thread.setRun(false);
1044 m_emergethread.setRun(false);
1046 m_emergethread.stop();
1048 dout_server<<"Server: Threads stopped"<<std::endl;
1050 dout_server<<"Server: Saving players"<<std::endl;
1052 // FIXME: Apparently this does not do anything here
1053 //m_env.serializePlayers(m_mapsavedir);
1056 void Server::step(float dtime)
1058 DSTACK(__FUNCTION_NAME);
1063 JMutexAutoLock lock(m_step_dtime_mutex);
1064 m_step_dtime += dtime;
1068 void Server::AsyncRunStep()
1070 DSTACK(__FUNCTION_NAME);
1074 JMutexAutoLock lock1(m_step_dtime_mutex);
1075 dtime = m_step_dtime;
1078 // Send blocks to clients
1084 //dstream<<"Server steps "<<dtime<<std::endl;
1085 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1088 JMutexAutoLock lock1(m_step_dtime_mutex);
1089 m_step_dtime -= dtime;
1096 m_uptime.set(m_uptime.get() + dtime);
1100 Update m_time_of_day
1103 m_time_counter += dtime;
1104 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1105 u32 units = (u32)(m_time_counter*speed);
1106 m_time_counter -= (f32)units / speed;
1107 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1109 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1112 Send to clients at constant intervals
1115 m_time_of_day_send_timer -= dtime;
1116 if(m_time_of_day_send_timer < 0.0)
1118 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1120 //JMutexAutoLock envlock(m_env_mutex);
1121 JMutexAutoLock conlock(m_con_mutex);
1123 for(core::map<u16, RemoteClient*>::Iterator
1124 i = m_clients.getIterator();
1125 i.atEnd() == false; i++)
1127 RemoteClient *client = i.getNode()->getValue();
1128 //Player *player = m_env.getPlayer(client->peer_id);
1130 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1131 m_time_of_day.get());
1133 m_con.Send(client->peer_id, 0, data, true);
1139 // Process connection's timeouts
1140 JMutexAutoLock lock2(m_con_mutex);
1141 m_con.RunTimeouts(dtime);
1145 // This has to be called so that the client list gets synced
1146 // with the peer list of the connection
1147 handlePeerChanges();
1152 // This also runs Map's timers
1153 JMutexAutoLock lock(m_env_mutex);
1164 m_liquid_transform_timer += dtime;
1165 if(m_liquid_transform_timer >= 1.00)
1167 m_liquid_transform_timer -= 1.00;
1169 JMutexAutoLock lock(m_env_mutex);
1171 core::map<v3s16, MapBlock*> modified_blocks;
1172 m_env.getMap().transformLiquids(modified_blocks);
1177 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1178 ServerMap &map = ((ServerMap&)m_env.getMap());
1179 map.updateLighting(modified_blocks, lighting_modified_blocks);
1181 // Add blocks modified by lighting to modified_blocks
1182 for(core::map<v3s16, MapBlock*>::Iterator
1183 i = lighting_modified_blocks.getIterator();
1184 i.atEnd() == false; i++)
1186 MapBlock *block = i.getNode()->getValue();
1187 modified_blocks.insert(block->getPos(), block);
1191 Set the modified blocks unsent for all the clients
1194 JMutexAutoLock lock2(m_con_mutex);
1196 for(core::map<u16, RemoteClient*>::Iterator
1197 i = m_clients.getIterator();
1198 i.atEnd() == false; i++)
1200 RemoteClient *client = i.getNode()->getValue();
1202 if(modified_blocks.size() > 0)
1204 // Remove block from sent history
1205 client->SetBlocksNotSent(modified_blocks);
1210 // Periodically print some info
1212 float &counter = m_print_info_timer;
1218 JMutexAutoLock lock2(m_con_mutex);
1220 for(core::map<u16, RemoteClient*>::Iterator
1221 i = m_clients.getIterator();
1222 i.atEnd() == false; i++)
1224 //u16 peer_id = i.getNode()->getKey();
1225 RemoteClient *client = i.getNode()->getValue();
1226 Player *player = m_env.getPlayer(client->peer_id);
1227 std::cout<<player->getName()<<"\t";
1228 client->PrintInfo(std::cout);
1233 //if(g_settings.getBool("enable_experimental"))
1237 Check added and deleted active objects
1240 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1242 JMutexAutoLock envlock(m_env_mutex);
1243 JMutexAutoLock conlock(m_con_mutex);
1245 // Radius inside which objects are active
1248 for(core::map<u16, RemoteClient*>::Iterator
1249 i = m_clients.getIterator();
1250 i.atEnd() == false; i++)
1252 RemoteClient *client = i.getNode()->getValue();
1253 Player *player = m_env.getPlayer(client->peer_id);
1256 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1257 <<" has no associated player"<<std::endl;
1260 v3s16 pos = floatToInt(player->getPosition(), BS);
1262 core::map<u16, bool> removed_objects;
1263 core::map<u16, bool> added_objects;
1264 m_env.getRemovedActiveObjects(pos, radius,
1265 client->m_known_objects, removed_objects);
1266 m_env.getAddedActiveObjects(pos, radius,
1267 client->m_known_objects, added_objects);
1269 // Ignore if nothing happened
1270 if(removed_objects.size() == 0 && added_objects.size() == 0)
1272 //dstream<<"INFO: active objects: none changed"<<std::endl;
1276 std::string data_buffer;
1280 // Handle removed objects
1281 writeU16((u8*)buf, removed_objects.size());
1282 data_buffer.append(buf, 2);
1283 for(core::map<u16, bool>::Iterator
1284 i = removed_objects.getIterator();
1285 i.atEnd()==false; i++)
1288 u16 id = i.getNode()->getKey();
1289 ServerActiveObject* obj = m_env.getActiveObject(id);
1291 // Add to data buffer for sending
1292 writeU16((u8*)buf, i.getNode()->getKey());
1293 data_buffer.append(buf, 2);
1295 // Remove from known objects
1296 client->m_known_objects.remove(i.getNode()->getKey());
1298 if(obj && obj->m_known_by_count > 0)
1299 obj->m_known_by_count--;
1302 // Handle added objects
1303 writeU16((u8*)buf, added_objects.size());
1304 data_buffer.append(buf, 2);
1305 for(core::map<u16, bool>::Iterator
1306 i = added_objects.getIterator();
1307 i.atEnd()==false; i++)
1310 u16 id = i.getNode()->getKey();
1311 ServerActiveObject* obj = m_env.getActiveObject(id);
1314 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1316 dstream<<"WARNING: "<<__FUNCTION_NAME
1317 <<": NULL object"<<std::endl;
1319 type = obj->getType();
1321 // Add to data buffer for sending
1322 writeU16((u8*)buf, id);
1323 data_buffer.append(buf, 2);
1324 writeU8((u8*)buf, type);
1325 data_buffer.append(buf, 1);
1328 data_buffer.append(serializeLongString(
1329 obj->getClientInitializationData()));
1331 data_buffer.append(serializeLongString(""));
1333 // Add to known objects
1334 client->m_known_objects.insert(i.getNode()->getKey(), false);
1337 obj->m_known_by_count++;
1341 SharedBuffer<u8> reply(2 + data_buffer.size());
1342 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1343 memcpy((char*)&reply[2], data_buffer.c_str(),
1344 data_buffer.size());
1346 m_con.Send(client->peer_id, 0, reply, true);
1348 dstream<<"INFO: Server: Sent object remove/add: "
1349 <<removed_objects.size()<<" removed, "
1350 <<added_objects.size()<<" added, "
1351 <<"packet size is "<<reply.getSize()<<std::endl;
1356 Collect a list of all the objects known by the clients
1357 and report it back to the environment.
1360 core::map<u16, bool> all_known_objects;
1362 for(core::map<u16, RemoteClient*>::Iterator
1363 i = m_clients.getIterator();
1364 i.atEnd() == false; i++)
1366 RemoteClient *client = i.getNode()->getValue();
1367 // Go through all known objects of client
1368 for(core::map<u16, bool>::Iterator
1369 i = client->m_known_objects.getIterator();
1370 i.atEnd()==false; i++)
1372 u16 id = i.getNode()->getKey();
1373 all_known_objects[id] = true;
1377 m_env.setKnownActiveObjects(whatever);
1383 Send object messages
1386 JMutexAutoLock envlock(m_env_mutex);
1387 JMutexAutoLock conlock(m_con_mutex);
1390 // Value = data sent by object
1391 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1393 // Get active object messages from environment
1396 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1400 core::list<ActiveObjectMessage>* message_list = NULL;
1401 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1402 n = buffered_messages.find(aom.id);
1405 message_list = new core::list<ActiveObjectMessage>;
1406 buffered_messages.insert(aom.id, message_list);
1410 message_list = n->getValue();
1412 message_list->push_back(aom);
1415 // Route data to every client
1416 for(core::map<u16, RemoteClient*>::Iterator
1417 i = m_clients.getIterator();
1418 i.atEnd()==false; i++)
1420 RemoteClient *client = i.getNode()->getValue();
1421 std::string reliable_data;
1422 std::string unreliable_data;
1423 // Go through all objects in message buffer
1424 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1425 j = buffered_messages.getIterator();
1426 j.atEnd()==false; j++)
1428 // If object is not known by client, skip it
1429 u16 id = j.getNode()->getKey();
1430 if(client->m_known_objects.find(id) == NULL)
1432 // Get message list of object
1433 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1434 // Go through every message
1435 for(core::list<ActiveObjectMessage>::Iterator
1436 k = list->begin(); k != list->end(); k++)
1438 // Compose the full new data with header
1439 ActiveObjectMessage aom = *k;
1440 std::string new_data;
1443 writeU16((u8*)&buf[0], aom.id);
1444 new_data.append(buf, 2);
1446 new_data += serializeString(aom.datastring);
1447 // Add data to buffer
1449 reliable_data += new_data;
1451 unreliable_data += new_data;
1455 reliable_data and unreliable_data are now ready.
1458 if(reliable_data.size() > 0)
1460 SharedBuffer<u8> reply(2 + reliable_data.size());
1461 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1462 memcpy((char*)&reply[2], reliable_data.c_str(),
1463 reliable_data.size());
1465 m_con.Send(client->peer_id, 0, reply, true);
1467 if(unreliable_data.size() > 0)
1469 SharedBuffer<u8> reply(2 + unreliable_data.size());
1470 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1471 memcpy((char*)&reply[2], unreliable_data.c_str(),
1472 unreliable_data.size());
1473 // Send as unreliable
1474 m_con.Send(client->peer_id, 0, reply, false);
1477 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1479 dstream<<"INFO: Server: Size of object message data: "
1480 <<"reliable: "<<reliable_data.size()
1481 <<", unreliable: "<<unreliable_data.size()
1486 // Clear buffered_messages
1487 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1488 i = buffered_messages.getIterator();
1489 i.atEnd()==false; i++)
1491 delete i.getNode()->getValue();
1495 } // enable_experimental
1498 Send queued-for-sending map edit events.
1501 while(m_unsent_map_edit_queue.size() != 0)
1503 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1505 if(event->type == MEET_ADDNODE)
1507 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1508 sendAddNode(event->p, event->n, event->already_known_by_peer);
1510 else if(event->type == MEET_REMOVENODE)
1512 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1513 sendRemoveNode(event->p, event->already_known_by_peer);
1515 else if(event->type == MEET_OTHER)
1517 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1522 dstream<<"WARNING: Server: Unknown MapEditEvent "
1523 <<((u32)event->type)<<std::endl;
1531 Send object positions
1532 TODO: Get rid of MapBlockObjects
1535 float &counter = m_objectdata_timer;
1537 if(counter >= g_settings.getFloat("objectdata_interval"))
1539 JMutexAutoLock lock1(m_env_mutex);
1540 JMutexAutoLock lock2(m_con_mutex);
1541 SendObjectData(counter);
1551 JMutexAutoLock envlock(m_env_mutex);
1552 JMutexAutoLock conlock(m_con_mutex);
1554 core::map<v3s16, MapBlock*> changed_blocks;
1555 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1557 for(core::map<v3s16, MapBlock*>::Iterator
1558 i = changed_blocks.getIterator();
1559 i.atEnd() == false; i++)
1561 MapBlock *block = i.getNode()->getValue();
1563 for(core::map<u16, RemoteClient*>::Iterator
1564 i = m_clients.getIterator();
1565 i.atEnd()==false; i++)
1567 RemoteClient *client = i.getNode()->getValue();
1568 client->SetBlockNotSent(block->getPos());
1574 Trigger emergethread (it somehow gets to a non-triggered but
1575 bysy state sometimes)
1578 float &counter = m_emergethread_trigger_timer;
1584 m_emergethread.trigger();
1590 float &counter = m_savemap_timer;
1592 if(counter >= g_settings.getFloat("server_map_save_interval"))
1596 JMutexAutoLock lock(m_env_mutex);
1598 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1600 // Save only changed parts
1601 m_env.getMap().save(true);
1603 // Delete unused sectors
1604 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1605 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1606 if(deleted_count > 0)
1608 dout_server<<"Server: Unloaded "<<deleted_count
1609 <<" sectors from memory"<<std::endl;
1613 m_env.serializePlayers(m_mapsavedir);
1619 void Server::Receive()
1621 DSTACK(__FUNCTION_NAME);
1622 u32 data_maxsize = 10000;
1623 Buffer<u8> data(data_maxsize);
1628 JMutexAutoLock conlock(m_con_mutex);
1629 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1632 // This has to be called so that the client list gets synced
1633 // with the peer list of the connection
1634 handlePeerChanges();
1636 ProcessData(*data, datasize, peer_id);
1638 catch(con::InvalidIncomingDataException &e)
1640 derr_server<<"Server::Receive(): "
1641 "InvalidIncomingDataException: what()="
1642 <<e.what()<<std::endl;
1644 catch(con::PeerNotFoundException &e)
1646 //NOTE: This is not needed anymore
1648 // The peer has been disconnected.
1649 // Find the associated player and remove it.
1651 /*JMutexAutoLock envlock(m_env_mutex);
1653 dout_server<<"ServerThread: peer_id="<<peer_id
1654 <<" has apparently closed connection. "
1655 <<"Removing player."<<std::endl;
1657 m_env.removePlayer(peer_id);*/
1661 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1663 DSTACK(__FUNCTION_NAME);
1664 // Environment is locked first.
1665 JMutexAutoLock envlock(m_env_mutex);
1666 JMutexAutoLock conlock(m_con_mutex);
1670 peer = m_con.GetPeer(peer_id);
1672 catch(con::PeerNotFoundException &e)
1674 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1675 <<peer_id<<" not found"<<std::endl;
1679 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1687 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1689 if(command == TOSERVER_INIT)
1691 // [0] u16 TOSERVER_INIT
1692 // [2] u8 SER_FMT_VER_HIGHEST
1693 // [3] u8[20] player_name
1698 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1699 <<peer->id<<std::endl;
1701 // First byte after command is maximum supported
1702 // serialization version
1703 u8 client_max = data[2];
1704 u8 our_max = SER_FMT_VER_HIGHEST;
1705 // Use the highest version supported by both
1706 u8 deployed = core::min_(client_max, our_max);
1707 // If it's lower than the lowest supported, give up.
1708 if(deployed < SER_FMT_VER_LOWEST)
1709 deployed = SER_FMT_VER_INVALID;
1711 //peer->serialization_version = deployed;
1712 getClient(peer->id)->pending_serialization_version = deployed;
1714 if(deployed == SER_FMT_VER_INVALID)
1716 derr_server<<DTIME<<"Server: Cannot negotiate "
1717 "serialization version with peer "
1718 <<peer_id<<std::endl;
1727 const u32 playername_size = 20;
1728 char playername[playername_size];
1729 for(u32 i=0; i<playername_size-1; i++)
1731 playername[i] = data[3+i];
1733 playername[playername_size-1] = 0;
1736 Player *player = emergePlayer(playername, "", peer_id);
1737 //Player *player = m_env.getPlayer(peer_id);
1740 // DEBUG: Test serialization
1741 std::ostringstream test_os;
1742 player->serialize(test_os);
1743 dstream<<"Player serialization test: \""<<test_os.str()
1745 std::istringstream test_is(test_os.str());
1746 player->deSerialize(test_is);
1749 // If failed, cancel
1752 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1753 <<": failed to emerge player"<<std::endl;
1758 // If a client is already connected to the player, cancel
1759 if(player->peer_id != 0)
1761 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1762 <<" tried to connect to "
1763 "an already connected player (peer_id="
1764 <<player->peer_id<<")"<<std::endl;
1767 // Set client of player
1768 player->peer_id = peer_id;
1771 // Check if player doesn't exist
1773 throw con::InvalidIncomingDataException
1774 ("Server::ProcessData(): INIT: Player doesn't exist");
1776 /*// update name if it was supplied
1777 if(datasize >= 20+3)
1780 player->updateName((const char*)&data[3]);
1783 // Now answer with a TOCLIENT_INIT
1785 SharedBuffer<u8> reply(2+1+6+8);
1786 writeU16(&reply[0], TOCLIENT_INIT);
1787 writeU8(&reply[2], deployed);
1788 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1789 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1792 m_con.Send(peer_id, 0, reply, true);
1796 if(command == TOSERVER_INIT2)
1798 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1799 <<peer->id<<std::endl;
1802 getClient(peer->id)->serialization_version
1803 = getClient(peer->id)->pending_serialization_version;
1806 Send some initialization data
1809 // Send player info to all players
1812 // Send inventory to player
1813 SendInventory(peer->id);
1817 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1818 m_time_of_day.get());
1819 m_con.Send(peer->id, 0, data, true);
1822 // Send information about server to player in chat
1823 SendChatMessage(peer_id, getStatusString());
1825 // Send information about joining in chat
1827 std::wstring name = L"unknown";
1828 Player *player = m_env.getPlayer(peer_id);
1830 name = narrow_to_wide(player->getName());
1832 std::wstring message;
1835 message += L" joined game";
1836 BroadcastChatMessage(message);
1842 if(peer_ser_ver == SER_FMT_VER_INVALID)
1844 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1845 " serialization format invalid or not initialized."
1846 " Skipping incoming command="<<command<<std::endl;
1850 Player *player = m_env.getPlayer(peer_id);
1853 derr_server<<"Server::ProcessData(): Cancelling: "
1854 "No player for peer_id="<<peer_id
1858 if(command == TOSERVER_PLAYERPOS)
1860 if(datasize < 2+12+12+4+4)
1864 v3s32 ps = readV3S32(&data[start+2]);
1865 v3s32 ss = readV3S32(&data[start+2+12]);
1866 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1867 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1868 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1869 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1870 pitch = wrapDegrees(pitch);
1871 yaw = wrapDegrees(yaw);
1872 player->setPosition(position);
1873 player->setSpeed(speed);
1874 player->setPitch(pitch);
1875 player->setYaw(yaw);
1877 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1878 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1879 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1881 else if(command == TOSERVER_GOTBLOCKS)
1894 u16 count = data[2];
1895 for(u16 i=0; i<count; i++)
1897 if((s16)datasize < 2+1+(i+1)*6)
1898 throw con::InvalidIncomingDataException
1899 ("GOTBLOCKS length is too short");
1900 v3s16 p = readV3S16(&data[2+1+i*6]);
1901 /*dstream<<"Server: GOTBLOCKS ("
1902 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1903 RemoteClient *client = getClient(peer_id);
1904 client->GotBlock(p);
1907 else if(command == TOSERVER_DELETEDBLOCKS)
1920 u16 count = data[2];
1921 for(u16 i=0; i<count; i++)
1923 if((s16)datasize < 2+1+(i+1)*6)
1924 throw con::InvalidIncomingDataException
1925 ("DELETEDBLOCKS length is too short");
1926 v3s16 p = readV3S16(&data[2+1+i*6]);
1927 /*dstream<<"Server: DELETEDBLOCKS ("
1928 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1929 RemoteClient *client = getClient(peer_id);
1930 client->SetBlockNotSent(p);
1933 else if(command == TOSERVER_CLICK_OBJECT)
1940 [2] u8 button (0=left, 1=right)
1945 u8 button = readU8(&data[2]);
1947 p.X = readS16(&data[3]);
1948 p.Y = readS16(&data[5]);
1949 p.Z = readS16(&data[7]);
1950 s16 id = readS16(&data[9]);
1951 //u16 item_i = readU16(&data[11]);
1953 MapBlock *block = NULL;
1956 block = m_env.getMap().getBlockNoCreate(p);
1958 catch(InvalidPositionException &e)
1960 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1964 MapBlockObject *obj = block->getObject(id);
1968 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1972 //TODO: Check that object is reasonably close
1977 InventoryList *ilist = player->inventory.getList("main");
1978 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1981 // Skip if inventory has no free space
1982 if(ilist->getUsedSlots() == ilist->getSize())
1984 dout_server<<"Player inventory has no free space"<<std::endl;
1989 Create the inventory item
1991 InventoryItem *item = NULL;
1992 // If it is an item-object, take the item from it
1993 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1995 item = ((ItemObject*)obj)->createInventoryItem();
1997 // Else create an item of the object
2000 item = new MapBlockObjectItem
2001 (obj->getInventoryString());
2004 // Add to inventory and send inventory
2005 ilist->addItem(item);
2006 SendInventory(player->peer_id);
2009 // Remove from block
2010 block->removeObject(id);
2013 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2021 [2] u8 button (0=left, 1=right)
2025 u8 button = readU8(&data[2]);
2026 u16 id = readS16(&data[3]);
2027 //u16 item_i = readU16(&data[11]);
2029 ServerActiveObject *obj = m_env.getActiveObject(id);
2033 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2038 //TODO: Check that object is reasonably close
2040 // Left click, pick object up (usually)
2043 InventoryList *ilist = player->inventory.getList("main");
2044 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2047 // Skip if inventory has no free space
2048 if(ilist->getUsedSlots() == ilist->getSize())
2050 dout_server<<"Player inventory has no free space"<<std::endl;
2054 // Skip if object has been removed
2059 Create the inventory item
2061 InventoryItem *item = obj->createPickedUpItem();
2065 // Add to inventory and send inventory
2066 ilist->addItem(item);
2067 SendInventory(player->peer_id);
2069 // Remove object from environment
2070 obj->m_removed = true;
2075 else if(command == TOSERVER_GROUND_ACTION)
2083 [3] v3s16 nodepos_undersurface
2084 [9] v3s16 nodepos_abovesurface
2089 2: stop digging (all parameters ignored)
2090 3: digging completed
2092 u8 action = readU8(&data[2]);
2094 p_under.X = readS16(&data[3]);
2095 p_under.Y = readS16(&data[5]);
2096 p_under.Z = readS16(&data[7]);
2098 p_over.X = readS16(&data[9]);
2099 p_over.Y = readS16(&data[11]);
2100 p_over.Z = readS16(&data[13]);
2101 u16 item_i = readU16(&data[15]);
2103 //TODO: Check that target is reasonably close
2111 NOTE: This can be used in the future to check if
2112 somebody is cheating, by checking the timing.
2119 else if(action == 2)
2122 RemoteClient *client = getClient(peer->id);
2123 JMutexAutoLock digmutex(client->m_dig_mutex);
2124 client->m_dig_tool_item = -1;
2129 3: Digging completed
2131 else if(action == 3)
2133 // Mandatory parameter; actually used for nothing
2134 core::map<v3s16, MapBlock*> modified_blocks;
2137 u8 mineral = MINERAL_NONE;
2139 bool cannot_remove_node = false;
2143 MapNode n = m_env.getMap().getNode(p_under);
2145 mineral = n.getMineral();
2146 // Get material at position
2148 // If not yet cancelled
2149 if(cannot_remove_node == false)
2151 // If it's not diggable, do nothing
2152 if(content_diggable(material) == false)
2154 derr_server<<"Server: Not finishing digging: "
2155 <<"Node not diggable"
2157 cannot_remove_node = true;
2160 // If not yet cancelled
2161 if(cannot_remove_node == false)
2163 // Get node metadata
2164 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2165 if(meta && meta->nodeRemovalDisabled() == true)
2167 derr_server<<"Server: Not finishing digging: "
2168 <<"Node metadata disables removal"
2170 cannot_remove_node = true;
2174 catch(InvalidPositionException &e)
2176 derr_server<<"Server: Not finishing digging: Node not found."
2177 <<" Adding block to emerge queue."
2179 m_emerge_queue.addBlock(peer_id,
2180 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2181 cannot_remove_node = true;
2185 If node can't be removed, set block to be re-sent to
2188 if(cannot_remove_node)
2190 derr_server<<"Server: Not finishing digging."<<std::endl;
2192 // Client probably has wrong data.
2193 // Set block not sent, so that client will get
2195 dstream<<"Client "<<peer_id<<" tried to dig "
2196 <<"node; but node cannot be removed."
2197 <<" setting MapBlock not sent."<<std::endl;
2198 RemoteClient *client = getClient(peer_id);
2199 v3s16 blockpos = getNodeBlockPos(p_under);
2200 client->SetBlockNotSent(blockpos);
2206 Send the removal to all other clients.
2207 - If other player is close, send REMOVENODE
2208 - Otherwise set blocks not sent
2210 core::list<u16> far_players;
2211 sendRemoveNode(p_under, peer_id, &far_players, 100);
2214 Update and send inventory
2217 if(g_settings.getBool("creative_mode") == false)
2222 InventoryList *mlist = player->inventory.getList("main");
2225 InventoryItem *item = mlist->getItem(item_i);
2226 if(item && (std::string)item->getName() == "ToolItem")
2228 ToolItem *titem = (ToolItem*)item;
2229 std::string toolname = titem->getToolName();
2231 // Get digging properties for material and tool
2232 DiggingProperties prop =
2233 getDiggingProperties(material, toolname);
2235 if(prop.diggable == false)
2237 derr_server<<"Server: WARNING: Player digged"
2238 <<" with impossible material + tool"
2239 <<" combination"<<std::endl;
2242 bool weared_out = titem->addWear(prop.wear);
2246 mlist->deleteItem(item_i);
2252 Add dug item to inventory
2255 InventoryItem *item = NULL;
2257 if(mineral != MINERAL_NONE)
2258 item = getDiggedMineralItem(mineral);
2263 std::string &dug_s = content_features(material).dug_item;
2266 std::istringstream is(dug_s, std::ios::binary);
2267 item = InventoryItem::deSerialize(is);
2273 // Add a item to inventory
2274 player->inventory.addItem("main", item);
2277 SendInventory(player->peer_id);
2283 (this takes some time so it is done after the quick stuff)
2285 m_ignore_map_edit_events = true;
2286 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2287 m_ignore_map_edit_events = false;
2290 Set blocks not sent to far players
2292 for(core::list<u16>::Iterator
2293 i = far_players.begin();
2294 i != far_players.end(); i++)
2297 RemoteClient *client = getClient(peer_id);
2300 client->SetBlocksNotSent(modified_blocks);
2307 else if(action == 1)
2310 InventoryList *ilist = player->inventory.getList("main");
2315 InventoryItem *item = ilist->getItem(item_i);
2317 // If there is no item, it is not possible to add it anywhere
2322 Handle material items
2324 if(std::string("MaterialItem") == item->getName())
2327 // Don't add a node if this is not a free space
2328 MapNode n2 = m_env.getMap().getNode(p_over);
2329 if(content_buildable_to(n2.d) == false)
2331 // Client probably has wrong data.
2332 // Set block not sent, so that client will get
2334 dstream<<"Client "<<peer_id<<" tried to place"
2335 <<" node in invalid position; setting"
2336 <<" MapBlock not sent."<<std::endl;
2337 RemoteClient *client = getClient(peer_id);
2338 v3s16 blockpos = getNodeBlockPos(p_over);
2339 client->SetBlockNotSent(blockpos);
2343 catch(InvalidPositionException &e)
2345 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2346 <<" Adding block to emerge queue."
2348 m_emerge_queue.addBlock(peer_id,
2349 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2353 // Reset build time counter
2354 getClient(peer->id)->m_time_from_building = 0.0;
2357 MaterialItem *mitem = (MaterialItem*)item;
2359 n.d = mitem->getMaterial();
2360 if(content_features(n.d).wall_mounted)
2361 n.dir = packDir(p_under - p_over);
2366 core::list<u16> far_players;
2367 sendAddNode(p_over, n, 0, &far_players, 100);
2372 InventoryList *ilist = player->inventory.getList("main");
2373 if(g_settings.getBool("creative_mode") == false && ilist)
2375 // Remove from inventory and send inventory
2376 if(mitem->getCount() == 1)
2377 ilist->deleteItem(item_i);
2381 SendInventory(peer_id);
2387 This takes some time so it is done after the quick stuff
2389 core::map<v3s16, MapBlock*> modified_blocks;
2390 m_ignore_map_edit_events = true;
2391 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2392 m_ignore_map_edit_events = false;
2395 Set blocks not sent to far players
2397 for(core::list<u16>::Iterator
2398 i = far_players.begin();
2399 i != far_players.end(); i++)
2402 RemoteClient *client = getClient(peer_id);
2405 client->SetBlocksNotSent(modified_blocks);
2409 Calculate special events
2412 /*if(n.d == CONTENT_MESE)
2415 for(s16 z=-1; z<=1; z++)
2416 for(s16 y=-1; y<=1; y++)
2417 for(s16 x=-1; x<=1; x++)
2424 Place other item (not a block)
2428 v3s16 blockpos = getNodeBlockPos(p_over);
2431 Check that the block is loaded so that the item
2432 can properly be added to the static list too
2434 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2437 derr_server<<"Error while placing object: "
2438 "block not found"<<std::endl;
2442 dout_server<<"Placing a miscellaneous item on map"
2445 // Calculate a position for it
2446 v3f pos = intToFloat(p_over, BS);
2448 pos.Y -= BS*0.25; // let it drop a bit
2450 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2451 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2456 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2460 derr_server<<"WARNING: item resulted in NULL object, "
2461 <<"not placing onto map"
2466 // Add the object to the environment
2467 m_env.addActiveObject(obj);
2469 dout_server<<"Placed object"<<std::endl;
2471 // If item has count<=1, delete it
2472 if(item->getCount() <= 1)
2474 InventoryList *ilist = player->inventory.getList("main");
2475 if(g_settings.getBool("creative_mode") == false && ilist)
2477 // Remove from inventory and send inventory
2478 ilist->deleteItem(item_i);
2480 SendInventory(peer_id);
2483 // Else decrement it
2488 SendInventory(peer_id);
2496 Catch invalid actions
2500 derr_server<<"WARNING: Server: Invalid action "
2501 <<action<<std::endl;
2505 else if(command == TOSERVER_RELEASE)
2514 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2517 else if(command == TOSERVER_SIGNTEXT)
2526 std::string datastring((char*)&data[2], datasize-2);
2527 std::istringstream is(datastring, std::ios_base::binary);
2530 is.read((char*)buf, 6);
2531 v3s16 blockpos = readV3S16(buf);
2532 is.read((char*)buf, 2);
2533 s16 id = readS16(buf);
2534 is.read((char*)buf, 2);
2535 u16 textlen = readU16(buf);
2537 for(u16 i=0; i<textlen; i++)
2539 is.read((char*)buf, 1);
2540 text += (char)buf[0];
2543 MapBlock *block = NULL;
2546 block = m_env.getMap().getBlockNoCreate(blockpos);
2548 catch(InvalidPositionException &e)
2550 derr_server<<"Error while setting sign text: "
2551 "block not found"<<std::endl;
2555 MapBlockObject *obj = block->getObject(id);
2558 derr_server<<"Error while setting sign text: "
2559 "object not found"<<std::endl;
2563 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2565 derr_server<<"Error while setting sign text: "
2566 "object is not a sign"<<std::endl;
2570 ((SignObject*)obj)->setText(text);
2572 obj->getBlock()->setChangedFlag();
2574 else if(command == TOSERVER_SIGNNODETEXT)
2582 std::string datastring((char*)&data[2], datasize-2);
2583 std::istringstream is(datastring, std::ios_base::binary);
2586 is.read((char*)buf, 6);
2587 v3s16 p = readV3S16(buf);
2588 is.read((char*)buf, 2);
2589 u16 textlen = readU16(buf);
2591 for(u16 i=0; i<textlen; i++)
2593 is.read((char*)buf, 1);
2594 text += (char)buf[0];
2597 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2600 if(meta->typeId() != CONTENT_SIGN_WALL)
2602 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2603 signmeta->setText(text);
2605 v3s16 blockpos = getNodeBlockPos(p);
2606 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2609 block->setChangedFlag();
2612 for(core::map<u16, RemoteClient*>::Iterator
2613 i = m_clients.getIterator();
2614 i.atEnd()==false; i++)
2616 RemoteClient *client = i.getNode()->getValue();
2617 client->SetBlockNotSent(blockpos);
2620 else if(command == TOSERVER_INVENTORY_ACTION)
2622 /*// Ignore inventory changes if in creative mode
2623 if(g_settings.getBool("creative_mode") == true)
2625 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2629 // Strip command and create a stream
2630 std::string datastring((char*)&data[2], datasize-2);
2631 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2632 std::istringstream is(datastring, std::ios_base::binary);
2634 InventoryAction *a = InventoryAction::deSerialize(is);
2639 c.current_player = player;
2642 Handle craftresult specially if not in creative mode
2644 bool disable_action = false;
2645 if(a->getType() == IACTION_MOVE
2646 && g_settings.getBool("creative_mode") == false)
2648 IMoveAction *ma = (IMoveAction*)a;
2649 if(ma->to_inv == "current_player" &&
2650 ma->from_inv == "current_player")
2652 InventoryList *rlist = player->inventory.getList("craftresult");
2654 InventoryList *clist = player->inventory.getList("craft");
2656 InventoryList *mlist = player->inventory.getList("main");
2659 Craftresult is no longer preview if something
2662 if(ma->to_list == "craftresult"
2663 && ma->from_list != "craftresult")
2665 // If it currently is a preview, remove
2667 if(player->craftresult_is_preview)
2669 rlist->deleteItem(0);
2671 player->craftresult_is_preview = false;
2674 Crafting takes place if this condition is true.
2676 if(player->craftresult_is_preview &&
2677 ma->from_list == "craftresult")
2679 player->craftresult_is_preview = false;
2680 clist->decrementMaterials(1);
2683 If the craftresult is placed on itself, move it to
2684 main inventory instead of doing the action
2686 if(ma->to_list == "craftresult"
2687 && ma->from_list == "craftresult")
2689 disable_action = true;
2691 InventoryItem *item1 = rlist->changeItem(0, NULL);
2692 mlist->addItem(item1);
2697 if(disable_action == false)
2699 // Feed action to player inventory
2707 SendInventory(player->peer_id);
2712 dstream<<"TOSERVER_INVENTORY_ACTION: "
2713 <<"InventoryAction::deSerialize() returned NULL"
2717 else if(command == TOSERVER_CHAT_MESSAGE)
2725 std::string datastring((char*)&data[2], datasize-2);
2726 std::istringstream is(datastring, std::ios_base::binary);
2729 is.read((char*)buf, 2);
2730 u16 len = readU16(buf);
2732 std::wstring message;
2733 for(u16 i=0; i<len; i++)
2735 is.read((char*)buf, 2);
2736 message += (wchar_t)readU16(buf);
2739 // Get player name of this client
2740 std::wstring name = narrow_to_wide(player->getName());
2742 // Line to send to players
2744 // Whether to send to the player that sent the line
2745 bool send_to_sender = false;
2746 // Whether to send to other players
2747 bool send_to_others = false;
2750 std::wstring commandprefix = L"/#";
2751 if(message.substr(0, commandprefix.size()) == commandprefix)
2753 line += L"Server: ";
2755 message = message.substr(commandprefix.size());
2756 // Get player name as narrow string
2757 std::string name_s = player->getName();
2758 // Convert message to narrow string
2759 std::string message_s = wide_to_narrow(message);
2760 // Operator is the single name defined in config.
2761 std::string operator_name = g_settings.get("name");
2762 bool is_operator = (operator_name != "" &&
2763 wide_to_narrow(name) == operator_name);
2764 bool valid_command = false;
2765 if(message_s == "help")
2767 line += L"-!- Available commands: ";
2771 line += L"shutdown setting ";
2776 send_to_sender = true;
2777 valid_command = true;
2779 else if(message_s == "status")
2781 line = getStatusString();
2782 send_to_sender = true;
2783 valid_command = true;
2785 else if(is_operator)
2787 if(message_s == "shutdown")
2789 dstream<<DTIME<<" Server: Operator requested shutdown."
2791 m_shutdown_requested.set(true);
2793 line += L"*** Server shutting down (operator request)";
2794 send_to_sender = true;
2795 valid_command = true;
2797 else if(message_s.substr(0,8) == "setting ")
2799 std::string confline = message_s.substr(8);
2800 g_settings.parseConfigLine(confline);
2801 line += L"-!- Setting changed.";
2802 send_to_sender = true;
2803 valid_command = true;
2807 if(valid_command == false)
2809 line += L"-!- Invalid command: " + message;
2810 send_to_sender = true;
2821 send_to_others = true;
2826 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2829 Send the message to clients
2831 for(core::map<u16, RemoteClient*>::Iterator
2832 i = m_clients.getIterator();
2833 i.atEnd() == false; i++)
2835 // Get client and check that it is valid
2836 RemoteClient *client = i.getNode()->getValue();
2837 assert(client->peer_id == i.getNode()->getKey());
2838 if(client->serialization_version == SER_FMT_VER_INVALID)
2842 bool sender_selected = (peer_id == client->peer_id);
2843 if(sender_selected == true && send_to_sender == false)
2845 if(sender_selected == false && send_to_others == false)
2848 SendChatMessage(client->peer_id, line);
2854 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2855 "unknown command "<<command<<std::endl;
2859 catch(SendFailedException &e)
2861 derr_server<<"Server::ProcessData(): SendFailedException: "
2867 void Server::onMapEditEvent(MapEditEvent *event)
2869 dstream<<"Server::onMapEditEvent()"<<std::endl;
2870 if(m_ignore_map_edit_events)
2872 MapEditEvent *e = event->clone();
2873 m_unsent_map_edit_queue.push_back(e);
2876 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2878 if(id == "current_player")
2880 assert(c->current_player);
2881 return &(c->current_player->inventory);
2885 std::string id0 = fn.next(":");
2887 if(id0 == "nodemeta")
2890 p.X = stoi(fn.next(","));
2891 p.Y = stoi(fn.next(","));
2892 p.Z = stoi(fn.next(","));
2893 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2895 return meta->getInventory();
2896 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2897 <<"no metadata found"<<std::endl;
2901 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2904 void Server::inventoryModified(InventoryContext *c, std::string id)
2906 if(id == "current_player")
2908 assert(c->current_player);
2910 SendInventory(c->current_player->peer_id);
2915 std::string id0 = fn.next(":");
2917 if(id0 == "nodemeta")
2920 p.X = stoi(fn.next(","));
2921 p.Y = stoi(fn.next(","));
2922 p.Z = stoi(fn.next(","));
2923 v3s16 blockpos = getNodeBlockPos(p);
2925 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2927 meta->inventoryModified();
2929 for(core::map<u16, RemoteClient*>::Iterator
2930 i = m_clients.getIterator();
2931 i.atEnd()==false; i++)
2933 RemoteClient *client = i.getNode()->getValue();
2934 client->SetBlockNotSent(blockpos);
2940 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2943 core::list<PlayerInfo> Server::getPlayerInfo()
2945 DSTACK(__FUNCTION_NAME);
2946 JMutexAutoLock envlock(m_env_mutex);
2947 JMutexAutoLock conlock(m_con_mutex);
2949 core::list<PlayerInfo> list;
2951 core::list<Player*> players = m_env.getPlayers();
2953 core::list<Player*>::Iterator i;
2954 for(i = players.begin();
2955 i != players.end(); i++)
2959 Player *player = *i;
2962 con::Peer *peer = m_con.GetPeer(player->peer_id);
2963 // Copy info from peer to info struct
2965 info.address = peer->address;
2966 info.avg_rtt = peer->avg_rtt;
2968 catch(con::PeerNotFoundException &e)
2970 // Set dummy peer info
2972 info.address = Address(0,0,0,0,0);
2976 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2977 info.position = player->getPosition();
2979 list.push_back(info);
2986 void Server::peerAdded(con::Peer *peer)
2988 DSTACK(__FUNCTION_NAME);
2989 dout_server<<"Server::peerAdded(): peer->id="
2990 <<peer->id<<std::endl;
2993 c.type = PEER_ADDED;
2994 c.peer_id = peer->id;
2996 m_peer_change_queue.push_back(c);
2999 void Server::deletingPeer(con::Peer *peer, bool timeout)
3001 DSTACK(__FUNCTION_NAME);
3002 dout_server<<"Server::deletingPeer(): peer->id="
3003 <<peer->id<<", timeout="<<timeout<<std::endl;
3006 c.type = PEER_REMOVED;
3007 c.peer_id = peer->id;
3008 c.timeout = timeout;
3009 m_peer_change_queue.push_back(c);
3012 void Server::SendObjectData(float dtime)
3014 DSTACK(__FUNCTION_NAME);
3016 core::map<v3s16, bool> stepped_blocks;
3018 for(core::map<u16, RemoteClient*>::Iterator
3019 i = m_clients.getIterator();
3020 i.atEnd() == false; i++)
3022 u16 peer_id = i.getNode()->getKey();
3023 RemoteClient *client = i.getNode()->getValue();
3024 assert(client->peer_id == peer_id);
3026 if(client->serialization_version == SER_FMT_VER_INVALID)
3029 client->SendObjectData(this, dtime, stepped_blocks);
3033 void Server::SendPlayerInfos()
3035 DSTACK(__FUNCTION_NAME);
3037 //JMutexAutoLock envlock(m_env_mutex);
3039 // Get connected players
3040 core::list<Player*> players = m_env.getPlayers(true);
3042 u32 player_count = players.getSize();
3043 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3045 SharedBuffer<u8> data(datasize);
3046 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3049 core::list<Player*>::Iterator i;
3050 for(i = players.begin();
3051 i != players.end(); i++)
3053 Player *player = *i;
3055 /*dstream<<"Server sending player info for player with "
3056 "peer_id="<<player->peer_id<<std::endl;*/
3058 writeU16(&data[start], player->peer_id);
3059 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3060 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3061 start += 2+PLAYERNAME_SIZE;
3064 //JMutexAutoLock conlock(m_con_mutex);
3067 m_con.SendToAll(0, data, true);
3070 void Server::SendInventory(u16 peer_id)
3072 DSTACK(__FUNCTION_NAME);
3074 Player* player = m_env.getPlayer(peer_id);
3078 Calculate crafting stuff
3080 if(g_settings.getBool("creative_mode") == false)
3082 InventoryList *clist = player->inventory.getList("craft");
3083 InventoryList *rlist = player->inventory.getList("craftresult");
3085 if(rlist->getUsedSlots() == 0)
3086 player->craftresult_is_preview = true;
3088 if(rlist && player->craftresult_is_preview)
3090 rlist->clearItems();
3092 if(clist && rlist && player->craftresult_is_preview)
3094 InventoryItem *items[9];
3095 for(u16 i=0; i<9; i++)
3097 items[i] = clist->getItem(i);
3106 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3107 if(checkItemCombination(items, specs))
3109 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3118 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3119 if(checkItemCombination(items, specs))
3121 rlist->addItem(new CraftItem("Stick", 4));
3130 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3131 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3132 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3133 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3134 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3135 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3136 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3137 if(checkItemCombination(items, specs))
3139 //rlist->addItem(new MapBlockObjectItem("Sign"));
3140 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3149 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3150 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3151 if(checkItemCombination(items, specs))
3153 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3162 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3163 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3164 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3165 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3166 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3167 if(checkItemCombination(items, specs))
3169 rlist->addItem(new ToolItem("WPick", 0));
3178 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3179 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3180 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3181 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3182 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3183 if(checkItemCombination(items, specs))
3185 rlist->addItem(new ToolItem("STPick", 0));
3194 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3195 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3196 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3197 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3198 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3199 if(checkItemCombination(items, specs))
3201 rlist->addItem(new ToolItem("SteelPick", 0));
3210 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3211 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3212 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3213 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3214 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3215 if(checkItemCombination(items, specs))
3217 rlist->addItem(new ToolItem("MesePick", 0));
3226 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3227 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3228 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3229 if(checkItemCombination(items, specs))
3231 rlist->addItem(new ToolItem("WShovel", 0));
3240 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3241 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3242 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3243 if(checkItemCombination(items, specs))
3245 rlist->addItem(new ToolItem("STShovel", 0));
3254 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3255 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3256 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3257 if(checkItemCombination(items, specs))
3259 rlist->addItem(new ToolItem("SteelShovel", 0));
3268 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3269 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3270 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3271 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3272 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3273 if(checkItemCombination(items, specs))
3275 rlist->addItem(new ToolItem("WAxe", 0));
3284 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3285 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3286 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3287 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3288 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3289 if(checkItemCombination(items, specs))
3291 rlist->addItem(new ToolItem("STAxe", 0));
3300 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3301 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3302 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3303 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3304 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3305 if(checkItemCombination(items, specs))
3307 rlist->addItem(new ToolItem("SteelAxe", 0));
3316 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3317 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3318 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3319 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3320 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3321 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3322 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3323 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3324 if(checkItemCombination(items, specs))
3326 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3335 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3336 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3337 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3338 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3339 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3340 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3341 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3342 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3343 if(checkItemCombination(items, specs))
3345 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3354 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3355 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3356 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3357 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3358 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3359 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3360 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3361 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3362 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3363 if(checkItemCombination(items, specs))
3365 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3371 } // if creative_mode == false
3377 std::ostringstream os;
3378 //os.imbue(std::locale("C"));
3380 player->inventory.serialize(os);
3382 std::string s = os.str();
3384 SharedBuffer<u8> data(s.size()+2);
3385 writeU16(&data[0], TOCLIENT_INVENTORY);
3386 memcpy(&data[2], s.c_str(), s.size());
3389 m_con.Send(peer_id, 0, data, true);
3392 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3394 DSTACK(__FUNCTION_NAME);
3396 std::ostringstream os(std::ios_base::binary);
3400 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3401 os.write((char*)buf, 2);
3404 writeU16(buf, message.size());
3405 os.write((char*)buf, 2);
3408 for(u32 i=0; i<message.size(); i++)
3412 os.write((char*)buf, 2);
3416 std::string s = os.str();
3417 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3419 m_con.Send(peer_id, 0, data, true);
3422 void Server::BroadcastChatMessage(const std::wstring &message)
3424 for(core::map<u16, RemoteClient*>::Iterator
3425 i = m_clients.getIterator();
3426 i.atEnd() == false; i++)
3428 // Get client and check that it is valid
3429 RemoteClient *client = i.getNode()->getValue();
3430 assert(client->peer_id == i.getNode()->getKey());
3431 if(client->serialization_version == SER_FMT_VER_INVALID)
3434 SendChatMessage(client->peer_id, message);
3438 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3439 core::list<u16> *far_players, float far_d_nodes)
3441 float maxd = far_d_nodes*BS;
3442 v3f p_f = intToFloat(p, BS);
3446 SharedBuffer<u8> reply(replysize);
3447 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3448 writeS16(&reply[2], p.X);
3449 writeS16(&reply[4], p.Y);
3450 writeS16(&reply[6], p.Z);
3452 for(core::map<u16, RemoteClient*>::Iterator
3453 i = m_clients.getIterator();
3454 i.atEnd() == false; i++)
3456 // Get client and check that it is valid
3457 RemoteClient *client = i.getNode()->getValue();
3458 assert(client->peer_id == i.getNode()->getKey());
3459 if(client->serialization_version == SER_FMT_VER_INVALID)
3462 // Don't send if it's the same one
3463 if(client->peer_id == ignore_id)
3469 Player *player = m_env.getPlayer(client->peer_id);
3472 // If player is far away, only set modified blocks not sent
3473 v3f player_pos = player->getPosition();
3474 if(player_pos.getDistanceFrom(p_f) > maxd)
3476 far_players->push_back(client->peer_id);
3483 m_con.Send(client->peer_id, 0, reply, true);
3487 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3488 core::list<u16> *far_players, float far_d_nodes)
3490 float maxd = far_d_nodes*BS;
3491 v3f p_f = intToFloat(p, BS);
3493 for(core::map<u16, RemoteClient*>::Iterator
3494 i = m_clients.getIterator();
3495 i.atEnd() == false; i++)
3497 // Get client and check that it is valid
3498 RemoteClient *client = i.getNode()->getValue();
3499 assert(client->peer_id == i.getNode()->getKey());
3500 if(client->serialization_version == SER_FMT_VER_INVALID)
3503 // Don't send if it's the same one
3504 if(client->peer_id == ignore_id)
3510 Player *player = m_env.getPlayer(client->peer_id);
3513 // If player is far away, only set modified blocks not sent
3514 v3f player_pos = player->getPosition();
3515 if(player_pos.getDistanceFrom(p_f) > maxd)
3517 far_players->push_back(client->peer_id);
3524 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3525 SharedBuffer<u8> reply(replysize);
3526 writeU16(&reply[0], TOCLIENT_ADDNODE);
3527 writeS16(&reply[2], p.X);
3528 writeS16(&reply[4], p.Y);
3529 writeS16(&reply[6], p.Z);
3530 n.serialize(&reply[8], client->serialization_version);
3533 m_con.Send(client->peer_id, 0, reply, true);
3537 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3539 DSTACK(__FUNCTION_NAME);
3541 Create a packet with the block in the right format
3544 std::ostringstream os(std::ios_base::binary);
3545 block->serialize(os, ver);
3546 std::string s = os.str();
3547 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3549 u32 replysize = 8 + blockdata.getSize();
3550 SharedBuffer<u8> reply(replysize);
3551 v3s16 p = block->getPos();
3552 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3553 writeS16(&reply[2], p.X);
3554 writeS16(&reply[4], p.Y);
3555 writeS16(&reply[6], p.Z);
3556 memcpy(&reply[8], *blockdata, blockdata.getSize());
3558 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3559 <<": \tpacket size: "<<replysize<<std::endl;*/
3564 m_con.Send(peer_id, 1, reply, true);
3567 void Server::SendBlocks(float dtime)
3569 DSTACK(__FUNCTION_NAME);
3571 JMutexAutoLock envlock(m_env_mutex);
3572 JMutexAutoLock conlock(m_con_mutex);
3574 //TimeTaker timer("Server::SendBlocks");
3576 core::array<PrioritySortedBlockTransfer> queue;
3578 s32 total_sending = 0;
3580 for(core::map<u16, RemoteClient*>::Iterator
3581 i = m_clients.getIterator();
3582 i.atEnd() == false; i++)
3584 RemoteClient *client = i.getNode()->getValue();
3585 assert(client->peer_id == i.getNode()->getKey());
3587 total_sending += client->SendingCount();
3589 if(client->serialization_version == SER_FMT_VER_INVALID)
3592 client->GetNextBlocks(this, dtime, queue);
3596 // Lowest priority number comes first.
3597 // Lowest is most important.
3600 for(u32 i=0; i<queue.size(); i++)
3602 //TODO: Calculate limit dynamically
3603 if(total_sending >= g_settings.getS32
3604 ("max_simultaneous_block_sends_server_total"))
3607 PrioritySortedBlockTransfer q = queue[i];
3609 MapBlock *block = NULL;
3612 block = m_env.getMap().getBlockNoCreate(q.pos);
3614 catch(InvalidPositionException &e)
3619 RemoteClient *client = getClient(q.peer_id);
3621 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3623 client->SentBlock(q.pos);
3630 RemoteClient* Server::getClient(u16 peer_id)
3632 DSTACK(__FUNCTION_NAME);
3633 //JMutexAutoLock lock(m_con_mutex);
3634 core::map<u16, RemoteClient*>::Node *n;
3635 n = m_clients.find(peer_id);
3636 // A client should exist for all peers
3638 return n->getValue();
3641 std::wstring Server::getStatusString()
3643 std::wostringstream os(std::ios_base::binary);
3646 os<<L"uptime="<<m_uptime.get();
3647 // Information about clients
3649 for(core::map<u16, RemoteClient*>::Iterator
3650 i = m_clients.getIterator();
3651 i.atEnd() == false; i++)
3653 // Get client and check that it is valid
3654 RemoteClient *client = i.getNode()->getValue();
3655 assert(client->peer_id == i.getNode()->getKey());
3656 if(client->serialization_version == SER_FMT_VER_INVALID)
3659 Player *player = m_env.getPlayer(client->peer_id);
3660 // Get name of player
3661 std::wstring name = L"unknown";
3663 name = narrow_to_wide(player->getName());
3664 // Add name to information string
3668 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3669 os<<" WARNING: Map saving is disabled."<<std::endl;
3674 void setCreativeInventory(Player *player)
3676 player->resetInventory();
3678 // Give some good picks
3680 InventoryItem *item = new ToolItem("STPick", 0);
3681 void* r = player->inventory.addItem("main", item);
3685 InventoryItem *item = new ToolItem("MesePick", 0);
3686 void* r = player->inventory.addItem("main", item);
3694 // CONTENT_IGNORE-terminated list
3695 u8 material_items[] = {
3704 CONTENT_WATERSOURCE,
3712 u8 *mip = material_items;
3713 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3715 if(*mip == CONTENT_IGNORE)
3718 InventoryItem *item = new MaterialItem(*mip, 1);
3719 player->inventory.addItem("main", item);
3725 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3728 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3729 player->inventory.addItem("main", item);
3732 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3734 // Skip some materials
3735 if(i == CONTENT_WATER || i == CONTENT_TORCH
3736 || i == CONTENT_COALSTONE)
3739 InventoryItem *item = new MaterialItem(i, 1);
3740 player->inventory.addItem("main", item);
3746 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3747 void* r = player->inventory.addItem("main", item);
3752 Player *Server::emergePlayer(const char *name, const char *password,
3756 Try to get an existing player
3758 Player *player = m_env.getPlayer(name);
3761 // If player is already connected, cancel
3762 if(player->peer_id != 0)
3764 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3769 player->peer_id = peer_id;
3771 // Reset inventory to creative if in creative mode
3772 if(g_settings.getBool("creative_mode"))
3774 setCreativeInventory(player);
3781 If player with the wanted peer_id already exists, cancel.
3783 if(m_env.getPlayer(peer_id) != NULL)
3785 dstream<<"emergePlayer(): Player with wrong name but same"
3786 " peer_id already exists"<<std::endl;
3794 player = new ServerRemotePlayer();
3795 //player->peer_id = c.peer_id;
3796 //player->peer_id = PEER_ID_INEXISTENT;
3797 player->peer_id = peer_id;
3798 player->updateName(name);
3804 dstream<<"Server: Finding spawn place for player \""
3805 <<player->getName()<<"\""<<std::endl;
3809 player->setPosition(intToFloat(v3s16(
3816 s16 groundheight = 0;
3818 // Try to find a good place a few times
3819 for(s32 i=0; i<1000; i++)
3822 // We're going to try to throw the player to this position
3823 nodepos = v2s16(-range + (myrand()%(range*2)),
3824 -range + (myrand()%(range*2)));
3825 v2s16 sectorpos = getNodeSectorPos(nodepos);
3826 // Get sector (NOTE: Don't get because it's slow)
3827 //m_env.getMap().emergeSector(sectorpos);
3828 // Get ground height at point (fallbacks to heightmap function)
3829 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3830 // Don't go underwater
3831 if(groundheight < WATER_LEVEL)
3833 //dstream<<"-> Underwater"<<std::endl;
3836 // Don't go to high places
3837 if(groundheight > WATER_LEVEL + 4)
3839 //dstream<<"-> Underwater"<<std::endl;
3843 // Found a good place
3844 dstream<<"Searched through "<<i<<" places."<<std::endl;
3849 // If no suitable place was not found, go above water at least.
3850 if(groundheight < WATER_LEVEL)
3851 groundheight = WATER_LEVEL;
3853 player->setPosition(intToFloat(v3s16(
3855 groundheight + 5, // Accomodate mud
3861 Add player to environment
3864 m_env.addPlayer(player);
3867 Add stuff to inventory
3870 if(g_settings.getBool("creative_mode"))
3872 setCreativeInventory(player);
3877 InventoryItem *item = new ToolItem("WPick", 32000);
3878 void* r = player->inventory.addItem("main", item);
3882 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3883 void* r = player->inventory.addItem("main", item);
3887 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3888 void* r = player->inventory.addItem("main", item);
3892 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3893 void* r = player->inventory.addItem("main", item);
3897 InventoryItem *item = new CraftItem("Stick", 4);
3898 void* r = player->inventory.addItem("main", item);
3902 InventoryItem *item = new ToolItem("WPick", 32000);
3903 void* r = player->inventory.addItem("main", item);
3907 InventoryItem *item = new ToolItem("STPick", 32000);
3908 void* r = player->inventory.addItem("main", item);
3911 /*// Give some lights
3913 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3914 bool r = player->inventory.addItem("main", item);
3918 for(u16 i=0; i<4; i++)
3920 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3921 bool r = player->inventory.addItem("main", item);
3924 /*// Give some other stuff
3926 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3927 bool r = player->inventory.addItem("main", item);
3934 } // create new player
3937 void Server::handlePeerChange(PeerChange &c)
3939 JMutexAutoLock envlock(m_env_mutex);
3940 JMutexAutoLock conlock(m_con_mutex);
3942 if(c.type == PEER_ADDED)
3949 core::map<u16, RemoteClient*>::Node *n;
3950 n = m_clients.find(c.peer_id);
3951 // The client shouldn't already exist
3955 RemoteClient *client = new RemoteClient();
3956 client->peer_id = c.peer_id;
3957 m_clients.insert(client->peer_id, client);
3960 else if(c.type == PEER_REMOVED)
3967 core::map<u16, RemoteClient*>::Node *n;
3968 n = m_clients.find(c.peer_id);
3969 // The client should exist
3972 // Collect information about leaving in chat
3973 std::wstring message;
3975 std::wstring name = L"unknown";
3976 Player *player = m_env.getPlayer(c.peer_id);
3978 name = narrow_to_wide(player->getName());
3982 message += L" left game";
3984 message += L" (timed out)";
3989 m_env.removePlayer(c.peer_id);
3992 // Set player client disconnected
3994 Player *player = m_env.getPlayer(c.peer_id);
3996 player->peer_id = 0;
4000 delete m_clients[c.peer_id];
4001 m_clients.remove(c.peer_id);
4003 // Send player info to all remaining clients
4006 // Send leave chat message to all remaining clients
4007 BroadcastChatMessage(message);
4016 void Server::handlePeerChanges()
4018 while(m_peer_change_queue.size() > 0)
4020 PeerChange c = m_peer_change_queue.pop_front();
4022 dout_server<<"Server: Handling peer change: "
4023 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4026 handlePeerChange(c);
4030 void dedicated_server_loop(Server &server, bool &kill)
4032 DSTACK(__FUNCTION_NAME);
4034 std::cout<<DTIME<<std::endl;
4035 std::cout<<"========================"<<std::endl;
4036 std::cout<<"Running dedicated server"<<std::endl;
4037 std::cout<<"========================"<<std::endl;
4038 std::cout<<std::endl;
4042 // This is kind of a hack but can be done like this
4043 // because server.step() is very light
4047 if(server.getShutdownRequested() || kill)
4049 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4053 static int counter = 0;
4059 core::list<PlayerInfo> list = server.getPlayerInfo();
4060 core::list<PlayerInfo>::Iterator i;
4061 static u32 sum_old = 0;
4062 u32 sum = PIChecksum(list);
4065 std::cout<<DTIME<<"Player info:"<<std::endl;
4066 for(i=list.begin(); i!=list.end(); i++)
4068 i->PrintLine(&std::cout);