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 ServerMapSector *sector =
205 (ServerMapSector*)map.getSectorNoGenerateNoEx(p2d);
206 block = map.generateBlock(p, block, sector, changed_blocks,
207 lighting_invalidated_blocks);
211 // TODO: Some additional checking and lighting updating,
216 JMutexAutoLock envlock(m_server->m_env_mutex);
221 Collect a list of blocks that have been modified in
222 addition to the fetched one.
225 if(lighting_invalidated_blocks.size() > 0)
227 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
228 <<" blocks"<<std::endl;*/
230 // 50-100ms for single block generation
231 //TimeTaker timer("** EmergeThread updateLighting");
233 // Update lighting without locking the environment mutex,
234 // add modified blocks to changed blocks
235 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
238 // Add all from changed_blocks to modified_blocks
239 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
240 i.atEnd() == false; i++)
242 MapBlock *block = i.getNode()->getValue();
243 modified_blocks.insert(block->getPos(), block);
246 // If we got no block, there should be no invalidated blocks
249 assert(lighting_invalidated_blocks.size() == 0);
255 Set sent status of modified blocks on clients
258 // NOTE: Server's clients are also behind the connection mutex
259 JMutexAutoLock lock(m_server->m_con_mutex);
262 Add the originally fetched block to the modified list
266 modified_blocks.insert(p, block);
270 Set the modified blocks unsent for all the clients
273 for(core::map<u16, RemoteClient*>::Iterator
274 i = m_server->m_clients.getIterator();
275 i.atEnd() == false; i++)
277 RemoteClient *client = i.getNode()->getValue();
279 if(modified_blocks.size() > 0)
281 // Remove block from sent history
282 client->SetBlocksNotSent(modified_blocks);
288 END_DEBUG_EXCEPTION_HANDLER
293 void RemoteClient::GetNextBlocks(Server *server, float dtime,
294 core::array<PrioritySortedBlockTransfer> &dest)
296 DSTACK(__FUNCTION_NAME);
299 m_nearest_unsent_reset_timer += dtime;
301 // Won't send anything if already sending
302 if(m_blocks_sending.size() >= g_settings.getU16
303 ("max_simultaneous_block_sends_per_client"))
305 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
309 Player *player = server->m_env.getPlayer(peer_id);
311 assert(player != NULL);
313 v3f playerpos = player->getPosition();
314 v3f playerspeed = player->getSpeed();
316 v3s16 center_nodepos = floatToInt(playerpos, BS);
318 v3s16 center = getNodeBlockPos(center_nodepos);
320 // Camera position and direction
322 playerpos + v3f(0, BS+BS/2, 0);
323 v3f camera_dir = v3f(0,0,1);
324 camera_dir.rotateYZBy(player->getPitch());
325 camera_dir.rotateXZBy(player->getYaw());
328 Get the starting value of the block finder radius.
330 s16 last_nearest_unsent_d;
333 if(m_last_center != center)
335 m_nearest_unsent_d = 0;
336 m_last_center = center;
339 /*dstream<<"m_nearest_unsent_reset_timer="
340 <<m_nearest_unsent_reset_timer<<std::endl;*/
341 if(m_nearest_unsent_reset_timer > 5.0)
343 m_nearest_unsent_reset_timer = 0;
344 m_nearest_unsent_d = 0;
345 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
348 last_nearest_unsent_d = m_nearest_unsent_d;
350 d_start = m_nearest_unsent_d;
352 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
353 ("max_simultaneous_block_sends_per_client");
354 u16 maximum_simultaneous_block_sends =
355 maximum_simultaneous_block_sends_setting;
358 Check the time from last addNode/removeNode.
360 Decrease send rate if player is building stuff.
362 m_time_from_building += dtime;
363 if(m_time_from_building < g_settings.getFloat(
364 "full_block_send_enable_min_time_from_building"))
366 maximum_simultaneous_block_sends
367 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
370 u32 num_blocks_selected = m_blocks_sending.size();
373 next time d will be continued from the d from which the nearest
374 unsent block was found this time.
376 This is because not necessarily any of the blocks found this
377 time are actually sent.
379 s32 new_nearest_unsent_d = -1;
381 s16 d_max = g_settings.getS16("max_block_send_distance");
382 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
384 //dstream<<"Starting from "<<d_start<<std::endl;
386 for(s16 d = d_start; d <= d_max; d++)
388 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
391 If m_nearest_unsent_d was changed by the EmergeThread
392 (it can change it to 0 through SetBlockNotSent),
394 Else update m_nearest_unsent_d
396 if(m_nearest_unsent_d != last_nearest_unsent_d)
398 d = m_nearest_unsent_d;
399 last_nearest_unsent_d = m_nearest_unsent_d;
403 Get the border/face dot coordinates of a "d-radiused"
406 core::list<v3s16> list;
407 getFacePositions(list, d);
409 core::list<v3s16>::Iterator li;
410 for(li=list.begin(); li!=list.end(); li++)
412 v3s16 p = *li + center;
416 - Don't allow too many simultaneous transfers
417 - EXCEPT when the blocks are very close
419 Also, don't send blocks that are already flying.
422 u16 maximum_simultaneous_block_sends_now =
423 maximum_simultaneous_block_sends;
425 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
427 maximum_simultaneous_block_sends_now =
428 maximum_simultaneous_block_sends_setting;
431 // Limit is dynamically lowered when building
432 if(num_blocks_selected
433 >= maximum_simultaneous_block_sends_now)
435 /*dstream<<"Not sending more blocks. Queue full. "
436 <<m_blocks_sending.size()
441 if(m_blocks_sending.find(p) != NULL)
447 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
448 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
449 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
450 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
451 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
452 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
455 // If this is true, inexistent block will be made from scratch
456 bool generate = d <= d_max_gen;
459 /*// Limit the generating area vertically to 2/3
460 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
463 // Limit the send area vertically to 2/3
464 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
470 If block is far away, don't generate it unless it is
473 NOTE: We can't know the ground level this way with the
479 MapSector *sector = NULL;
482 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
484 catch(InvalidPositionException &e)
490 // Get center ground height in nodes
491 f32 gh = sector->getGroundHeight(
492 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
493 // Block center y in nodes
494 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
495 // If differs a lot, don't generate
496 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
503 Don't generate or send if not in sight
506 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
512 Don't send already sent blocks
515 if(m_blocks_sent.find(p) != NULL)
520 Check if map has this block
522 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
524 bool surely_not_found_on_disk = false;
525 bool block_is_invalid = false;
530 surely_not_found_on_disk = true;
533 if(block->isValid() == false)
535 block_is_invalid = true;
538 /*if(block->isFullyGenerated() == false)
540 block_is_invalid = true;
544 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
545 v2s16 chunkpos = map->sector_to_chunk(p2d);
546 if(map->chunkNonVolatile(chunkpos) == false)
547 block_is_invalid = true;
551 If block has been marked to not exist on disk (dummy)
552 and generating new ones is not wanted, skip block.
554 if(generate == false && surely_not_found_on_disk == true)
561 Record the lowest d from which a a block has been
562 found being not sent and possibly to exist
564 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
566 new_nearest_unsent_d = d;
570 Add inexistent block to emerge queue.
572 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
574 //TODO: Get value from somewhere
575 // Allow only one block in emerge queue
576 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
577 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
579 //dstream<<"Adding block to emerge queue"<<std::endl;
581 // Add it to the emerge queue and trigger the thread
584 if(generate == false)
585 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
587 server->m_emerge_queue.addBlock(peer_id, p, flags);
588 server->m_emergethread.trigger();
596 Add block to send queue
599 PrioritySortedBlockTransfer q((float)d, p, peer_id);
603 num_blocks_selected += 1;
608 if(new_nearest_unsent_d != -1)
610 m_nearest_unsent_d = new_nearest_unsent_d;
614 void RemoteClient::SendObjectData(
617 core::map<v3s16, bool> &stepped_blocks
620 DSTACK(__FUNCTION_NAME);
622 // Can't send anything without knowing version
623 if(serialization_version == SER_FMT_VER_INVALID)
625 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
631 Send a TOCLIENT_OBJECTDATA packet.
635 u16 number of player positions
646 std::ostringstream os(std::ios_base::binary);
650 writeU16(buf, TOCLIENT_OBJECTDATA);
651 os.write((char*)buf, 2);
654 Get and write player data
657 // Get connected players
658 core::list<Player*> players = server->m_env.getPlayers(true);
660 // Write player count
661 u16 playercount = players.size();
662 writeU16(buf, playercount);
663 os.write((char*)buf, 2);
665 core::list<Player*>::Iterator i;
666 for(i = players.begin();
667 i != players.end(); i++)
671 v3f pf = player->getPosition();
672 v3f sf = player->getSpeed();
674 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
675 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
676 s32 pitch_i (player->getPitch() * 100);
677 s32 yaw_i (player->getYaw() * 100);
679 writeU16(buf, player->peer_id);
680 os.write((char*)buf, 2);
681 writeV3S32(buf, position_i);
682 os.write((char*)buf, 12);
683 writeV3S32(buf, speed_i);
684 os.write((char*)buf, 12);
685 writeS32(buf, pitch_i);
686 os.write((char*)buf, 4);
687 writeS32(buf, yaw_i);
688 os.write((char*)buf, 4);
692 Get and write object data
698 For making players to be able to build to their nearby
699 environment (building is not possible on blocks that are not
702 - Add blocks to emerge queue if they are not found
704 SUGGESTION: These could be ignored from the backside of the player
707 Player *player = server->m_env.getPlayer(peer_id);
711 v3f playerpos = player->getPosition();
712 v3f playerspeed = player->getSpeed();
714 v3s16 center_nodepos = floatToInt(playerpos, BS);
715 v3s16 center = getNodeBlockPos(center_nodepos);
717 s16 d_max = g_settings.getS16("active_object_range");
719 // Number of blocks whose objects were written to bos
722 std::ostringstream bos(std::ios_base::binary);
724 for(s16 d = 0; d <= d_max; d++)
726 core::list<v3s16> list;
727 getFacePositions(list, d);
729 core::list<v3s16>::Iterator li;
730 for(li=list.begin(); li!=list.end(); li++)
732 v3s16 p = *li + center;
735 Ignore blocks that haven't been sent to the client
738 if(m_blocks_sent.find(p) == NULL)
742 // Try stepping block and add it to a send queue
747 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
750 Step block if not in stepped_blocks and add to stepped_blocks.
752 if(stepped_blocks.find(p) == NULL)
754 block->stepObjects(dtime, true, server->getDayNightRatio());
755 stepped_blocks.insert(p, true);
756 block->setChangedFlag();
759 // Skip block if there are no objects
760 if(block->getObjectCount() == 0)
769 bos.write((char*)buf, 6);
772 block->serializeObjects(bos, serialization_version);
777 Stop collecting objects if data is already too big
779 // Sum of player and object data sizes
780 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
781 // break out if data too big
782 if(sum > MAX_OBJECTDATA_SIZE)
784 goto skip_subsequent;
788 catch(InvalidPositionException &e)
791 // Add it to the emerge queue and trigger the thread.
792 // Fetch the block only if it is on disk.
794 // Grab and increment counter
795 /*SharedPtr<JMutexAutoLock> lock
796 (m_num_blocks_in_emerge_queue.getLock());
797 m_num_blocks_in_emerge_queue.m_value++;*/
799 // Add to queue as an anonymous fetch from disk
800 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
801 server->m_emerge_queue.addBlock(0, p, flags);
802 server->m_emergethread.trigger();
810 writeU16(buf, blockcount);
811 os.write((char*)buf, 2);
813 // Write block objects
820 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
823 std::string s = os.str();
824 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
825 // Send as unreliable
826 server->m_con.Send(peer_id, 0, data, false);
829 void RemoteClient::GotBlock(v3s16 p)
831 if(m_blocks_sending.find(p) != NULL)
832 m_blocks_sending.remove(p);
835 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
836 " m_blocks_sending"<<std::endl;*/
837 m_excess_gotblocks++;
839 m_blocks_sent.insert(p, true);
842 void RemoteClient::SentBlock(v3s16 p)
844 if(m_blocks_sending.find(p) == NULL)
845 m_blocks_sending.insert(p, 0.0);
847 dstream<<"RemoteClient::SentBlock(): Sent block"
848 " already in m_blocks_sending"<<std::endl;
851 void RemoteClient::SetBlockNotSent(v3s16 p)
853 m_nearest_unsent_d = 0;
855 if(m_blocks_sending.find(p) != NULL)
856 m_blocks_sending.remove(p);
857 if(m_blocks_sent.find(p) != NULL)
858 m_blocks_sent.remove(p);
861 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
863 m_nearest_unsent_d = 0;
865 for(core::map<v3s16, MapBlock*>::Iterator
866 i = blocks.getIterator();
867 i.atEnd()==false; i++)
869 v3s16 p = i.getNode()->getKey();
871 if(m_blocks_sending.find(p) != NULL)
872 m_blocks_sending.remove(p);
873 if(m_blocks_sent.find(p) != NULL)
874 m_blocks_sent.remove(p);
882 PlayerInfo::PlayerInfo()
887 void PlayerInfo::PrintLine(std::ostream *s)
890 (*s)<<"\""<<name<<"\" ("
891 <<(position.X/10)<<","<<(position.Y/10)
892 <<","<<(position.Z/10)<<") ";
894 (*s)<<" avg_rtt="<<avg_rtt;
898 u32 PIChecksum(core::list<PlayerInfo> &l)
900 core::list<PlayerInfo>::Iterator i;
903 for(i=l.begin(); i!=l.end(); i++)
905 checksum += a * (i->id+1);
906 checksum ^= 0x435aafcd;
917 std::string mapsavedir
919 m_env(new ServerMap(mapsavedir), this),
920 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
922 m_emergethread(this),
925 m_time_of_day_send_timer(0),
927 m_mapsavedir(mapsavedir),
928 m_shutdown_requested(false),
929 m_ignore_map_edit_events(false),
930 m_ignore_map_edit_events_peer_id(0)
932 m_liquid_transform_timer = 0.0;
933 m_print_info_timer = 0.0;
934 m_objectdata_timer = 0.0;
935 m_emergethread_trigger_timer = 0.0;
936 m_savemap_timer = 0.0;
940 m_step_dtime_mutex.Init();
943 m_env.getMap().addEventReceiver(this);
946 m_env.deSerializePlayers(m_mapsavedir);
952 Send shutdown message
955 JMutexAutoLock conlock(m_con_mutex);
957 std::wstring line = L"*** Server shutting down";
960 Send the message to clients
962 for(core::map<u16, RemoteClient*>::Iterator
963 i = m_clients.getIterator();
964 i.atEnd() == false; i++)
966 // Get client and check that it is valid
967 RemoteClient *client = i.getNode()->getValue();
968 assert(client->peer_id == i.getNode()->getKey());
969 if(client->serialization_version == SER_FMT_VER_INVALID)
972 SendChatMessage(client->peer_id, line);
979 m_env.serializePlayers(m_mapsavedir);
990 JMutexAutoLock clientslock(m_con_mutex);
992 for(core::map<u16, RemoteClient*>::Iterator
993 i = m_clients.getIterator();
994 i.atEnd() == false; i++)
997 // NOTE: These are removed by env destructor
999 u16 peer_id = i.getNode()->getKey();
1000 JMutexAutoLock envlock(m_env_mutex);
1001 m_env.removePlayer(peer_id);
1005 delete i.getNode()->getValue();
1010 void Server::start(unsigned short port)
1012 DSTACK(__FUNCTION_NAME);
1013 // Stop thread if already running
1016 // Initialize connection
1017 m_con.setTimeoutMs(30);
1021 m_thread.setRun(true);
1024 dout_server<<"Server: Started on port "<<port<<std::endl;
1029 DSTACK(__FUNCTION_NAME);
1031 // Stop threads (set run=false first so both start stopping)
1032 m_thread.setRun(false);
1033 m_emergethread.setRun(false);
1035 m_emergethread.stop();
1037 dout_server<<"Server: Threads stopped"<<std::endl;
1039 dout_server<<"Server: Saving players"<<std::endl;
1041 // FIXME: Apparently this does not do anything here
1042 //m_env.serializePlayers(m_mapsavedir);
1045 void Server::step(float dtime)
1047 DSTACK(__FUNCTION_NAME);
1052 JMutexAutoLock lock(m_step_dtime_mutex);
1053 m_step_dtime += dtime;
1057 void Server::AsyncRunStep()
1059 DSTACK(__FUNCTION_NAME);
1063 JMutexAutoLock lock1(m_step_dtime_mutex);
1064 dtime = m_step_dtime;
1067 // Send blocks to clients
1073 //dstream<<"Server steps "<<dtime<<std::endl;
1074 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1077 JMutexAutoLock lock1(m_step_dtime_mutex);
1078 m_step_dtime -= dtime;
1085 m_uptime.set(m_uptime.get() + dtime);
1089 Update m_time_of_day
1092 m_time_counter += dtime;
1093 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1094 u32 units = (u32)(m_time_counter*speed);
1095 m_time_counter -= (f32)units / speed;
1096 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1098 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1101 Send to clients at constant intervals
1104 m_time_of_day_send_timer -= dtime;
1105 if(m_time_of_day_send_timer < 0.0)
1107 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1109 //JMutexAutoLock envlock(m_env_mutex);
1110 JMutexAutoLock conlock(m_con_mutex);
1112 for(core::map<u16, RemoteClient*>::Iterator
1113 i = m_clients.getIterator();
1114 i.atEnd() == false; i++)
1116 RemoteClient *client = i.getNode()->getValue();
1117 //Player *player = m_env.getPlayer(client->peer_id);
1119 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1120 m_time_of_day.get());
1122 m_con.Send(client->peer_id, 0, data, true);
1128 // Process connection's timeouts
1129 JMutexAutoLock lock2(m_con_mutex);
1130 m_con.RunTimeouts(dtime);
1134 // This has to be called so that the client list gets synced
1135 // with the peer list of the connection
1136 handlePeerChanges();
1141 // This also runs Map's timers
1142 JMutexAutoLock lock(m_env_mutex);
1153 m_liquid_transform_timer += dtime;
1154 if(m_liquid_transform_timer >= 1.00)
1156 m_liquid_transform_timer -= 1.00;
1158 JMutexAutoLock lock(m_env_mutex);
1160 core::map<v3s16, MapBlock*> modified_blocks;
1161 m_env.getMap().transformLiquids(modified_blocks);
1166 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1167 ServerMap &map = ((ServerMap&)m_env.getMap());
1168 map.updateLighting(modified_blocks, lighting_modified_blocks);
1170 // Add blocks modified by lighting to modified_blocks
1171 for(core::map<v3s16, MapBlock*>::Iterator
1172 i = lighting_modified_blocks.getIterator();
1173 i.atEnd() == false; i++)
1175 MapBlock *block = i.getNode()->getValue();
1176 modified_blocks.insert(block->getPos(), block);
1180 Set the modified blocks unsent for all the clients
1183 JMutexAutoLock lock2(m_con_mutex);
1185 for(core::map<u16, RemoteClient*>::Iterator
1186 i = m_clients.getIterator();
1187 i.atEnd() == false; i++)
1189 RemoteClient *client = i.getNode()->getValue();
1191 if(modified_blocks.size() > 0)
1193 // Remove block from sent history
1194 client->SetBlocksNotSent(modified_blocks);
1199 // Periodically print some info
1201 float &counter = m_print_info_timer;
1207 JMutexAutoLock lock2(m_con_mutex);
1209 for(core::map<u16, RemoteClient*>::Iterator
1210 i = m_clients.getIterator();
1211 i.atEnd() == false; i++)
1213 //u16 peer_id = i.getNode()->getKey();
1214 RemoteClient *client = i.getNode()->getValue();
1215 Player *player = m_env.getPlayer(client->peer_id);
1216 std::cout<<player->getName()<<"\t";
1217 client->PrintInfo(std::cout);
1222 //if(g_settings.getBool("enable_experimental"))
1226 Check added and deleted active objects
1229 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1231 JMutexAutoLock envlock(m_env_mutex);
1232 JMutexAutoLock conlock(m_con_mutex);
1234 // Radius inside which objects are active
1237 for(core::map<u16, RemoteClient*>::Iterator
1238 i = m_clients.getIterator();
1239 i.atEnd() == false; i++)
1241 RemoteClient *client = i.getNode()->getValue();
1242 Player *player = m_env.getPlayer(client->peer_id);
1245 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1246 <<" has no associated player"<<std::endl;
1249 v3s16 pos = floatToInt(player->getPosition(), BS);
1251 core::map<u16, bool> removed_objects;
1252 core::map<u16, bool> added_objects;
1253 m_env.getRemovedActiveObjects(pos, radius,
1254 client->m_known_objects, removed_objects);
1255 m_env.getAddedActiveObjects(pos, radius,
1256 client->m_known_objects, added_objects);
1258 // Ignore if nothing happened
1259 if(removed_objects.size() == 0 && added_objects.size() == 0)
1261 //dstream<<"INFO: active objects: none changed"<<std::endl;
1265 std::string data_buffer;
1269 // Handle removed objects
1270 writeU16((u8*)buf, removed_objects.size());
1271 data_buffer.append(buf, 2);
1272 for(core::map<u16, bool>::Iterator
1273 i = removed_objects.getIterator();
1274 i.atEnd()==false; i++)
1277 u16 id = i.getNode()->getKey();
1278 ServerActiveObject* obj = m_env.getActiveObject(id);
1280 // Add to data buffer for sending
1281 writeU16((u8*)buf, i.getNode()->getKey());
1282 data_buffer.append(buf, 2);
1284 // Remove from known objects
1285 client->m_known_objects.remove(i.getNode()->getKey());
1287 if(obj && obj->m_known_by_count > 0)
1288 obj->m_known_by_count--;
1291 // Handle added objects
1292 writeU16((u8*)buf, added_objects.size());
1293 data_buffer.append(buf, 2);
1294 for(core::map<u16, bool>::Iterator
1295 i = added_objects.getIterator();
1296 i.atEnd()==false; i++)
1299 u16 id = i.getNode()->getKey();
1300 ServerActiveObject* obj = m_env.getActiveObject(id);
1303 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1305 dstream<<"WARNING: "<<__FUNCTION_NAME
1306 <<": NULL object"<<std::endl;
1308 type = obj->getType();
1310 // Add to data buffer for sending
1311 writeU16((u8*)buf, id);
1312 data_buffer.append(buf, 2);
1313 writeU8((u8*)buf, type);
1314 data_buffer.append(buf, 1);
1316 data_buffer.append(serializeLongString(
1317 obj->getClientInitializationData()));
1319 // Add to known objects
1320 client->m_known_objects.insert(i.getNode()->getKey(), false);
1323 obj->m_known_by_count++;
1327 SharedBuffer<u8> reply(2 + data_buffer.size());
1328 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1329 memcpy((char*)&reply[2], data_buffer.c_str(),
1330 data_buffer.size());
1332 m_con.Send(client->peer_id, 0, reply, true);
1334 dstream<<"INFO: Server: Sent object remove/add: "
1335 <<removed_objects.size()<<" removed, "
1336 <<added_objects.size()<<" added, "
1337 <<"packet size is "<<reply.getSize()<<std::endl;
1342 Collect a list of all the objects known by the clients
1343 and report it back to the environment.
1346 core::map<u16, bool> all_known_objects;
1348 for(core::map<u16, RemoteClient*>::Iterator
1349 i = m_clients.getIterator();
1350 i.atEnd() == false; i++)
1352 RemoteClient *client = i.getNode()->getValue();
1353 // Go through all known objects of client
1354 for(core::map<u16, bool>::Iterator
1355 i = client->m_known_objects.getIterator();
1356 i.atEnd()==false; i++)
1358 u16 id = i.getNode()->getKey();
1359 all_known_objects[id] = true;
1363 m_env.setKnownActiveObjects(whatever);
1369 Send object messages
1372 JMutexAutoLock envlock(m_env_mutex);
1373 JMutexAutoLock conlock(m_con_mutex);
1376 // Value = data sent by object
1377 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1379 // Get active object messages from environment
1382 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1386 core::list<ActiveObjectMessage>* message_list = NULL;
1387 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1388 n = buffered_messages.find(aom.id);
1391 message_list = new core::list<ActiveObjectMessage>;
1392 buffered_messages.insert(aom.id, message_list);
1396 message_list = n->getValue();
1398 message_list->push_back(aom);
1401 // Route data to every client
1402 for(core::map<u16, RemoteClient*>::Iterator
1403 i = m_clients.getIterator();
1404 i.atEnd()==false; i++)
1406 RemoteClient *client = i.getNode()->getValue();
1407 std::string reliable_data;
1408 std::string unreliable_data;
1409 // Go through all objects in message buffer
1410 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1411 j = buffered_messages.getIterator();
1412 j.atEnd()==false; j++)
1414 // If object is not known by client, skip it
1415 u16 id = j.getNode()->getKey();
1416 if(client->m_known_objects.find(id) == NULL)
1418 // Get message list of object
1419 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1420 // Go through every message
1421 for(core::list<ActiveObjectMessage>::Iterator
1422 k = list->begin(); k != list->end(); k++)
1424 // Compose the full new data with header
1425 ActiveObjectMessage aom = *k;
1426 std::string new_data;
1429 writeU16((u8*)&buf[0], aom.id);
1430 new_data.append(buf, 2);
1432 new_data += serializeString(aom.datastring);
1433 // Add data to buffer
1435 reliable_data += new_data;
1437 unreliable_data += new_data;
1441 reliable_data and unreliable_data are now ready.
1444 if(reliable_data.size() > 0)
1446 SharedBuffer<u8> reply(2 + reliable_data.size());
1447 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1448 memcpy((char*)&reply[2], reliable_data.c_str(),
1449 reliable_data.size());
1451 m_con.Send(client->peer_id, 0, reply, true);
1453 if(unreliable_data.size() > 0)
1455 SharedBuffer<u8> reply(2 + unreliable_data.size());
1456 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1457 memcpy((char*)&reply[2], unreliable_data.c_str(),
1458 unreliable_data.size());
1459 // Send as unreliable
1460 m_con.Send(client->peer_id, 0, reply, false);
1463 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1465 dstream<<"INFO: Server: Size of object message data: "
1466 <<"reliable: "<<reliable_data.size()
1467 <<", unreliable: "<<unreliable_data.size()
1472 // Clear buffered_messages
1473 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1474 i = buffered_messages.getIterator();
1475 i.atEnd()==false; i++)
1477 delete i.getNode()->getValue();
1481 } // enable_experimental
1484 Send queued-for-sending map edit events.
1487 while(m_unsent_map_edit_queue.size() != 0)
1489 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1491 if(event->type == MEET_ADDNODE)
1493 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1494 sendAddNode(event->p, event->n, event->already_known_by_peer);
1496 else if(event->type == MEET_REMOVENODE)
1498 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1499 sendRemoveNode(event->p, event->already_known_by_peer);
1501 else if(event->type == MEET_OTHER)
1503 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1508 dstream<<"WARNING: Server: Unknown MapEditEvent "
1509 <<((u32)event->type)<<std::endl;
1517 Send object positions
1518 TODO: Get rid of MapBlockObjects
1521 float &counter = m_objectdata_timer;
1523 if(counter >= g_settings.getFloat("objectdata_interval"))
1525 JMutexAutoLock lock1(m_env_mutex);
1526 JMutexAutoLock lock2(m_con_mutex);
1527 SendObjectData(counter);
1537 JMutexAutoLock envlock(m_env_mutex);
1538 JMutexAutoLock conlock(m_con_mutex);
1540 core::map<v3s16, MapBlock*> changed_blocks;
1541 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1543 for(core::map<v3s16, MapBlock*>::Iterator
1544 i = changed_blocks.getIterator();
1545 i.atEnd() == false; i++)
1547 MapBlock *block = i.getNode()->getValue();
1549 for(core::map<u16, RemoteClient*>::Iterator
1550 i = m_clients.getIterator();
1551 i.atEnd()==false; i++)
1553 RemoteClient *client = i.getNode()->getValue();
1554 client->SetBlockNotSent(block->getPos());
1560 Trigger emergethread (it somehow gets to a non-triggered but
1561 bysy state sometimes)
1564 float &counter = m_emergethread_trigger_timer;
1570 m_emergethread.trigger();
1576 float &counter = m_savemap_timer;
1578 if(counter >= g_settings.getFloat("server_map_save_interval"))
1582 JMutexAutoLock lock(m_env_mutex);
1584 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1586 // Save only changed parts
1587 m_env.getMap().save(true);
1589 // Delete unused sectors
1590 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1591 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1592 if(deleted_count > 0)
1594 dout_server<<"Server: Unloaded "<<deleted_count
1595 <<" sectors from memory"<<std::endl;
1599 m_env.serializePlayers(m_mapsavedir);
1605 void Server::Receive()
1607 DSTACK(__FUNCTION_NAME);
1608 u32 data_maxsize = 10000;
1609 Buffer<u8> data(data_maxsize);
1614 JMutexAutoLock conlock(m_con_mutex);
1615 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1618 // This has to be called so that the client list gets synced
1619 // with the peer list of the connection
1620 handlePeerChanges();
1622 ProcessData(*data, datasize, peer_id);
1624 catch(con::InvalidIncomingDataException &e)
1626 derr_server<<"Server::Receive(): "
1627 "InvalidIncomingDataException: what()="
1628 <<e.what()<<std::endl;
1630 catch(con::PeerNotFoundException &e)
1632 //NOTE: This is not needed anymore
1634 // The peer has been disconnected.
1635 // Find the associated player and remove it.
1637 /*JMutexAutoLock envlock(m_env_mutex);
1639 dout_server<<"ServerThread: peer_id="<<peer_id
1640 <<" has apparently closed connection. "
1641 <<"Removing player."<<std::endl;
1643 m_env.removePlayer(peer_id);*/
1647 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1649 DSTACK(__FUNCTION_NAME);
1650 // Environment is locked first.
1651 JMutexAutoLock envlock(m_env_mutex);
1652 JMutexAutoLock conlock(m_con_mutex);
1656 peer = m_con.GetPeer(peer_id);
1658 catch(con::PeerNotFoundException &e)
1660 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1661 <<peer_id<<" not found"<<std::endl;
1665 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1673 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1675 if(command == TOSERVER_INIT)
1677 // [0] u16 TOSERVER_INIT
1678 // [2] u8 SER_FMT_VER_HIGHEST
1679 // [3] u8[20] player_name
1684 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1685 <<peer->id<<std::endl;
1687 // First byte after command is maximum supported
1688 // serialization version
1689 u8 client_max = data[2];
1690 u8 our_max = SER_FMT_VER_HIGHEST;
1691 // Use the highest version supported by both
1692 u8 deployed = core::min_(client_max, our_max);
1693 // If it's lower than the lowest supported, give up.
1694 if(deployed < SER_FMT_VER_LOWEST)
1695 deployed = SER_FMT_VER_INVALID;
1697 //peer->serialization_version = deployed;
1698 getClient(peer->id)->pending_serialization_version = deployed;
1700 if(deployed == SER_FMT_VER_INVALID)
1702 derr_server<<DTIME<<"Server: Cannot negotiate "
1703 "serialization version with peer "
1704 <<peer_id<<std::endl;
1713 const u32 playername_size = 20;
1714 char playername[playername_size];
1715 for(u32 i=0; i<playername_size-1; i++)
1717 playername[i] = data[3+i];
1719 playername[playername_size-1] = 0;
1722 Player *player = emergePlayer(playername, "", peer_id);
1723 //Player *player = m_env.getPlayer(peer_id);
1726 // DEBUG: Test serialization
1727 std::ostringstream test_os;
1728 player->serialize(test_os);
1729 dstream<<"Player serialization test: \""<<test_os.str()
1731 std::istringstream test_is(test_os.str());
1732 player->deSerialize(test_is);
1735 // If failed, cancel
1738 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1739 <<": failed to emerge player"<<std::endl;
1744 // If a client is already connected to the player, cancel
1745 if(player->peer_id != 0)
1747 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1748 <<" tried to connect to "
1749 "an already connected player (peer_id="
1750 <<player->peer_id<<")"<<std::endl;
1753 // Set client of player
1754 player->peer_id = peer_id;
1757 // Check if player doesn't exist
1759 throw con::InvalidIncomingDataException
1760 ("Server::ProcessData(): INIT: Player doesn't exist");
1762 /*// update name if it was supplied
1763 if(datasize >= 20+3)
1766 player->updateName((const char*)&data[3]);
1769 // Now answer with a TOCLIENT_INIT
1771 SharedBuffer<u8> reply(2+1+6+8);
1772 writeU16(&reply[0], TOCLIENT_INIT);
1773 writeU8(&reply[2], deployed);
1774 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1775 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1778 m_con.Send(peer_id, 0, reply, true);
1782 if(command == TOSERVER_INIT2)
1784 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1785 <<peer->id<<std::endl;
1788 getClient(peer->id)->serialization_version
1789 = getClient(peer->id)->pending_serialization_version;
1792 Send some initialization data
1795 // Send player info to all players
1798 // Send inventory to player
1799 SendInventory(peer->id);
1803 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1804 m_time_of_day.get());
1805 m_con.Send(peer->id, 0, data, true);
1808 // Send information about server to player in chat
1809 SendChatMessage(peer_id, getStatusString());
1811 // Send information about joining in chat
1813 std::wstring name = L"unknown";
1814 Player *player = m_env.getPlayer(peer_id);
1816 name = narrow_to_wide(player->getName());
1818 std::wstring message;
1821 message += L" joined game";
1822 BroadcastChatMessage(message);
1828 if(peer_ser_ver == SER_FMT_VER_INVALID)
1830 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1831 " serialization format invalid or not initialized."
1832 " Skipping incoming command="<<command<<std::endl;
1836 Player *player = m_env.getPlayer(peer_id);
1839 derr_server<<"Server::ProcessData(): Cancelling: "
1840 "No player for peer_id="<<peer_id
1844 if(command == TOSERVER_PLAYERPOS)
1846 if(datasize < 2+12+12+4+4)
1850 v3s32 ps = readV3S32(&data[start+2]);
1851 v3s32 ss = readV3S32(&data[start+2+12]);
1852 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1853 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1854 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1855 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1856 pitch = wrapDegrees(pitch);
1857 yaw = wrapDegrees(yaw);
1858 player->setPosition(position);
1859 player->setSpeed(speed);
1860 player->setPitch(pitch);
1861 player->setYaw(yaw);
1863 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1864 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1865 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1867 else if(command == TOSERVER_GOTBLOCKS)
1880 u16 count = data[2];
1881 for(u16 i=0; i<count; i++)
1883 if((s16)datasize < 2+1+(i+1)*6)
1884 throw con::InvalidIncomingDataException
1885 ("GOTBLOCKS length is too short");
1886 v3s16 p = readV3S16(&data[2+1+i*6]);
1887 /*dstream<<"Server: GOTBLOCKS ("
1888 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1889 RemoteClient *client = getClient(peer_id);
1890 client->GotBlock(p);
1893 else if(command == TOSERVER_DELETEDBLOCKS)
1906 u16 count = data[2];
1907 for(u16 i=0; i<count; i++)
1909 if((s16)datasize < 2+1+(i+1)*6)
1910 throw con::InvalidIncomingDataException
1911 ("DELETEDBLOCKS length is too short");
1912 v3s16 p = readV3S16(&data[2+1+i*6]);
1913 /*dstream<<"Server: DELETEDBLOCKS ("
1914 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1915 RemoteClient *client = getClient(peer_id);
1916 client->SetBlockNotSent(p);
1919 else if(command == TOSERVER_CLICK_OBJECT)
1926 [2] u8 button (0=left, 1=right)
1931 u8 button = readU8(&data[2]);
1933 p.X = readS16(&data[3]);
1934 p.Y = readS16(&data[5]);
1935 p.Z = readS16(&data[7]);
1936 s16 id = readS16(&data[9]);
1937 //u16 item_i = readU16(&data[11]);
1939 MapBlock *block = NULL;
1942 block = m_env.getMap().getBlockNoCreate(p);
1944 catch(InvalidPositionException &e)
1946 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1950 MapBlockObject *obj = block->getObject(id);
1954 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1958 //TODO: Check that object is reasonably close
1963 InventoryList *ilist = player->inventory.getList("main");
1964 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1967 // Skip if inventory has no free space
1968 if(ilist->getUsedSlots() == ilist->getSize())
1970 dout_server<<"Player inventory has no free space"<<std::endl;
1975 Create the inventory item
1977 InventoryItem *item = NULL;
1978 // If it is an item-object, take the item from it
1979 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1981 item = ((ItemObject*)obj)->createInventoryItem();
1983 // Else create an item of the object
1986 item = new MapBlockObjectItem
1987 (obj->getInventoryString());
1990 // Add to inventory and send inventory
1991 ilist->addItem(item);
1992 SendInventory(player->peer_id);
1995 // Remove from block
1996 block->removeObject(id);
1999 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2007 [2] u8 button (0=left, 1=right)
2011 u8 button = readU8(&data[2]);
2012 u16 id = readS16(&data[3]);
2013 //u16 item_i = readU16(&data[11]);
2015 ServerActiveObject *obj = m_env.getActiveObject(id);
2019 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2024 //TODO: Check that object is reasonably close
2026 // Left click, pick object up (usually)
2029 InventoryList *ilist = player->inventory.getList("main");
2030 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2033 // Skip if inventory has no free space
2034 if(ilist->getUsedSlots() == ilist->getSize())
2036 dout_server<<"Player inventory has no free space"<<std::endl;
2040 // Skip if object has been removed
2045 Create the inventory item
2047 InventoryItem *item = obj->createPickedUpItem();
2051 // Add to inventory and send inventory
2052 ilist->addItem(item);
2053 SendInventory(player->peer_id);
2055 // Remove object from environment
2056 obj->m_removed = true;
2061 else if(command == TOSERVER_GROUND_ACTION)
2069 [3] v3s16 nodepos_undersurface
2070 [9] v3s16 nodepos_abovesurface
2075 2: stop digging (all parameters ignored)
2076 3: digging completed
2078 u8 action = readU8(&data[2]);
2080 p_under.X = readS16(&data[3]);
2081 p_under.Y = readS16(&data[5]);
2082 p_under.Z = readS16(&data[7]);
2084 p_over.X = readS16(&data[9]);
2085 p_over.Y = readS16(&data[11]);
2086 p_over.Z = readS16(&data[13]);
2087 u16 item_i = readU16(&data[15]);
2089 //TODO: Check that target is reasonably close
2097 NOTE: This can be used in the future to check if
2098 somebody is cheating, by checking the timing.
2105 else if(action == 2)
2108 RemoteClient *client = getClient(peer->id);
2109 JMutexAutoLock digmutex(client->m_dig_mutex);
2110 client->m_dig_tool_item = -1;
2115 3: Digging completed
2117 else if(action == 3)
2119 // Mandatory parameter; actually used for nothing
2120 core::map<v3s16, MapBlock*> modified_blocks;
2123 u8 mineral = MINERAL_NONE;
2125 bool cannot_remove_node = false;
2129 MapNode n = m_env.getMap().getNode(p_under);
2131 mineral = n.getMineral();
2132 // Get material at position
2134 // If not yet cancelled
2135 if(cannot_remove_node == false)
2137 // If it's not diggable, do nothing
2138 if(content_diggable(material) == false)
2140 derr_server<<"Server: Not finishing digging: "
2141 <<"Node not diggable"
2143 cannot_remove_node = true;
2146 // If not yet cancelled
2147 if(cannot_remove_node == false)
2149 // Get node metadata
2150 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2151 if(meta && meta->nodeRemovalDisabled() == true)
2153 derr_server<<"Server: Not finishing digging: "
2154 <<"Node metadata disables removal"
2156 cannot_remove_node = true;
2160 catch(InvalidPositionException &e)
2162 derr_server<<"Server: Not finishing digging: Node not found."
2163 <<" Adding block to emerge queue."
2165 m_emerge_queue.addBlock(peer_id,
2166 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2167 cannot_remove_node = true;
2171 If node can't be removed, set block to be re-sent to
2174 if(cannot_remove_node)
2176 derr_server<<"Server: Not finishing digging."<<std::endl;
2178 // Client probably has wrong data.
2179 // Set block not sent, so that client will get
2181 dstream<<"Client "<<peer_id<<" tried to dig "
2182 <<"node; but node cannot be removed."
2183 <<" setting MapBlock not sent."<<std::endl;
2184 RemoteClient *client = getClient(peer_id);
2185 v3s16 blockpos = getNodeBlockPos(p_under);
2186 client->SetBlockNotSent(blockpos);
2192 Send the removal to all other clients.
2193 - If other player is close, send REMOVENODE
2194 - Otherwise set blocks not sent
2196 core::list<u16> far_players;
2197 sendRemoveNode(p_under, peer_id, &far_players, 100);
2200 Update and send inventory
2203 if(g_settings.getBool("creative_mode") == false)
2208 InventoryList *mlist = player->inventory.getList("main");
2211 InventoryItem *item = mlist->getItem(item_i);
2212 if(item && (std::string)item->getName() == "ToolItem")
2214 ToolItem *titem = (ToolItem*)item;
2215 std::string toolname = titem->getToolName();
2217 // Get digging properties for material and tool
2218 DiggingProperties prop =
2219 getDiggingProperties(material, toolname);
2221 if(prop.diggable == false)
2223 derr_server<<"Server: WARNING: Player digged"
2224 <<" with impossible material + tool"
2225 <<" combination"<<std::endl;
2228 bool weared_out = titem->addWear(prop.wear);
2232 mlist->deleteItem(item_i);
2238 Add dug item to inventory
2241 InventoryItem *item = NULL;
2243 if(mineral != MINERAL_NONE)
2244 item = getDiggedMineralItem(mineral);
2249 std::string &dug_s = content_features(material).dug_item;
2252 std::istringstream is(dug_s, std::ios::binary);
2253 item = InventoryItem::deSerialize(is);
2259 // Add a item to inventory
2260 player->inventory.addItem("main", item);
2263 SendInventory(player->peer_id);
2269 (this takes some time so it is done after the quick stuff)
2271 m_ignore_map_edit_events = true;
2272 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2273 m_ignore_map_edit_events = false;
2276 Set blocks not sent to far players
2278 for(core::list<u16>::Iterator
2279 i = far_players.begin();
2280 i != far_players.end(); i++)
2283 RemoteClient *client = getClient(peer_id);
2286 client->SetBlocksNotSent(modified_blocks);
2293 else if(action == 1)
2296 InventoryList *ilist = player->inventory.getList("main");
2301 InventoryItem *item = ilist->getItem(item_i);
2303 // If there is no item, it is not possible to add it anywhere
2308 Handle material items
2310 if(std::string("MaterialItem") == item->getName())
2313 // Don't add a node if this is not a free space
2314 MapNode n2 = m_env.getMap().getNode(p_over);
2315 if(content_buildable_to(n2.d) == false)
2317 // Client probably has wrong data.
2318 // Set block not sent, so that client will get
2320 dstream<<"Client "<<peer_id<<" tried to place"
2321 <<" node in invalid position; setting"
2322 <<" MapBlock not sent."<<std::endl;
2323 RemoteClient *client = getClient(peer_id);
2324 v3s16 blockpos = getNodeBlockPos(p_over);
2325 client->SetBlockNotSent(blockpos);
2329 catch(InvalidPositionException &e)
2331 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2332 <<" Adding block to emerge queue."
2334 m_emerge_queue.addBlock(peer_id,
2335 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2339 // Reset build time counter
2340 getClient(peer->id)->m_time_from_building = 0.0;
2343 MaterialItem *mitem = (MaterialItem*)item;
2345 n.d = mitem->getMaterial();
2346 if(content_features(n.d).wall_mounted)
2347 n.dir = packDir(p_under - p_over);
2352 core::list<u16> far_players;
2353 sendAddNode(p_over, n, 0, &far_players, 100);
2358 InventoryList *ilist = player->inventory.getList("main");
2359 if(g_settings.getBool("creative_mode") == false && ilist)
2361 // Remove from inventory and send inventory
2362 if(mitem->getCount() == 1)
2363 ilist->deleteItem(item_i);
2367 SendInventory(peer_id);
2373 This takes some time so it is done after the quick stuff
2375 core::map<v3s16, MapBlock*> modified_blocks;
2376 m_ignore_map_edit_events = true;
2377 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2378 m_ignore_map_edit_events = false;
2381 Set blocks not sent to far players
2383 for(core::list<u16>::Iterator
2384 i = far_players.begin();
2385 i != far_players.end(); i++)
2388 RemoteClient *client = getClient(peer_id);
2391 client->SetBlocksNotSent(modified_blocks);
2395 Calculate special events
2398 /*if(n.d == CONTENT_MESE)
2401 for(s16 z=-1; z<=1; z++)
2402 for(s16 y=-1; y<=1; y++)
2403 for(s16 x=-1; x<=1; x++)
2410 Place other item (not a block)
2414 v3s16 blockpos = getNodeBlockPos(p_over);
2417 Check that the block is loaded so that the item
2418 can properly be added to the static list too
2420 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2423 derr_server<<"Error while placing object: "
2424 "block not found"<<std::endl;
2428 dout_server<<"Placing a miscellaneous item on map"
2431 // Calculate a position for it
2432 v3f pos = intToFloat(p_over, BS);
2434 pos.Y -= BS*0.25; // let it drop a bit
2436 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2437 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2442 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2446 derr_server<<"WARNING: item resulted in NULL object, "
2447 <<"not placing onto map"
2452 // Add the object to the environment
2453 m_env.addActiveObject(obj);
2455 dout_server<<"Placed object"<<std::endl;
2457 // If item has count<=1, delete it
2458 if(item->getCount() <= 1)
2460 InventoryList *ilist = player->inventory.getList("main");
2461 if(g_settings.getBool("creative_mode") == false && ilist)
2463 // Remove from inventory and send inventory
2464 ilist->deleteItem(item_i);
2466 SendInventory(peer_id);
2469 // Else decrement it
2474 SendInventory(peer_id);
2482 Catch invalid actions
2486 derr_server<<"WARNING: Server: Invalid action "
2487 <<action<<std::endl;
2491 else if(command == TOSERVER_RELEASE)
2500 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2503 else if(command == TOSERVER_SIGNTEXT)
2512 std::string datastring((char*)&data[2], datasize-2);
2513 std::istringstream is(datastring, std::ios_base::binary);
2516 is.read((char*)buf, 6);
2517 v3s16 blockpos = readV3S16(buf);
2518 is.read((char*)buf, 2);
2519 s16 id = readS16(buf);
2520 is.read((char*)buf, 2);
2521 u16 textlen = readU16(buf);
2523 for(u16 i=0; i<textlen; i++)
2525 is.read((char*)buf, 1);
2526 text += (char)buf[0];
2529 MapBlock *block = NULL;
2532 block = m_env.getMap().getBlockNoCreate(blockpos);
2534 catch(InvalidPositionException &e)
2536 derr_server<<"Error while setting sign text: "
2537 "block not found"<<std::endl;
2541 MapBlockObject *obj = block->getObject(id);
2544 derr_server<<"Error while setting sign text: "
2545 "object not found"<<std::endl;
2549 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2551 derr_server<<"Error while setting sign text: "
2552 "object is not a sign"<<std::endl;
2556 ((SignObject*)obj)->setText(text);
2558 obj->getBlock()->setChangedFlag();
2560 else if(command == TOSERVER_SIGNNODETEXT)
2568 std::string datastring((char*)&data[2], datasize-2);
2569 std::istringstream is(datastring, std::ios_base::binary);
2572 is.read((char*)buf, 6);
2573 v3s16 p = readV3S16(buf);
2574 is.read((char*)buf, 2);
2575 u16 textlen = readU16(buf);
2577 for(u16 i=0; i<textlen; i++)
2579 is.read((char*)buf, 1);
2580 text += (char)buf[0];
2583 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2586 if(meta->typeId() != CONTENT_SIGN_WALL)
2588 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2589 signmeta->setText(text);
2591 v3s16 blockpos = getNodeBlockPos(p);
2592 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2595 block->setChangedFlag();
2598 for(core::map<u16, RemoteClient*>::Iterator
2599 i = m_clients.getIterator();
2600 i.atEnd()==false; i++)
2602 RemoteClient *client = i.getNode()->getValue();
2603 client->SetBlockNotSent(blockpos);
2606 else if(command == TOSERVER_INVENTORY_ACTION)
2608 /*// Ignore inventory changes if in creative mode
2609 if(g_settings.getBool("creative_mode") == true)
2611 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2615 // Strip command and create a stream
2616 std::string datastring((char*)&data[2], datasize-2);
2617 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2618 std::istringstream is(datastring, std::ios_base::binary);
2620 InventoryAction *a = InventoryAction::deSerialize(is);
2625 c.current_player = player;
2628 Handle craftresult specially if not in creative mode
2630 bool disable_action = false;
2631 if(a->getType() == IACTION_MOVE
2632 && g_settings.getBool("creative_mode") == false)
2634 IMoveAction *ma = (IMoveAction*)a;
2635 if(ma->to_inv == "current_player" &&
2636 ma->from_inv == "current_player")
2638 // Don't allow moving anything to craftresult
2639 if(ma->to_list == "craftresult")
2642 disable_action = true;
2644 // When something is removed from craftresult
2645 if(ma->from_list == "craftresult")
2647 disable_action = true;
2648 // Remove stuff from craft
2649 InventoryList *clist = player->inventory.getList("craft");
2652 u16 count = ma->count;
2655 clist->decrementMaterials(count);
2658 // Feed action to player inventory
2659 //a->apply(&player->inventory);
2663 // If something appeared in craftresult, throw it
2665 InventoryList *rlist = player->inventory.getList("craftresult");
2666 InventoryList *mlist = player->inventory.getList("main");
2667 if(rlist && mlist && rlist->getUsedSlots() == 1)
2669 InventoryItem *item1 = rlist->changeItem(0, NULL);
2670 mlist->addItem(item1);
2676 if(disable_action == false)
2678 // Feed action to player inventory
2679 //a->apply(&player->inventory);
2687 SendInventory(player->peer_id);
2692 dstream<<"TOSERVER_INVENTORY_ACTION: "
2693 <<"InventoryAction::deSerialize() returned NULL"
2697 else if(command == TOSERVER_CHAT_MESSAGE)
2705 std::string datastring((char*)&data[2], datasize-2);
2706 std::istringstream is(datastring, std::ios_base::binary);
2709 is.read((char*)buf, 2);
2710 u16 len = readU16(buf);
2712 std::wstring message;
2713 for(u16 i=0; i<len; i++)
2715 is.read((char*)buf, 2);
2716 message += (wchar_t)readU16(buf);
2719 // Get player name of this client
2720 std::wstring name = narrow_to_wide(player->getName());
2722 // Line to send to players
2724 // Whether to send to the player that sent the line
2725 bool send_to_sender = false;
2726 // Whether to send to other players
2727 bool send_to_others = false;
2730 std::wstring commandprefix = L"/#";
2731 if(message.substr(0, commandprefix.size()) == commandprefix)
2733 line += L"Server: ";
2735 message = message.substr(commandprefix.size());
2736 // Get player name as narrow string
2737 std::string name_s = player->getName();
2738 // Convert message to narrow string
2739 std::string message_s = wide_to_narrow(message);
2740 // Operator is the single name defined in config.
2741 std::string operator_name = g_settings.get("name");
2742 bool is_operator = (operator_name != "" &&
2743 wide_to_narrow(name) == operator_name);
2744 bool valid_command = false;
2745 if(message_s == "help")
2747 line += L"-!- Available commands: ";
2751 line += L"shutdown setting ";
2756 send_to_sender = true;
2757 valid_command = true;
2759 else if(message_s == "status")
2761 line = getStatusString();
2762 send_to_sender = true;
2763 valid_command = true;
2765 else if(is_operator)
2767 if(message_s == "shutdown")
2769 dstream<<DTIME<<" Server: Operator requested shutdown."
2771 m_shutdown_requested.set(true);
2773 line += L"*** Server shutting down (operator request)";
2774 send_to_sender = true;
2775 valid_command = true;
2777 else if(message_s.substr(0,8) == "setting ")
2779 std::string confline = message_s.substr(8);
2780 g_settings.parseConfigLine(confline);
2781 line += L"-!- Setting changed.";
2782 send_to_sender = true;
2783 valid_command = true;
2787 if(valid_command == false)
2789 line += L"-!- Invalid command: " + message;
2790 send_to_sender = true;
2801 send_to_others = true;
2806 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2809 Send the message to clients
2811 for(core::map<u16, RemoteClient*>::Iterator
2812 i = m_clients.getIterator();
2813 i.atEnd() == false; i++)
2815 // Get client and check that it is valid
2816 RemoteClient *client = i.getNode()->getValue();
2817 assert(client->peer_id == i.getNode()->getKey());
2818 if(client->serialization_version == SER_FMT_VER_INVALID)
2822 bool sender_selected = (peer_id == client->peer_id);
2823 if(sender_selected == true && send_to_sender == false)
2825 if(sender_selected == false && send_to_others == false)
2828 SendChatMessage(client->peer_id, line);
2834 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2835 "unknown command "<<command<<std::endl;
2839 catch(SendFailedException &e)
2841 derr_server<<"Server::ProcessData(): SendFailedException: "
2847 void Server::onMapEditEvent(MapEditEvent *event)
2849 dstream<<"Server::onMapEditEvent()"<<std::endl;
2850 if(m_ignore_map_edit_events)
2852 MapEditEvent *e = event->clone();
2853 m_unsent_map_edit_queue.push_back(e);
2856 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2858 if(id == "current_player")
2860 assert(c->current_player);
2861 return &(c->current_player->inventory);
2865 std::string id0 = fn.next(":");
2867 if(id0 == "nodemeta")
2870 p.X = stoi(fn.next(","));
2871 p.Y = stoi(fn.next(","));
2872 p.Z = stoi(fn.next(","));
2873 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2875 return meta->getInventory();
2876 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2877 <<"no metadata found"<<std::endl;
2881 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2884 void Server::inventoryModified(InventoryContext *c, std::string id)
2886 if(id == "current_player")
2888 assert(c->current_player);
2890 SendInventory(c->current_player->peer_id);
2895 std::string id0 = fn.next(":");
2897 if(id0 == "nodemeta")
2900 p.X = stoi(fn.next(","));
2901 p.Y = stoi(fn.next(","));
2902 p.Z = stoi(fn.next(","));
2903 v3s16 blockpos = getNodeBlockPos(p);
2905 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2907 meta->inventoryModified();
2909 for(core::map<u16, RemoteClient*>::Iterator
2910 i = m_clients.getIterator();
2911 i.atEnd()==false; i++)
2913 RemoteClient *client = i.getNode()->getValue();
2914 client->SetBlockNotSent(blockpos);
2920 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2923 core::list<PlayerInfo> Server::getPlayerInfo()
2925 DSTACK(__FUNCTION_NAME);
2926 JMutexAutoLock envlock(m_env_mutex);
2927 JMutexAutoLock conlock(m_con_mutex);
2929 core::list<PlayerInfo> list;
2931 core::list<Player*> players = m_env.getPlayers();
2933 core::list<Player*>::Iterator i;
2934 for(i = players.begin();
2935 i != players.end(); i++)
2939 Player *player = *i;
2942 con::Peer *peer = m_con.GetPeer(player->peer_id);
2943 // Copy info from peer to info struct
2945 info.address = peer->address;
2946 info.avg_rtt = peer->avg_rtt;
2948 catch(con::PeerNotFoundException &e)
2950 // Set dummy peer info
2952 info.address = Address(0,0,0,0,0);
2956 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2957 info.position = player->getPosition();
2959 list.push_back(info);
2966 void Server::peerAdded(con::Peer *peer)
2968 DSTACK(__FUNCTION_NAME);
2969 dout_server<<"Server::peerAdded(): peer->id="
2970 <<peer->id<<std::endl;
2973 c.type = PEER_ADDED;
2974 c.peer_id = peer->id;
2976 m_peer_change_queue.push_back(c);
2979 void Server::deletingPeer(con::Peer *peer, bool timeout)
2981 DSTACK(__FUNCTION_NAME);
2982 dout_server<<"Server::deletingPeer(): peer->id="
2983 <<peer->id<<", timeout="<<timeout<<std::endl;
2986 c.type = PEER_REMOVED;
2987 c.peer_id = peer->id;
2988 c.timeout = timeout;
2989 m_peer_change_queue.push_back(c);
2992 void Server::SendObjectData(float dtime)
2994 DSTACK(__FUNCTION_NAME);
2996 core::map<v3s16, bool> stepped_blocks;
2998 for(core::map<u16, RemoteClient*>::Iterator
2999 i = m_clients.getIterator();
3000 i.atEnd() == false; i++)
3002 u16 peer_id = i.getNode()->getKey();
3003 RemoteClient *client = i.getNode()->getValue();
3004 assert(client->peer_id == peer_id);
3006 if(client->serialization_version == SER_FMT_VER_INVALID)
3009 client->SendObjectData(this, dtime, stepped_blocks);
3013 void Server::SendPlayerInfos()
3015 DSTACK(__FUNCTION_NAME);
3017 //JMutexAutoLock envlock(m_env_mutex);
3019 // Get connected players
3020 core::list<Player*> players = m_env.getPlayers(true);
3022 u32 player_count = players.getSize();
3023 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3025 SharedBuffer<u8> data(datasize);
3026 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3029 core::list<Player*>::Iterator i;
3030 for(i = players.begin();
3031 i != players.end(); i++)
3033 Player *player = *i;
3035 /*dstream<<"Server sending player info for player with "
3036 "peer_id="<<player->peer_id<<std::endl;*/
3038 writeU16(&data[start], player->peer_id);
3039 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3040 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3041 start += 2+PLAYERNAME_SIZE;
3044 //JMutexAutoLock conlock(m_con_mutex);
3047 m_con.SendToAll(0, data, true);
3050 void Server::SendInventory(u16 peer_id)
3052 DSTACK(__FUNCTION_NAME);
3054 Player* player = m_env.getPlayer(peer_id);
3057 Calculate crafting stuff
3059 if(g_settings.getBool("creative_mode") == false)
3061 InventoryList *clist = player->inventory.getList("craft");
3062 InventoryList *rlist = player->inventory.getList("craftresult");
3065 rlist->clearItems();
3069 InventoryItem *items[9];
3070 for(u16 i=0; i<9; i++)
3072 items[i] = clist->getItem(i);
3081 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3082 if(checkItemCombination(items, specs))
3084 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3093 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3094 if(checkItemCombination(items, specs))
3096 rlist->addItem(new CraftItem("Stick", 4));
3105 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3106 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3107 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3108 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3109 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3110 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3111 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3112 if(checkItemCombination(items, specs))
3114 //rlist->addItem(new MapBlockObjectItem("Sign"));
3115 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3124 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3125 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3126 if(checkItemCombination(items, specs))
3128 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3137 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3138 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3139 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3140 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3141 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3142 if(checkItemCombination(items, specs))
3144 rlist->addItem(new ToolItem("WPick", 0));
3153 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3154 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3155 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3156 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3157 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3158 if(checkItemCombination(items, specs))
3160 rlist->addItem(new ToolItem("STPick", 0));
3169 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3170 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3171 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3172 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3173 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3174 if(checkItemCombination(items, specs))
3176 rlist->addItem(new ToolItem("SteelPick", 0));
3185 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3186 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3187 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3188 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3189 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3190 if(checkItemCombination(items, specs))
3192 rlist->addItem(new ToolItem("MesePick", 0));
3201 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3202 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3203 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3204 if(checkItemCombination(items, specs))
3206 rlist->addItem(new ToolItem("WShovel", 0));
3215 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3216 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3217 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3218 if(checkItemCombination(items, specs))
3220 rlist->addItem(new ToolItem("STShovel", 0));
3229 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3230 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3231 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3232 if(checkItemCombination(items, specs))
3234 rlist->addItem(new ToolItem("SteelShovel", 0));
3243 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3244 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3245 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3246 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3247 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3248 if(checkItemCombination(items, specs))
3250 rlist->addItem(new ToolItem("WAxe", 0));
3259 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3260 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3261 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3262 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3263 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3264 if(checkItemCombination(items, specs))
3266 rlist->addItem(new ToolItem("STAxe", 0));
3275 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3276 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3277 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3278 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3279 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3280 if(checkItemCombination(items, specs))
3282 rlist->addItem(new ToolItem("SteelAxe", 0));
3291 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3292 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3293 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3294 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3295 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3296 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3297 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3298 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3299 if(checkItemCombination(items, specs))
3301 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3310 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3311 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3312 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3313 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3314 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3315 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3316 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3317 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3318 if(checkItemCombination(items, specs))
3320 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3329 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3330 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3331 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3332 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3333 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3334 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3335 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3336 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3337 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3338 if(checkItemCombination(items, specs))
3340 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3346 } // if creative_mode == false
3352 std::ostringstream os;
3353 //os.imbue(std::locale("C"));
3355 player->inventory.serialize(os);
3357 std::string s = os.str();
3359 SharedBuffer<u8> data(s.size()+2);
3360 writeU16(&data[0], TOCLIENT_INVENTORY);
3361 memcpy(&data[2], s.c_str(), s.size());
3364 m_con.Send(peer_id, 0, data, true);
3367 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3369 DSTACK(__FUNCTION_NAME);
3371 std::ostringstream os(std::ios_base::binary);
3375 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3376 os.write((char*)buf, 2);
3379 writeU16(buf, message.size());
3380 os.write((char*)buf, 2);
3383 for(u32 i=0; i<message.size(); i++)
3387 os.write((char*)buf, 2);
3391 std::string s = os.str();
3392 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3394 m_con.Send(peer_id, 0, data, true);
3397 void Server::BroadcastChatMessage(const std::wstring &message)
3399 for(core::map<u16, RemoteClient*>::Iterator
3400 i = m_clients.getIterator();
3401 i.atEnd() == false; i++)
3403 // Get client and check that it is valid
3404 RemoteClient *client = i.getNode()->getValue();
3405 assert(client->peer_id == i.getNode()->getKey());
3406 if(client->serialization_version == SER_FMT_VER_INVALID)
3409 SendChatMessage(client->peer_id, message);
3413 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3414 core::list<u16> *far_players, float far_d_nodes)
3416 float maxd = far_d_nodes*BS;
3417 v3f p_f = intToFloat(p, BS);
3421 SharedBuffer<u8> reply(replysize);
3422 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3423 writeS16(&reply[2], p.X);
3424 writeS16(&reply[4], p.Y);
3425 writeS16(&reply[6], p.Z);
3427 for(core::map<u16, RemoteClient*>::Iterator
3428 i = m_clients.getIterator();
3429 i.atEnd() == false; i++)
3431 // Get client and check that it is valid
3432 RemoteClient *client = i.getNode()->getValue();
3433 assert(client->peer_id == i.getNode()->getKey());
3434 if(client->serialization_version == SER_FMT_VER_INVALID)
3437 // Don't send if it's the same one
3438 if(client->peer_id == ignore_id)
3444 Player *player = m_env.getPlayer(client->peer_id);
3447 // If player is far away, only set modified blocks not sent
3448 v3f player_pos = player->getPosition();
3449 if(player_pos.getDistanceFrom(p_f) > maxd)
3451 far_players->push_back(client->peer_id);
3458 m_con.Send(client->peer_id, 0, reply, true);
3462 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3463 core::list<u16> *far_players, float far_d_nodes)
3465 float maxd = far_d_nodes*BS;
3466 v3f p_f = intToFloat(p, BS);
3468 for(core::map<u16, RemoteClient*>::Iterator
3469 i = m_clients.getIterator();
3470 i.atEnd() == false; i++)
3472 // Get client and check that it is valid
3473 RemoteClient *client = i.getNode()->getValue();
3474 assert(client->peer_id == i.getNode()->getKey());
3475 if(client->serialization_version == SER_FMT_VER_INVALID)
3478 // Don't send if it's the same one
3479 if(client->peer_id == ignore_id)
3485 Player *player = m_env.getPlayer(client->peer_id);
3488 // If player is far away, only set modified blocks not sent
3489 v3f player_pos = player->getPosition();
3490 if(player_pos.getDistanceFrom(p_f) > maxd)
3492 far_players->push_back(client->peer_id);
3499 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3500 SharedBuffer<u8> reply(replysize);
3501 writeU16(&reply[0], TOCLIENT_ADDNODE);
3502 writeS16(&reply[2], p.X);
3503 writeS16(&reply[4], p.Y);
3504 writeS16(&reply[6], p.Z);
3505 n.serialize(&reply[8], client->serialization_version);
3508 m_con.Send(client->peer_id, 0, reply, true);
3512 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3514 DSTACK(__FUNCTION_NAME);
3516 Create a packet with the block in the right format
3519 std::ostringstream os(std::ios_base::binary);
3520 block->serialize(os, ver);
3521 std::string s = os.str();
3522 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3524 u32 replysize = 8 + blockdata.getSize();
3525 SharedBuffer<u8> reply(replysize);
3526 v3s16 p = block->getPos();
3527 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3528 writeS16(&reply[2], p.X);
3529 writeS16(&reply[4], p.Y);
3530 writeS16(&reply[6], p.Z);
3531 memcpy(&reply[8], *blockdata, blockdata.getSize());
3533 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3534 <<": \tpacket size: "<<replysize<<std::endl;*/
3539 m_con.Send(peer_id, 1, reply, true);
3542 void Server::SendBlocks(float dtime)
3544 DSTACK(__FUNCTION_NAME);
3546 JMutexAutoLock envlock(m_env_mutex);
3547 JMutexAutoLock conlock(m_con_mutex);
3549 //TimeTaker timer("Server::SendBlocks");
3551 core::array<PrioritySortedBlockTransfer> queue;
3553 s32 total_sending = 0;
3555 for(core::map<u16, RemoteClient*>::Iterator
3556 i = m_clients.getIterator();
3557 i.atEnd() == false; i++)
3559 RemoteClient *client = i.getNode()->getValue();
3560 assert(client->peer_id == i.getNode()->getKey());
3562 total_sending += client->SendingCount();
3564 if(client->serialization_version == SER_FMT_VER_INVALID)
3567 client->GetNextBlocks(this, dtime, queue);
3571 // Lowest priority number comes first.
3572 // Lowest is most important.
3575 for(u32 i=0; i<queue.size(); i++)
3577 //TODO: Calculate limit dynamically
3578 if(total_sending >= g_settings.getS32
3579 ("max_simultaneous_block_sends_server_total"))
3582 PrioritySortedBlockTransfer q = queue[i];
3584 MapBlock *block = NULL;
3587 block = m_env.getMap().getBlockNoCreate(q.pos);
3589 catch(InvalidPositionException &e)
3594 RemoteClient *client = getClient(q.peer_id);
3596 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3598 client->SentBlock(q.pos);
3605 RemoteClient* Server::getClient(u16 peer_id)
3607 DSTACK(__FUNCTION_NAME);
3608 //JMutexAutoLock lock(m_con_mutex);
3609 core::map<u16, RemoteClient*>::Node *n;
3610 n = m_clients.find(peer_id);
3611 // A client should exist for all peers
3613 return n->getValue();
3616 std::wstring Server::getStatusString()
3618 std::wostringstream os(std::ios_base::binary);
3621 os<<L"uptime="<<m_uptime.get();
3622 // Information about clients
3624 for(core::map<u16, RemoteClient*>::Iterator
3625 i = m_clients.getIterator();
3626 i.atEnd() == false; i++)
3628 // Get client and check that it is valid
3629 RemoteClient *client = i.getNode()->getValue();
3630 assert(client->peer_id == i.getNode()->getKey());
3631 if(client->serialization_version == SER_FMT_VER_INVALID)
3634 Player *player = m_env.getPlayer(client->peer_id);
3635 // Get name of player
3636 std::wstring name = L"unknown";
3638 name = narrow_to_wide(player->getName());
3639 // Add name to information string
3643 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3644 os<<" WARNING: Map saving is disabled."<<std::endl;
3649 void setCreativeInventory(Player *player)
3651 player->resetInventory();
3653 // Give some good picks
3655 InventoryItem *item = new ToolItem("STPick", 0);
3656 void* r = player->inventory.addItem("main", item);
3660 InventoryItem *item = new ToolItem("MesePick", 0);
3661 void* r = player->inventory.addItem("main", item);
3669 // CONTENT_IGNORE-terminated list
3670 u8 material_items[] = {
3679 CONTENT_WATERSOURCE,
3687 u8 *mip = material_items;
3688 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3690 if(*mip == CONTENT_IGNORE)
3693 InventoryItem *item = new MaterialItem(*mip, 1);
3694 player->inventory.addItem("main", item);
3700 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3703 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3704 player->inventory.addItem("main", item);
3707 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3709 // Skip some materials
3710 if(i == CONTENT_WATER || i == CONTENT_TORCH
3711 || i == CONTENT_COALSTONE)
3714 InventoryItem *item = new MaterialItem(i, 1);
3715 player->inventory.addItem("main", item);
3721 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3722 void* r = player->inventory.addItem("main", item);
3727 Player *Server::emergePlayer(const char *name, const char *password,
3731 Try to get an existing player
3733 Player *player = m_env.getPlayer(name);
3736 // If player is already connected, cancel
3737 if(player->peer_id != 0)
3739 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3744 player->peer_id = peer_id;
3746 // Reset inventory to creative if in creative mode
3747 if(g_settings.getBool("creative_mode"))
3749 setCreativeInventory(player);
3756 If player with the wanted peer_id already exists, cancel.
3758 if(m_env.getPlayer(peer_id) != NULL)
3760 dstream<<"emergePlayer(): Player with wrong name but same"
3761 " peer_id already exists"<<std::endl;
3769 player = new ServerRemotePlayer();
3770 //player->peer_id = c.peer_id;
3771 //player->peer_id = PEER_ID_INEXISTENT;
3772 player->peer_id = peer_id;
3773 player->updateName(name);
3779 dstream<<"Server: Finding spawn place for player \""
3780 <<player->getName()<<"\""<<std::endl;
3784 player->setPosition(intToFloat(v3s16(
3791 s16 groundheight = 0;
3793 // Try to find a good place a few times
3794 for(s32 i=0; i<1000; i++)
3797 // We're going to try to throw the player to this position
3798 nodepos = v2s16(-range + (myrand()%(range*2)),
3799 -range + (myrand()%(range*2)));
3800 v2s16 sectorpos = getNodeSectorPos(nodepos);
3801 // Get sector (NOTE: Don't get because it's slow)
3802 //m_env.getMap().emergeSector(sectorpos);
3803 // Get ground height at point (fallbacks to heightmap function)
3804 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3805 // Don't go underwater
3806 if(groundheight < WATER_LEVEL)
3808 //dstream<<"-> Underwater"<<std::endl;
3811 // Don't go to high places
3812 if(groundheight > WATER_LEVEL + 4)
3814 //dstream<<"-> Underwater"<<std::endl;
3818 // Found a good place
3819 dstream<<"Searched through "<<i<<" places."<<std::endl;
3824 // If no suitable place was not found, go above water at least.
3825 if(groundheight < WATER_LEVEL)
3826 groundheight = WATER_LEVEL;
3828 player->setPosition(intToFloat(v3s16(
3830 groundheight + 5, // Accomodate mud
3836 Add player to environment
3839 m_env.addPlayer(player);
3842 Add stuff to inventory
3845 if(g_settings.getBool("creative_mode"))
3847 setCreativeInventory(player);
3852 InventoryItem *item = new ToolItem("WPick", 32000);
3853 void* r = player->inventory.addItem("main", item);
3857 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3858 void* r = player->inventory.addItem("main", item);
3862 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3863 void* r = player->inventory.addItem("main", item);
3867 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3868 void* r = player->inventory.addItem("main", item);
3872 InventoryItem *item = new CraftItem("Stick", 4);
3873 void* r = player->inventory.addItem("main", item);
3877 InventoryItem *item = new ToolItem("WPick", 32000);
3878 void* r = player->inventory.addItem("main", item);
3882 InventoryItem *item = new ToolItem("STPick", 32000);
3883 void* r = player->inventory.addItem("main", item);
3886 /*// Give some lights
3888 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3889 bool r = player->inventory.addItem("main", item);
3893 for(u16 i=0; i<4; i++)
3895 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3896 bool r = player->inventory.addItem("main", item);
3899 /*// Give some other stuff
3901 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3902 bool r = player->inventory.addItem("main", item);
3909 } // create new player
3912 void Server::handlePeerChange(PeerChange &c)
3914 JMutexAutoLock envlock(m_env_mutex);
3915 JMutexAutoLock conlock(m_con_mutex);
3917 if(c.type == PEER_ADDED)
3924 core::map<u16, RemoteClient*>::Node *n;
3925 n = m_clients.find(c.peer_id);
3926 // The client shouldn't already exist
3930 RemoteClient *client = new RemoteClient();
3931 client->peer_id = c.peer_id;
3932 m_clients.insert(client->peer_id, client);
3935 else if(c.type == PEER_REMOVED)
3942 core::map<u16, RemoteClient*>::Node *n;
3943 n = m_clients.find(c.peer_id);
3944 // The client should exist
3947 // Collect information about leaving in chat
3948 std::wstring message;
3950 std::wstring name = L"unknown";
3951 Player *player = m_env.getPlayer(c.peer_id);
3953 name = narrow_to_wide(player->getName());
3957 message += L" left game";
3959 message += L" (timed out)";
3964 m_env.removePlayer(c.peer_id);
3967 // Set player client disconnected
3969 Player *player = m_env.getPlayer(c.peer_id);
3971 player->peer_id = 0;
3975 delete m_clients[c.peer_id];
3976 m_clients.remove(c.peer_id);
3978 // Send player info to all remaining clients
3981 // Send leave chat message to all remaining clients
3982 BroadcastChatMessage(message);
3991 void Server::handlePeerChanges()
3993 while(m_peer_change_queue.size() > 0)
3995 PeerChange c = m_peer_change_queue.pop_front();
3997 dout_server<<"Server: Handling peer change: "
3998 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4001 handlePeerChange(c);
4005 void dedicated_server_loop(Server &server, bool &kill)
4007 DSTACK(__FUNCTION_NAME);
4009 std::cout<<DTIME<<std::endl;
4010 std::cout<<"========================"<<std::endl;
4011 std::cout<<"Running dedicated server"<<std::endl;
4012 std::cout<<"========================"<<std::endl;
4013 std::cout<<std::endl;
4017 // This is kind of a hack but can be done like this
4018 // because server.step() is very light
4022 if(server.getShutdownRequested() || kill)
4024 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4028 static int counter = 0;
4034 core::list<PlayerInfo> list = server.getPlayerInfo();
4035 core::list<PlayerInfo>::Iterator i;
4036 static u32 sum_old = 0;
4037 u32 sum = PIChecksum(list);
4040 std::cout<<DTIME<<"Player info:"<<std::endl;
4041 for(i=list.begin(); i!=list.end(); i++)
4043 i->PrintLine(&std::cout);