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);
1229 std::cout<<player->getName()<<"\t";
1230 client->PrintInfo(std::cout);
1235 //if(g_settings.getBool("enable_experimental"))
1239 Check added and deleted active objects
1242 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1244 JMutexAutoLock envlock(m_env_mutex);
1245 JMutexAutoLock conlock(m_con_mutex);
1247 // Radius inside which objects are active
1250 for(core::map<u16, RemoteClient*>::Iterator
1251 i = m_clients.getIterator();
1252 i.atEnd() == false; i++)
1254 RemoteClient *client = i.getNode()->getValue();
1255 Player *player = m_env.getPlayer(client->peer_id);
1258 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1259 <<" has no associated player"<<std::endl;
1262 v3s16 pos = floatToInt(player->getPosition(), BS);
1264 core::map<u16, bool> removed_objects;
1265 core::map<u16, bool> added_objects;
1266 m_env.getRemovedActiveObjects(pos, radius,
1267 client->m_known_objects, removed_objects);
1268 m_env.getAddedActiveObjects(pos, radius,
1269 client->m_known_objects, added_objects);
1271 // Ignore if nothing happened
1272 if(removed_objects.size() == 0 && added_objects.size() == 0)
1274 //dstream<<"INFO: active objects: none changed"<<std::endl;
1278 std::string data_buffer;
1282 // Handle removed objects
1283 writeU16((u8*)buf, removed_objects.size());
1284 data_buffer.append(buf, 2);
1285 for(core::map<u16, bool>::Iterator
1286 i = removed_objects.getIterator();
1287 i.atEnd()==false; i++)
1290 u16 id = i.getNode()->getKey();
1291 ServerActiveObject* obj = m_env.getActiveObject(id);
1293 // Add to data buffer for sending
1294 writeU16((u8*)buf, i.getNode()->getKey());
1295 data_buffer.append(buf, 2);
1297 // Remove from known objects
1298 client->m_known_objects.remove(i.getNode()->getKey());
1300 if(obj && obj->m_known_by_count > 0)
1301 obj->m_known_by_count--;
1304 // Handle added objects
1305 writeU16((u8*)buf, added_objects.size());
1306 data_buffer.append(buf, 2);
1307 for(core::map<u16, bool>::Iterator
1308 i = added_objects.getIterator();
1309 i.atEnd()==false; i++)
1312 u16 id = i.getNode()->getKey();
1313 ServerActiveObject* obj = m_env.getActiveObject(id);
1316 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1318 dstream<<"WARNING: "<<__FUNCTION_NAME
1319 <<": NULL object"<<std::endl;
1321 type = obj->getType();
1323 // Add to data buffer for sending
1324 writeU16((u8*)buf, id);
1325 data_buffer.append(buf, 2);
1326 writeU8((u8*)buf, type);
1327 data_buffer.append(buf, 1);
1330 data_buffer.append(serializeLongString(
1331 obj->getClientInitializationData()));
1333 data_buffer.append(serializeLongString(""));
1335 // Add to known objects
1336 client->m_known_objects.insert(i.getNode()->getKey(), false);
1339 obj->m_known_by_count++;
1343 SharedBuffer<u8> reply(2 + data_buffer.size());
1344 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1345 memcpy((char*)&reply[2], data_buffer.c_str(),
1346 data_buffer.size());
1348 m_con.Send(client->peer_id, 0, reply, true);
1350 dstream<<"INFO: Server: Sent object remove/add: "
1351 <<removed_objects.size()<<" removed, "
1352 <<added_objects.size()<<" added, "
1353 <<"packet size is "<<reply.getSize()<<std::endl;
1358 Collect a list of all the objects known by the clients
1359 and report it back to the environment.
1362 core::map<u16, bool> all_known_objects;
1364 for(core::map<u16, RemoteClient*>::Iterator
1365 i = m_clients.getIterator();
1366 i.atEnd() == false; i++)
1368 RemoteClient *client = i.getNode()->getValue();
1369 // Go through all known objects of client
1370 for(core::map<u16, bool>::Iterator
1371 i = client->m_known_objects.getIterator();
1372 i.atEnd()==false; i++)
1374 u16 id = i.getNode()->getKey();
1375 all_known_objects[id] = true;
1379 m_env.setKnownActiveObjects(whatever);
1385 Send object messages
1388 JMutexAutoLock envlock(m_env_mutex);
1389 JMutexAutoLock conlock(m_con_mutex);
1392 // Value = data sent by object
1393 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1395 // Get active object messages from environment
1398 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1402 core::list<ActiveObjectMessage>* message_list = NULL;
1403 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1404 n = buffered_messages.find(aom.id);
1407 message_list = new core::list<ActiveObjectMessage>;
1408 buffered_messages.insert(aom.id, message_list);
1412 message_list = n->getValue();
1414 message_list->push_back(aom);
1417 // Route data to every client
1418 for(core::map<u16, RemoteClient*>::Iterator
1419 i = m_clients.getIterator();
1420 i.atEnd()==false; i++)
1422 RemoteClient *client = i.getNode()->getValue();
1423 std::string reliable_data;
1424 std::string unreliable_data;
1425 // Go through all objects in message buffer
1426 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1427 j = buffered_messages.getIterator();
1428 j.atEnd()==false; j++)
1430 // If object is not known by client, skip it
1431 u16 id = j.getNode()->getKey();
1432 if(client->m_known_objects.find(id) == NULL)
1434 // Get message list of object
1435 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1436 // Go through every message
1437 for(core::list<ActiveObjectMessage>::Iterator
1438 k = list->begin(); k != list->end(); k++)
1440 // Compose the full new data with header
1441 ActiveObjectMessage aom = *k;
1442 std::string new_data;
1445 writeU16((u8*)&buf[0], aom.id);
1446 new_data.append(buf, 2);
1448 new_data += serializeString(aom.datastring);
1449 // Add data to buffer
1451 reliable_data += new_data;
1453 unreliable_data += new_data;
1457 reliable_data and unreliable_data are now ready.
1460 if(reliable_data.size() > 0)
1462 SharedBuffer<u8> reply(2 + reliable_data.size());
1463 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1464 memcpy((char*)&reply[2], reliable_data.c_str(),
1465 reliable_data.size());
1467 m_con.Send(client->peer_id, 0, reply, true);
1469 if(unreliable_data.size() > 0)
1471 SharedBuffer<u8> reply(2 + unreliable_data.size());
1472 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1473 memcpy((char*)&reply[2], unreliable_data.c_str(),
1474 unreliable_data.size());
1475 // Send as unreliable
1476 m_con.Send(client->peer_id, 0, reply, false);
1479 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1481 dstream<<"INFO: Server: Size of object message data: "
1482 <<"reliable: "<<reliable_data.size()
1483 <<", unreliable: "<<unreliable_data.size()
1488 // Clear buffered_messages
1489 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1490 i = buffered_messages.getIterator();
1491 i.atEnd()==false; i++)
1493 delete i.getNode()->getValue();
1497 } // enable_experimental
1500 Send queued-for-sending map edit events.
1503 while(m_unsent_map_edit_queue.size() != 0)
1505 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1507 if(event->type == MEET_ADDNODE)
1509 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1510 sendAddNode(event->p, event->n, event->already_known_by_peer);
1512 else if(event->type == MEET_REMOVENODE)
1514 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1515 sendRemoveNode(event->p, event->already_known_by_peer);
1517 else if(event->type == MEET_OTHER)
1519 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1524 dstream<<"WARNING: Server: Unknown MapEditEvent "
1525 <<((u32)event->type)<<std::endl;
1533 Send object positions
1534 TODO: Get rid of MapBlockObjects
1537 float &counter = m_objectdata_timer;
1539 if(counter >= g_settings.getFloat("objectdata_interval"))
1541 JMutexAutoLock lock1(m_env_mutex);
1542 JMutexAutoLock lock2(m_con_mutex);
1543 SendObjectData(counter);
1553 JMutexAutoLock envlock(m_env_mutex);
1554 JMutexAutoLock conlock(m_con_mutex);
1556 core::map<v3s16, MapBlock*> changed_blocks;
1557 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1559 for(core::map<v3s16, MapBlock*>::Iterator
1560 i = changed_blocks.getIterator();
1561 i.atEnd() == false; i++)
1563 MapBlock *block = i.getNode()->getValue();
1565 for(core::map<u16, RemoteClient*>::Iterator
1566 i = m_clients.getIterator();
1567 i.atEnd()==false; i++)
1569 RemoteClient *client = i.getNode()->getValue();
1570 client->SetBlockNotSent(block->getPos());
1576 Trigger emergethread (it somehow gets to a non-triggered but
1577 bysy state sometimes)
1580 float &counter = m_emergethread_trigger_timer;
1586 m_emergethread.trigger();
1592 float &counter = m_savemap_timer;
1594 if(counter >= g_settings.getFloat("server_map_save_interval"))
1598 JMutexAutoLock lock(m_env_mutex);
1600 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1602 // Save only changed parts
1603 m_env.getMap().save(true);
1605 // Delete unused sectors
1606 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1607 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1608 if(deleted_count > 0)
1610 dout_server<<"Server: Unloaded "<<deleted_count
1611 <<" sectors from memory"<<std::endl;
1615 m_env.serializePlayers(m_mapsavedir);
1621 void Server::Receive()
1623 DSTACK(__FUNCTION_NAME);
1624 u32 data_maxsize = 10000;
1625 Buffer<u8> data(data_maxsize);
1630 JMutexAutoLock conlock(m_con_mutex);
1631 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1634 // This has to be called so that the client list gets synced
1635 // with the peer list of the connection
1636 handlePeerChanges();
1638 ProcessData(*data, datasize, peer_id);
1640 catch(con::InvalidIncomingDataException &e)
1642 derr_server<<"Server::Receive(): "
1643 "InvalidIncomingDataException: what()="
1644 <<e.what()<<std::endl;
1646 catch(con::PeerNotFoundException &e)
1648 //NOTE: This is not needed anymore
1650 // The peer has been disconnected.
1651 // Find the associated player and remove it.
1653 /*JMutexAutoLock envlock(m_env_mutex);
1655 dout_server<<"ServerThread: peer_id="<<peer_id
1656 <<" has apparently closed connection. "
1657 <<"Removing player."<<std::endl;
1659 m_env.removePlayer(peer_id);*/
1663 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1665 DSTACK(__FUNCTION_NAME);
1666 // Environment is locked first.
1667 JMutexAutoLock envlock(m_env_mutex);
1668 JMutexAutoLock conlock(m_con_mutex);
1672 peer = m_con.GetPeer(peer_id);
1674 catch(con::PeerNotFoundException &e)
1676 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1677 <<peer_id<<" not found"<<std::endl;
1681 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1689 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1691 if(command == TOSERVER_INIT)
1693 // [0] u16 TOSERVER_INIT
1694 // [2] u8 SER_FMT_VER_HIGHEST
1695 // [3] u8[20] player_name
1700 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1701 <<peer->id<<std::endl;
1703 // First byte after command is maximum supported
1704 // serialization version
1705 u8 client_max = data[2];
1706 u8 our_max = SER_FMT_VER_HIGHEST;
1707 // Use the highest version supported by both
1708 u8 deployed = core::min_(client_max, our_max);
1709 // If it's lower than the lowest supported, give up.
1710 if(deployed < SER_FMT_VER_LOWEST)
1711 deployed = SER_FMT_VER_INVALID;
1713 //peer->serialization_version = deployed;
1714 getClient(peer->id)->pending_serialization_version = deployed;
1716 if(deployed == SER_FMT_VER_INVALID)
1718 derr_server<<DTIME<<"Server: Cannot negotiate "
1719 "serialization version with peer "
1720 <<peer_id<<std::endl;
1729 const u32 playername_size = 20;
1730 char playername[playername_size];
1731 for(u32 i=0; i<playername_size-1; i++)
1733 playername[i] = data[3+i];
1735 playername[playername_size-1] = 0;
1738 Player *player = emergePlayer(playername, "", peer_id);
1739 //Player *player = m_env.getPlayer(peer_id);
1742 // DEBUG: Test serialization
1743 std::ostringstream test_os;
1744 player->serialize(test_os);
1745 dstream<<"Player serialization test: \""<<test_os.str()
1747 std::istringstream test_is(test_os.str());
1748 player->deSerialize(test_is);
1751 // If failed, cancel
1754 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1755 <<": failed to emerge player"<<std::endl;
1760 // If a client is already connected to the player, cancel
1761 if(player->peer_id != 0)
1763 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1764 <<" tried to connect to "
1765 "an already connected player (peer_id="
1766 <<player->peer_id<<")"<<std::endl;
1769 // Set client of player
1770 player->peer_id = peer_id;
1773 // Check if player doesn't exist
1775 throw con::InvalidIncomingDataException
1776 ("Server::ProcessData(): INIT: Player doesn't exist");
1778 /*// update name if it was supplied
1779 if(datasize >= 20+3)
1782 player->updateName((const char*)&data[3]);
1785 // Now answer with a TOCLIENT_INIT
1787 SharedBuffer<u8> reply(2+1+6+8);
1788 writeU16(&reply[0], TOCLIENT_INIT);
1789 writeU8(&reply[2], deployed);
1790 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1791 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1794 m_con.Send(peer_id, 0, reply, true);
1798 if(command == TOSERVER_INIT2)
1800 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1801 <<peer->id<<std::endl;
1804 getClient(peer->id)->serialization_version
1805 = getClient(peer->id)->pending_serialization_version;
1808 Send some initialization data
1811 // Send player info to all players
1814 // Send inventory to player
1815 SendInventory(peer->id);
1819 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1820 m_time_of_day.get());
1821 m_con.Send(peer->id, 0, data, true);
1824 // Send information about server to player in chat
1825 SendChatMessage(peer_id, getStatusString());
1827 // Send information about joining in chat
1829 std::wstring name = L"unknown";
1830 Player *player = m_env.getPlayer(peer_id);
1832 name = narrow_to_wide(player->getName());
1834 std::wstring message;
1837 message += L" joined game";
1838 BroadcastChatMessage(message);
1844 if(peer_ser_ver == SER_FMT_VER_INVALID)
1846 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1847 " serialization format invalid or not initialized."
1848 " Skipping incoming command="<<command<<std::endl;
1852 Player *player = m_env.getPlayer(peer_id);
1855 derr_server<<"Server::ProcessData(): Cancelling: "
1856 "No player for peer_id="<<peer_id
1860 if(command == TOSERVER_PLAYERPOS)
1862 if(datasize < 2+12+12+4+4)
1866 v3s32 ps = readV3S32(&data[start+2]);
1867 v3s32 ss = readV3S32(&data[start+2+12]);
1868 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1869 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1870 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1871 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1872 pitch = wrapDegrees(pitch);
1873 yaw = wrapDegrees(yaw);
1874 player->setPosition(position);
1875 player->setSpeed(speed);
1876 player->setPitch(pitch);
1877 player->setYaw(yaw);
1879 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1880 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1881 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1883 else if(command == TOSERVER_GOTBLOCKS)
1896 u16 count = data[2];
1897 for(u16 i=0; i<count; i++)
1899 if((s16)datasize < 2+1+(i+1)*6)
1900 throw con::InvalidIncomingDataException
1901 ("GOTBLOCKS length is too short");
1902 v3s16 p = readV3S16(&data[2+1+i*6]);
1903 /*dstream<<"Server: GOTBLOCKS ("
1904 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1905 RemoteClient *client = getClient(peer_id);
1906 client->GotBlock(p);
1909 else if(command == TOSERVER_DELETEDBLOCKS)
1922 u16 count = data[2];
1923 for(u16 i=0; i<count; i++)
1925 if((s16)datasize < 2+1+(i+1)*6)
1926 throw con::InvalidIncomingDataException
1927 ("DELETEDBLOCKS length is too short");
1928 v3s16 p = readV3S16(&data[2+1+i*6]);
1929 /*dstream<<"Server: DELETEDBLOCKS ("
1930 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1931 RemoteClient *client = getClient(peer_id);
1932 client->SetBlockNotSent(p);
1935 else if(command == TOSERVER_CLICK_OBJECT)
1942 [2] u8 button (0=left, 1=right)
1947 u8 button = readU8(&data[2]);
1949 p.X = readS16(&data[3]);
1950 p.Y = readS16(&data[5]);
1951 p.Z = readS16(&data[7]);
1952 s16 id = readS16(&data[9]);
1953 //u16 item_i = readU16(&data[11]);
1955 MapBlock *block = NULL;
1958 block = m_env.getMap().getBlockNoCreate(p);
1960 catch(InvalidPositionException &e)
1962 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1966 MapBlockObject *obj = block->getObject(id);
1970 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1974 //TODO: Check that object is reasonably close
1979 InventoryList *ilist = player->inventory.getList("main");
1980 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1983 // Skip if inventory has no free space
1984 if(ilist->getUsedSlots() == ilist->getSize())
1986 dout_server<<"Player inventory has no free space"<<std::endl;
1991 Create the inventory item
1993 InventoryItem *item = NULL;
1994 // If it is an item-object, take the item from it
1995 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1997 item = ((ItemObject*)obj)->createInventoryItem();
1999 // Else create an item of the object
2002 item = new MapBlockObjectItem
2003 (obj->getInventoryString());
2006 // Add to inventory and send inventory
2007 ilist->addItem(item);
2008 SendInventory(player->peer_id);
2011 // Remove from block
2012 block->removeObject(id);
2015 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2023 [2] u8 button (0=left, 1=right)
2027 u8 button = readU8(&data[2]);
2028 u16 id = readS16(&data[3]);
2029 //u16 item_i = readU16(&data[11]);
2031 ServerActiveObject *obj = m_env.getActiveObject(id);
2035 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2040 //TODO: Check that object is reasonably close
2042 // Left click, pick object up (usually)
2045 InventoryList *ilist = player->inventory.getList("main");
2046 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2049 // Skip if inventory has no free space
2050 if(ilist->getUsedSlots() == ilist->getSize())
2052 dout_server<<"Player inventory has no free space"<<std::endl;
2056 // Skip if object has been removed
2061 Create the inventory item
2063 InventoryItem *item = obj->createPickedUpItem();
2067 // Add to inventory and send inventory
2068 ilist->addItem(item);
2069 SendInventory(player->peer_id);
2071 // Remove object from environment
2072 obj->m_removed = true;
2077 else if(command == TOSERVER_GROUND_ACTION)
2085 [3] v3s16 nodepos_undersurface
2086 [9] v3s16 nodepos_abovesurface
2091 2: stop digging (all parameters ignored)
2092 3: digging completed
2094 u8 action = readU8(&data[2]);
2096 p_under.X = readS16(&data[3]);
2097 p_under.Y = readS16(&data[5]);
2098 p_under.Z = readS16(&data[7]);
2100 p_over.X = readS16(&data[9]);
2101 p_over.Y = readS16(&data[11]);
2102 p_over.Z = readS16(&data[13]);
2103 u16 item_i = readU16(&data[15]);
2105 //TODO: Check that target is reasonably close
2113 NOTE: This can be used in the future to check if
2114 somebody is cheating, by checking the timing.
2121 else if(action == 2)
2124 RemoteClient *client = getClient(peer->id);
2125 JMutexAutoLock digmutex(client->m_dig_mutex);
2126 client->m_dig_tool_item = -1;
2131 3: Digging completed
2133 else if(action == 3)
2135 // Mandatory parameter; actually used for nothing
2136 core::map<v3s16, MapBlock*> modified_blocks;
2139 u8 mineral = MINERAL_NONE;
2141 bool cannot_remove_node = false;
2145 MapNode n = m_env.getMap().getNode(p_under);
2147 mineral = n.getMineral();
2148 // Get material at position
2150 // If not yet cancelled
2151 if(cannot_remove_node == false)
2153 // If it's not diggable, do nothing
2154 if(content_diggable(material) == false)
2156 derr_server<<"Server: Not finishing digging: "
2157 <<"Node not diggable"
2159 cannot_remove_node = true;
2162 // If not yet cancelled
2163 if(cannot_remove_node == false)
2165 // Get node metadata
2166 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2167 if(meta && meta->nodeRemovalDisabled() == true)
2169 derr_server<<"Server: Not finishing digging: "
2170 <<"Node metadata disables removal"
2172 cannot_remove_node = true;
2176 catch(InvalidPositionException &e)
2178 derr_server<<"Server: Not finishing digging: Node not found."
2179 <<" Adding block to emerge queue."
2181 m_emerge_queue.addBlock(peer_id,
2182 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2183 cannot_remove_node = true;
2187 If node can't be removed, set block to be re-sent to
2190 if(cannot_remove_node)
2192 derr_server<<"Server: Not finishing digging."<<std::endl;
2194 // Client probably has wrong data.
2195 // Set block not sent, so that client will get
2197 dstream<<"Client "<<peer_id<<" tried to dig "
2198 <<"node; but node cannot be removed."
2199 <<" setting MapBlock not sent."<<std::endl;
2200 RemoteClient *client = getClient(peer_id);
2201 v3s16 blockpos = getNodeBlockPos(p_under);
2202 client->SetBlockNotSent(blockpos);
2208 Send the removal to all other clients.
2209 - If other player is close, send REMOVENODE
2210 - Otherwise set blocks not sent
2212 core::list<u16> far_players;
2213 sendRemoveNode(p_under, peer_id, &far_players, 100);
2216 Update and send inventory
2219 if(g_settings.getBool("creative_mode") == false)
2224 InventoryList *mlist = player->inventory.getList("main");
2227 InventoryItem *item = mlist->getItem(item_i);
2228 if(item && (std::string)item->getName() == "ToolItem")
2230 ToolItem *titem = (ToolItem*)item;
2231 std::string toolname = titem->getToolName();
2233 // Get digging properties for material and tool
2234 DiggingProperties prop =
2235 getDiggingProperties(material, toolname);
2237 if(prop.diggable == false)
2239 derr_server<<"Server: WARNING: Player digged"
2240 <<" with impossible material + tool"
2241 <<" combination"<<std::endl;
2244 bool weared_out = titem->addWear(prop.wear);
2248 mlist->deleteItem(item_i);
2254 Add dug item to inventory
2257 InventoryItem *item = NULL;
2259 if(mineral != MINERAL_NONE)
2260 item = getDiggedMineralItem(mineral);
2265 std::string &dug_s = content_features(material).dug_item;
2268 std::istringstream is(dug_s, std::ios::binary);
2269 item = InventoryItem::deSerialize(is);
2275 // Add a item to inventory
2276 player->inventory.addItem("main", item);
2279 SendInventory(player->peer_id);
2285 (this takes some time so it is done after the quick stuff)
2287 m_ignore_map_edit_events = true;
2288 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2289 m_ignore_map_edit_events = false;
2292 Set blocks not sent to far players
2294 for(core::list<u16>::Iterator
2295 i = far_players.begin();
2296 i != far_players.end(); i++)
2299 RemoteClient *client = getClient(peer_id);
2302 client->SetBlocksNotSent(modified_blocks);
2309 else if(action == 1)
2312 InventoryList *ilist = player->inventory.getList("main");
2317 InventoryItem *item = ilist->getItem(item_i);
2319 // If there is no item, it is not possible to add it anywhere
2324 Handle material items
2326 if(std::string("MaterialItem") == item->getName())
2329 // Don't add a node if this is not a free space
2330 MapNode n2 = m_env.getMap().getNode(p_over);
2331 if(content_buildable_to(n2.d) == false)
2333 // Client probably has wrong data.
2334 // Set block not sent, so that client will get
2336 dstream<<"Client "<<peer_id<<" tried to place"
2337 <<" node in invalid position; setting"
2338 <<" MapBlock not sent."<<std::endl;
2339 RemoteClient *client = getClient(peer_id);
2340 v3s16 blockpos = getNodeBlockPos(p_over);
2341 client->SetBlockNotSent(blockpos);
2345 catch(InvalidPositionException &e)
2347 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2348 <<" Adding block to emerge queue."
2350 m_emerge_queue.addBlock(peer_id,
2351 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2355 // Reset build time counter
2356 getClient(peer->id)->m_time_from_building = 0.0;
2359 MaterialItem *mitem = (MaterialItem*)item;
2361 n.d = mitem->getMaterial();
2362 if(content_features(n.d).wall_mounted)
2363 n.dir = packDir(p_under - p_over);
2368 core::list<u16> far_players;
2369 sendAddNode(p_over, n, 0, &far_players, 100);
2374 InventoryList *ilist = player->inventory.getList("main");
2375 if(g_settings.getBool("creative_mode") == false && ilist)
2377 // Remove from inventory and send inventory
2378 if(mitem->getCount() == 1)
2379 ilist->deleteItem(item_i);
2383 SendInventory(peer_id);
2389 This takes some time so it is done after the quick stuff
2391 core::map<v3s16, MapBlock*> modified_blocks;
2392 m_ignore_map_edit_events = true;
2393 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2394 m_ignore_map_edit_events = false;
2397 Set blocks not sent to far players
2399 for(core::list<u16>::Iterator
2400 i = far_players.begin();
2401 i != far_players.end(); i++)
2404 RemoteClient *client = getClient(peer_id);
2407 client->SetBlocksNotSent(modified_blocks);
2411 Calculate special events
2414 /*if(n.d == CONTENT_MESE)
2417 for(s16 z=-1; z<=1; z++)
2418 for(s16 y=-1; y<=1; y++)
2419 for(s16 x=-1; x<=1; x++)
2426 Place other item (not a block)
2430 v3s16 blockpos = getNodeBlockPos(p_over);
2433 Check that the block is loaded so that the item
2434 can properly be added to the static list too
2436 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2439 derr_server<<"Error while placing object: "
2440 "block not found"<<std::endl;
2444 dout_server<<"Placing a miscellaneous item on map"
2447 // Calculate a position for it
2448 v3f pos = intToFloat(p_over, BS);
2450 pos.Y -= BS*0.25; // let it drop a bit
2452 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2453 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2458 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2462 derr_server<<"WARNING: item resulted in NULL object, "
2463 <<"not placing onto map"
2468 // Add the object to the environment
2469 m_env.addActiveObject(obj);
2471 dout_server<<"Placed object"<<std::endl;
2473 // If item has count<=1, delete it
2474 if(item->getCount() <= 1)
2476 InventoryList *ilist = player->inventory.getList("main");
2477 if(g_settings.getBool("creative_mode") == false && ilist)
2479 // Remove from inventory and send inventory
2480 ilist->deleteItem(item_i);
2482 SendInventory(peer_id);
2485 // Else decrement it
2490 SendInventory(peer_id);
2498 Catch invalid actions
2502 derr_server<<"WARNING: Server: Invalid action "
2503 <<action<<std::endl;
2507 else if(command == TOSERVER_RELEASE)
2516 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2519 else if(command == TOSERVER_SIGNTEXT)
2528 std::string datastring((char*)&data[2], datasize-2);
2529 std::istringstream is(datastring, std::ios_base::binary);
2532 is.read((char*)buf, 6);
2533 v3s16 blockpos = readV3S16(buf);
2534 is.read((char*)buf, 2);
2535 s16 id = readS16(buf);
2536 is.read((char*)buf, 2);
2537 u16 textlen = readU16(buf);
2539 for(u16 i=0; i<textlen; i++)
2541 is.read((char*)buf, 1);
2542 text += (char)buf[0];
2545 MapBlock *block = NULL;
2548 block = m_env.getMap().getBlockNoCreate(blockpos);
2550 catch(InvalidPositionException &e)
2552 derr_server<<"Error while setting sign text: "
2553 "block not found"<<std::endl;
2557 MapBlockObject *obj = block->getObject(id);
2560 derr_server<<"Error while setting sign text: "
2561 "object not found"<<std::endl;
2565 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2567 derr_server<<"Error while setting sign text: "
2568 "object is not a sign"<<std::endl;
2572 ((SignObject*)obj)->setText(text);
2574 obj->getBlock()->setChangedFlag();
2576 else if(command == TOSERVER_SIGNNODETEXT)
2584 std::string datastring((char*)&data[2], datasize-2);
2585 std::istringstream is(datastring, std::ios_base::binary);
2588 is.read((char*)buf, 6);
2589 v3s16 p = readV3S16(buf);
2590 is.read((char*)buf, 2);
2591 u16 textlen = readU16(buf);
2593 for(u16 i=0; i<textlen; i++)
2595 is.read((char*)buf, 1);
2596 text += (char)buf[0];
2599 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2602 if(meta->typeId() != CONTENT_SIGN_WALL)
2604 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2605 signmeta->setText(text);
2607 v3s16 blockpos = getNodeBlockPos(p);
2608 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2611 block->setChangedFlag();
2614 for(core::map<u16, RemoteClient*>::Iterator
2615 i = m_clients.getIterator();
2616 i.atEnd()==false; i++)
2618 RemoteClient *client = i.getNode()->getValue();
2619 client->SetBlockNotSent(blockpos);
2622 else if(command == TOSERVER_INVENTORY_ACTION)
2624 /*// Ignore inventory changes if in creative mode
2625 if(g_settings.getBool("creative_mode") == true)
2627 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2631 // Strip command and create a stream
2632 std::string datastring((char*)&data[2], datasize-2);
2633 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2634 std::istringstream is(datastring, std::ios_base::binary);
2636 InventoryAction *a = InventoryAction::deSerialize(is);
2641 c.current_player = player;
2644 Handle craftresult specially if not in creative mode
2646 bool disable_action = false;
2647 if(a->getType() == IACTION_MOVE
2648 && g_settings.getBool("creative_mode") == false)
2650 IMoveAction *ma = (IMoveAction*)a;
2651 if(ma->to_inv == "current_player" &&
2652 ma->from_inv == "current_player")
2654 InventoryList *rlist = player->inventory.getList("craftresult");
2656 InventoryList *clist = player->inventory.getList("craft");
2658 InventoryList *mlist = player->inventory.getList("main");
2661 Craftresult is no longer preview if something
2664 if(ma->to_list == "craftresult"
2665 && ma->from_list != "craftresult")
2667 // If it currently is a preview, remove
2669 if(player->craftresult_is_preview)
2671 rlist->deleteItem(0);
2673 player->craftresult_is_preview = false;
2676 Crafting takes place if this condition is true.
2678 if(player->craftresult_is_preview &&
2679 ma->from_list == "craftresult")
2681 player->craftresult_is_preview = false;
2682 clist->decrementMaterials(1);
2685 If the craftresult is placed on itself, move it to
2686 main inventory instead of doing the action
2688 if(ma->to_list == "craftresult"
2689 && ma->from_list == "craftresult")
2691 disable_action = true;
2693 InventoryItem *item1 = rlist->changeItem(0, NULL);
2694 mlist->addItem(item1);
2699 if(disable_action == false)
2701 // Feed action to player inventory
2709 SendInventory(player->peer_id);
2714 dstream<<"TOSERVER_INVENTORY_ACTION: "
2715 <<"InventoryAction::deSerialize() returned NULL"
2719 else if(command == TOSERVER_CHAT_MESSAGE)
2727 std::string datastring((char*)&data[2], datasize-2);
2728 std::istringstream is(datastring, std::ios_base::binary);
2731 is.read((char*)buf, 2);
2732 u16 len = readU16(buf);
2734 std::wstring message;
2735 for(u16 i=0; i<len; i++)
2737 is.read((char*)buf, 2);
2738 message += (wchar_t)readU16(buf);
2741 // Get player name of this client
2742 std::wstring name = narrow_to_wide(player->getName());
2744 // Line to send to players
2746 // Whether to send to the player that sent the line
2747 bool send_to_sender = false;
2748 // Whether to send to other players
2749 bool send_to_others = false;
2752 std::wstring commandprefix = L"/#";
2753 if(message.substr(0, commandprefix.size()) == commandprefix)
2755 line += L"Server: ";
2757 message = message.substr(commandprefix.size());
2758 // Get player name as narrow string
2759 std::string name_s = player->getName();
2760 // Convert message to narrow string
2761 std::string message_s = wide_to_narrow(message);
2762 // Operator is the single name defined in config.
2763 std::string operator_name = g_settings.get("name");
2764 bool is_operator = (operator_name != "" &&
2765 wide_to_narrow(name) == operator_name);
2766 bool valid_command = false;
2767 if(message_s == "help")
2769 line += L"-!- Available commands: ";
2773 line += L"shutdown setting ";
2778 send_to_sender = true;
2779 valid_command = true;
2781 else if(message_s == "status")
2783 line = getStatusString();
2784 send_to_sender = true;
2785 valid_command = true;
2787 else if(is_operator)
2789 if(message_s == "shutdown")
2791 dstream<<DTIME<<" Server: Operator requested shutdown."
2793 m_shutdown_requested.set(true);
2795 line += L"*** Server shutting down (operator request)";
2796 send_to_sender = true;
2797 valid_command = true;
2799 else if(message_s.substr(0,8) == "setting ")
2801 std::string confline = message_s.substr(8);
2802 g_settings.parseConfigLine(confline);
2803 line += L"-!- Setting changed.";
2804 send_to_sender = true;
2805 valid_command = true;
2809 if(valid_command == false)
2811 line += L"-!- Invalid command: " + message;
2812 send_to_sender = true;
2823 send_to_others = true;
2828 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2831 Send the message to clients
2833 for(core::map<u16, RemoteClient*>::Iterator
2834 i = m_clients.getIterator();
2835 i.atEnd() == false; i++)
2837 // Get client and check that it is valid
2838 RemoteClient *client = i.getNode()->getValue();
2839 assert(client->peer_id == i.getNode()->getKey());
2840 if(client->serialization_version == SER_FMT_VER_INVALID)
2844 bool sender_selected = (peer_id == client->peer_id);
2845 if(sender_selected == true && send_to_sender == false)
2847 if(sender_selected == false && send_to_others == false)
2850 SendChatMessage(client->peer_id, line);
2856 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2857 "unknown command "<<command<<std::endl;
2861 catch(SendFailedException &e)
2863 derr_server<<"Server::ProcessData(): SendFailedException: "
2869 void Server::onMapEditEvent(MapEditEvent *event)
2871 dstream<<"Server::onMapEditEvent()"<<std::endl;
2872 if(m_ignore_map_edit_events)
2874 MapEditEvent *e = event->clone();
2875 m_unsent_map_edit_queue.push_back(e);
2878 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2880 if(id == "current_player")
2882 assert(c->current_player);
2883 return &(c->current_player->inventory);
2887 std::string id0 = fn.next(":");
2889 if(id0 == "nodemeta")
2892 p.X = stoi(fn.next(","));
2893 p.Y = stoi(fn.next(","));
2894 p.Z = stoi(fn.next(","));
2895 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2897 return meta->getInventory();
2898 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2899 <<"no metadata found"<<std::endl;
2903 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2906 void Server::inventoryModified(InventoryContext *c, std::string id)
2908 if(id == "current_player")
2910 assert(c->current_player);
2912 SendInventory(c->current_player->peer_id);
2917 std::string id0 = fn.next(":");
2919 if(id0 == "nodemeta")
2922 p.X = stoi(fn.next(","));
2923 p.Y = stoi(fn.next(","));
2924 p.Z = stoi(fn.next(","));
2925 v3s16 blockpos = getNodeBlockPos(p);
2927 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2929 meta->inventoryModified();
2931 for(core::map<u16, RemoteClient*>::Iterator
2932 i = m_clients.getIterator();
2933 i.atEnd()==false; i++)
2935 RemoteClient *client = i.getNode()->getValue();
2936 client->SetBlockNotSent(blockpos);
2942 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2945 core::list<PlayerInfo> Server::getPlayerInfo()
2947 DSTACK(__FUNCTION_NAME);
2948 JMutexAutoLock envlock(m_env_mutex);
2949 JMutexAutoLock conlock(m_con_mutex);
2951 core::list<PlayerInfo> list;
2953 core::list<Player*> players = m_env.getPlayers();
2955 core::list<Player*>::Iterator i;
2956 for(i = players.begin();
2957 i != players.end(); i++)
2961 Player *player = *i;
2964 con::Peer *peer = m_con.GetPeer(player->peer_id);
2965 // Copy info from peer to info struct
2967 info.address = peer->address;
2968 info.avg_rtt = peer->avg_rtt;
2970 catch(con::PeerNotFoundException &e)
2972 // Set dummy peer info
2974 info.address = Address(0,0,0,0,0);
2978 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2979 info.position = player->getPosition();
2981 list.push_back(info);
2988 void Server::peerAdded(con::Peer *peer)
2990 DSTACK(__FUNCTION_NAME);
2991 dout_server<<"Server::peerAdded(): peer->id="
2992 <<peer->id<<std::endl;
2995 c.type = PEER_ADDED;
2996 c.peer_id = peer->id;
2998 m_peer_change_queue.push_back(c);
3001 void Server::deletingPeer(con::Peer *peer, bool timeout)
3003 DSTACK(__FUNCTION_NAME);
3004 dout_server<<"Server::deletingPeer(): peer->id="
3005 <<peer->id<<", timeout="<<timeout<<std::endl;
3008 c.type = PEER_REMOVED;
3009 c.peer_id = peer->id;
3010 c.timeout = timeout;
3011 m_peer_change_queue.push_back(c);
3014 void Server::SendObjectData(float dtime)
3016 DSTACK(__FUNCTION_NAME);
3018 core::map<v3s16, bool> stepped_blocks;
3020 for(core::map<u16, RemoteClient*>::Iterator
3021 i = m_clients.getIterator();
3022 i.atEnd() == false; i++)
3024 u16 peer_id = i.getNode()->getKey();
3025 RemoteClient *client = i.getNode()->getValue();
3026 assert(client->peer_id == peer_id);
3028 if(client->serialization_version == SER_FMT_VER_INVALID)
3031 client->SendObjectData(this, dtime, stepped_blocks);
3035 void Server::SendPlayerInfos()
3037 DSTACK(__FUNCTION_NAME);
3039 //JMutexAutoLock envlock(m_env_mutex);
3041 // Get connected players
3042 core::list<Player*> players = m_env.getPlayers(true);
3044 u32 player_count = players.getSize();
3045 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3047 SharedBuffer<u8> data(datasize);
3048 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3051 core::list<Player*>::Iterator i;
3052 for(i = players.begin();
3053 i != players.end(); i++)
3055 Player *player = *i;
3057 /*dstream<<"Server sending player info for player with "
3058 "peer_id="<<player->peer_id<<std::endl;*/
3060 writeU16(&data[start], player->peer_id);
3061 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3062 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3063 start += 2+PLAYERNAME_SIZE;
3066 //JMutexAutoLock conlock(m_con_mutex);
3069 m_con.SendToAll(0, data, true);
3072 void Server::SendInventory(u16 peer_id)
3074 DSTACK(__FUNCTION_NAME);
3076 Player* player = m_env.getPlayer(peer_id);
3080 Calculate crafting stuff
3082 if(g_settings.getBool("creative_mode") == false)
3084 InventoryList *clist = player->inventory.getList("craft");
3085 InventoryList *rlist = player->inventory.getList("craftresult");
3087 if(rlist->getUsedSlots() == 0)
3088 player->craftresult_is_preview = true;
3090 if(rlist && player->craftresult_is_preview)
3092 rlist->clearItems();
3094 if(clist && rlist && player->craftresult_is_preview)
3096 InventoryItem *items[9];
3097 for(u16 i=0; i<9; i++)
3099 items[i] = clist->getItem(i);
3108 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3109 if(checkItemCombination(items, specs))
3111 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3120 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3121 if(checkItemCombination(items, specs))
3123 rlist->addItem(new CraftItem("Stick", 4));
3132 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3133 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3134 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3135 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3136 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3137 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3138 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3139 if(checkItemCombination(items, specs))
3141 //rlist->addItem(new MapBlockObjectItem("Sign"));
3142 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3151 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3152 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3153 if(checkItemCombination(items, specs))
3155 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3164 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3165 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3166 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3167 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3168 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3169 if(checkItemCombination(items, specs))
3171 rlist->addItem(new ToolItem("WPick", 0));
3180 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3181 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3182 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3183 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3184 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3185 if(checkItemCombination(items, specs))
3187 rlist->addItem(new ToolItem("STPick", 0));
3196 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3197 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3198 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3199 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3200 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3201 if(checkItemCombination(items, specs))
3203 rlist->addItem(new ToolItem("SteelPick", 0));
3212 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3213 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3214 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3215 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3216 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3217 if(checkItemCombination(items, specs))
3219 rlist->addItem(new ToolItem("MesePick", 0));
3228 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3229 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3230 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3231 if(checkItemCombination(items, specs))
3233 rlist->addItem(new ToolItem("WShovel", 0));
3242 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3243 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3244 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3245 if(checkItemCombination(items, specs))
3247 rlist->addItem(new ToolItem("STShovel", 0));
3256 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3257 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3258 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3259 if(checkItemCombination(items, specs))
3261 rlist->addItem(new ToolItem("SteelShovel", 0));
3270 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3271 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3272 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3273 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3274 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3275 if(checkItemCombination(items, specs))
3277 rlist->addItem(new ToolItem("WAxe", 0));
3286 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3287 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3288 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3289 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3290 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3291 if(checkItemCombination(items, specs))
3293 rlist->addItem(new ToolItem("STAxe", 0));
3302 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3303 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3304 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3305 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3306 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3307 if(checkItemCombination(items, specs))
3309 rlist->addItem(new ToolItem("SteelAxe", 0));
3318 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3319 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3320 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3321 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3322 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3323 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3324 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3325 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3326 if(checkItemCombination(items, specs))
3328 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3337 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3338 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3339 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3340 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3341 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3342 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3343 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3344 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3345 if(checkItemCombination(items, specs))
3347 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3356 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3357 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3358 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3359 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3360 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3361 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3362 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3363 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3364 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3365 if(checkItemCombination(items, specs))
3367 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3373 } // if creative_mode == false
3379 std::ostringstream os;
3380 //os.imbue(std::locale("C"));
3382 player->inventory.serialize(os);
3384 std::string s = os.str();
3386 SharedBuffer<u8> data(s.size()+2);
3387 writeU16(&data[0], TOCLIENT_INVENTORY);
3388 memcpy(&data[2], s.c_str(), s.size());
3391 m_con.Send(peer_id, 0, data, true);
3394 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3396 DSTACK(__FUNCTION_NAME);
3398 std::ostringstream os(std::ios_base::binary);
3402 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3403 os.write((char*)buf, 2);
3406 writeU16(buf, message.size());
3407 os.write((char*)buf, 2);
3410 for(u32 i=0; i<message.size(); i++)
3414 os.write((char*)buf, 2);
3418 std::string s = os.str();
3419 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3421 m_con.Send(peer_id, 0, data, true);
3424 void Server::BroadcastChatMessage(const std::wstring &message)
3426 for(core::map<u16, RemoteClient*>::Iterator
3427 i = m_clients.getIterator();
3428 i.atEnd() == false; i++)
3430 // Get client and check that it is valid
3431 RemoteClient *client = i.getNode()->getValue();
3432 assert(client->peer_id == i.getNode()->getKey());
3433 if(client->serialization_version == SER_FMT_VER_INVALID)
3436 SendChatMessage(client->peer_id, message);
3440 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3441 core::list<u16> *far_players, float far_d_nodes)
3443 float maxd = far_d_nodes*BS;
3444 v3f p_f = intToFloat(p, BS);
3448 SharedBuffer<u8> reply(replysize);
3449 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3450 writeS16(&reply[2], p.X);
3451 writeS16(&reply[4], p.Y);
3452 writeS16(&reply[6], p.Z);
3454 for(core::map<u16, RemoteClient*>::Iterator
3455 i = m_clients.getIterator();
3456 i.atEnd() == false; i++)
3458 // Get client and check that it is valid
3459 RemoteClient *client = i.getNode()->getValue();
3460 assert(client->peer_id == i.getNode()->getKey());
3461 if(client->serialization_version == SER_FMT_VER_INVALID)
3464 // Don't send if it's the same one
3465 if(client->peer_id == ignore_id)
3471 Player *player = m_env.getPlayer(client->peer_id);
3474 // If player is far away, only set modified blocks not sent
3475 v3f player_pos = player->getPosition();
3476 if(player_pos.getDistanceFrom(p_f) > maxd)
3478 far_players->push_back(client->peer_id);
3485 m_con.Send(client->peer_id, 0, reply, true);
3489 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3490 core::list<u16> *far_players, float far_d_nodes)
3492 float maxd = far_d_nodes*BS;
3493 v3f p_f = intToFloat(p, BS);
3495 for(core::map<u16, RemoteClient*>::Iterator
3496 i = m_clients.getIterator();
3497 i.atEnd() == false; i++)
3499 // Get client and check that it is valid
3500 RemoteClient *client = i.getNode()->getValue();
3501 assert(client->peer_id == i.getNode()->getKey());
3502 if(client->serialization_version == SER_FMT_VER_INVALID)
3505 // Don't send if it's the same one
3506 if(client->peer_id == ignore_id)
3512 Player *player = m_env.getPlayer(client->peer_id);
3515 // If player is far away, only set modified blocks not sent
3516 v3f player_pos = player->getPosition();
3517 if(player_pos.getDistanceFrom(p_f) > maxd)
3519 far_players->push_back(client->peer_id);
3526 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3527 SharedBuffer<u8> reply(replysize);
3528 writeU16(&reply[0], TOCLIENT_ADDNODE);
3529 writeS16(&reply[2], p.X);
3530 writeS16(&reply[4], p.Y);
3531 writeS16(&reply[6], p.Z);
3532 n.serialize(&reply[8], client->serialization_version);
3535 m_con.Send(client->peer_id, 0, reply, true);
3539 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3541 DSTACK(__FUNCTION_NAME);
3543 Create a packet with the block in the right format
3546 std::ostringstream os(std::ios_base::binary);
3547 block->serialize(os, ver);
3548 std::string s = os.str();
3549 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3551 u32 replysize = 8 + blockdata.getSize();
3552 SharedBuffer<u8> reply(replysize);
3553 v3s16 p = block->getPos();
3554 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3555 writeS16(&reply[2], p.X);
3556 writeS16(&reply[4], p.Y);
3557 writeS16(&reply[6], p.Z);
3558 memcpy(&reply[8], *blockdata, blockdata.getSize());
3560 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3561 <<": \tpacket size: "<<replysize<<std::endl;*/
3566 m_con.Send(peer_id, 1, reply, true);
3569 void Server::SendBlocks(float dtime)
3571 DSTACK(__FUNCTION_NAME);
3573 JMutexAutoLock envlock(m_env_mutex);
3574 JMutexAutoLock conlock(m_con_mutex);
3576 //TimeTaker timer("Server::SendBlocks");
3578 core::array<PrioritySortedBlockTransfer> queue;
3580 s32 total_sending = 0;
3582 for(core::map<u16, RemoteClient*>::Iterator
3583 i = m_clients.getIterator();
3584 i.atEnd() == false; i++)
3586 RemoteClient *client = i.getNode()->getValue();
3587 assert(client->peer_id == i.getNode()->getKey());
3589 total_sending += client->SendingCount();
3591 if(client->serialization_version == SER_FMT_VER_INVALID)
3594 client->GetNextBlocks(this, dtime, queue);
3598 // Lowest priority number comes first.
3599 // Lowest is most important.
3602 for(u32 i=0; i<queue.size(); i++)
3604 //TODO: Calculate limit dynamically
3605 if(total_sending >= g_settings.getS32
3606 ("max_simultaneous_block_sends_server_total"))
3609 PrioritySortedBlockTransfer q = queue[i];
3611 MapBlock *block = NULL;
3614 block = m_env.getMap().getBlockNoCreate(q.pos);
3616 catch(InvalidPositionException &e)
3621 RemoteClient *client = getClient(q.peer_id);
3623 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3625 client->SentBlock(q.pos);
3632 RemoteClient* Server::getClient(u16 peer_id)
3634 DSTACK(__FUNCTION_NAME);
3635 //JMutexAutoLock lock(m_con_mutex);
3636 core::map<u16, RemoteClient*>::Node *n;
3637 n = m_clients.find(peer_id);
3638 // A client should exist for all peers
3640 return n->getValue();
3643 std::wstring Server::getStatusString()
3645 std::wostringstream os(std::ios_base::binary);
3648 os<<L"uptime="<<m_uptime.get();
3649 // Information about clients
3651 for(core::map<u16, RemoteClient*>::Iterator
3652 i = m_clients.getIterator();
3653 i.atEnd() == false; i++)
3655 // Get client and check that it is valid
3656 RemoteClient *client = i.getNode()->getValue();
3657 assert(client->peer_id == i.getNode()->getKey());
3658 if(client->serialization_version == SER_FMT_VER_INVALID)
3661 Player *player = m_env.getPlayer(client->peer_id);
3662 // Get name of player
3663 std::wstring name = L"unknown";
3665 name = narrow_to_wide(player->getName());
3666 // Add name to information string
3670 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3671 os<<" WARNING: Map saving is disabled."<<std::endl;
3676 void setCreativeInventory(Player *player)
3678 player->resetInventory();
3680 // Give some good picks
3682 InventoryItem *item = new ToolItem("STPick", 0);
3683 void* r = player->inventory.addItem("main", item);
3687 InventoryItem *item = new ToolItem("MesePick", 0);
3688 void* r = player->inventory.addItem("main", item);
3696 // CONTENT_IGNORE-terminated list
3697 u8 material_items[] = {
3706 CONTENT_WATERSOURCE,
3714 u8 *mip = material_items;
3715 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3717 if(*mip == CONTENT_IGNORE)
3720 InventoryItem *item = new MaterialItem(*mip, 1);
3721 player->inventory.addItem("main", item);
3727 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3730 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3731 player->inventory.addItem("main", item);
3734 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3736 // Skip some materials
3737 if(i == CONTENT_WATER || i == CONTENT_TORCH
3738 || i == CONTENT_COALSTONE)
3741 InventoryItem *item = new MaterialItem(i, 1);
3742 player->inventory.addItem("main", item);
3748 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3749 void* r = player->inventory.addItem("main", item);
3754 Player *Server::emergePlayer(const char *name, const char *password,
3758 Try to get an existing player
3760 Player *player = m_env.getPlayer(name);
3763 // If player is already connected, cancel
3764 if(player->peer_id != 0)
3766 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3771 player->peer_id = peer_id;
3773 // Reset inventory to creative if in creative mode
3774 if(g_settings.getBool("creative_mode"))
3776 setCreativeInventory(player);
3783 If player with the wanted peer_id already exists, cancel.
3785 if(m_env.getPlayer(peer_id) != NULL)
3787 dstream<<"emergePlayer(): Player with wrong name but same"
3788 " peer_id already exists"<<std::endl;
3796 player = new ServerRemotePlayer();
3797 //player->peer_id = c.peer_id;
3798 //player->peer_id = PEER_ID_INEXISTENT;
3799 player->peer_id = peer_id;
3800 player->updateName(name);
3806 dstream<<"Server: Finding spawn place for player \""
3807 <<player->getName()<<"\""<<std::endl;
3811 player->setPosition(intToFloat(v3s16(
3818 s16 groundheight = 0;
3820 // Try to find a good place a few times
3821 for(s32 i=0; i<1000; i++)
3824 // We're going to try to throw the player to this position
3825 nodepos = v2s16(-range + (myrand()%(range*2)),
3826 -range + (myrand()%(range*2)));
3827 v2s16 sectorpos = getNodeSectorPos(nodepos);
3828 // Get sector (NOTE: Don't get because it's slow)
3829 //m_env.getMap().emergeSector(sectorpos);
3830 // Get ground height at point (fallbacks to heightmap function)
3831 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3832 // Don't go underwater
3833 if(groundheight < WATER_LEVEL)
3835 //dstream<<"-> Underwater"<<std::endl;
3838 // Don't go to high places
3839 if(groundheight > WATER_LEVEL + 4)
3841 //dstream<<"-> Underwater"<<std::endl;
3845 // Found a good place
3846 dstream<<"Searched through "<<i<<" places."<<std::endl;
3851 // If no suitable place was not found, go above water at least.
3852 if(groundheight < WATER_LEVEL)
3853 groundheight = WATER_LEVEL;
3855 player->setPosition(intToFloat(v3s16(
3857 groundheight + 5, // Accomodate mud
3863 Add player to environment
3866 m_env.addPlayer(player);
3869 Add stuff to inventory
3872 if(g_settings.getBool("creative_mode"))
3874 setCreativeInventory(player);
3879 InventoryItem *item = new ToolItem("WPick", 32000);
3880 void* r = player->inventory.addItem("main", item);
3884 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3885 void* r = player->inventory.addItem("main", item);
3889 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3890 void* r = player->inventory.addItem("main", item);
3894 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3895 void* r = player->inventory.addItem("main", item);
3899 InventoryItem *item = new CraftItem("Stick", 4);
3900 void* r = player->inventory.addItem("main", item);
3904 InventoryItem *item = new ToolItem("WPick", 32000);
3905 void* r = player->inventory.addItem("main", item);
3909 InventoryItem *item = new ToolItem("STPick", 32000);
3910 void* r = player->inventory.addItem("main", item);
3913 /*// Give some lights
3915 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3916 bool r = player->inventory.addItem("main", item);
3920 for(u16 i=0; i<4; i++)
3922 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3923 bool r = player->inventory.addItem("main", item);
3926 /*// Give some other stuff
3928 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3929 bool r = player->inventory.addItem("main", item);
3936 } // create new player
3939 void Server::handlePeerChange(PeerChange &c)
3941 JMutexAutoLock envlock(m_env_mutex);
3942 JMutexAutoLock conlock(m_con_mutex);
3944 if(c.type == PEER_ADDED)
3951 core::map<u16, RemoteClient*>::Node *n;
3952 n = m_clients.find(c.peer_id);
3953 // The client shouldn't already exist
3957 RemoteClient *client = new RemoteClient();
3958 client->peer_id = c.peer_id;
3959 m_clients.insert(client->peer_id, client);
3962 else if(c.type == PEER_REMOVED)
3969 core::map<u16, RemoteClient*>::Node *n;
3970 n = m_clients.find(c.peer_id);
3971 // The client should exist
3974 // Collect information about leaving in chat
3975 std::wstring message;
3977 std::wstring name = L"unknown";
3978 Player *player = m_env.getPlayer(c.peer_id);
3980 name = narrow_to_wide(player->getName());
3984 message += L" left game";
3986 message += L" (timed out)";
3991 m_env.removePlayer(c.peer_id);
3994 // Set player client disconnected
3996 Player *player = m_env.getPlayer(c.peer_id);
3998 player->peer_id = 0;
4002 delete m_clients[c.peer_id];
4003 m_clients.remove(c.peer_id);
4005 // Send player info to all remaining clients
4008 // Send leave chat message to all remaining clients
4009 BroadcastChatMessage(message);
4018 void Server::handlePeerChanges()
4020 while(m_peer_change_queue.size() > 0)
4022 PeerChange c = m_peer_change_queue.pop_front();
4024 dout_server<<"Server: Handling peer change: "
4025 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4028 handlePeerChange(c);
4032 void dedicated_server_loop(Server &server, bool &kill)
4034 DSTACK(__FUNCTION_NAME);
4036 std::cout<<DTIME<<std::endl;
4037 std::cout<<"========================"<<std::endl;
4038 std::cout<<"Running dedicated server"<<std::endl;
4039 std::cout<<"========================"<<std::endl;
4040 std::cout<<std::endl;
4044 // This is kind of a hack but can be done like this
4045 // because server.step() is very light
4049 if(server.getShutdownRequested() || kill)
4051 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4055 static int counter = 0;
4061 core::list<PlayerInfo> list = server.getPlayerInfo();
4062 core::list<PlayerInfo>::Iterator i;
4063 static u32 sum_old = 0;
4064 u32 sum = PIChecksum(list);
4067 std::cout<<DTIME<<"Player info:"<<std::endl;
4068 for(i=list.begin(); i!=list.end(); i++)
4070 i->PrintLine(&std::cout);