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);
95 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
97 //TimeTaker timer("block emerge");
100 Try to emerge it from somewhere.
102 If it is only wanted as optional, only loading from disk
107 Check if any peer wants it as non-optional. In that case it
110 Also decrement the emerge queue count in clients.
113 bool optional = true;
116 core::map<u16, u8>::Iterator i;
117 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
119 //u16 peer_id = i.getNode()->getKey();
122 u8 flags = i.getNode()->getValue();
123 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
129 /*dstream<<"EmergeThread: p="
130 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
131 <<"optional="<<optional<<std::endl;*/
133 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
135 core::map<v3s16, MapBlock*> changed_blocks;
136 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
138 MapBlock *block = NULL;
139 bool got_block = true;
140 core::map<v3s16, MapBlock*> modified_blocks;
144 //TimeTaker envlockwaittimer("block emerge envlock wait time");
147 JMutexAutoLock envlock(m_server->m_env_mutex);
149 //envlockwaittimer.stop();
151 //TimeTaker timer("block emerge (while env locked)");
154 bool only_from_disk = false;
157 only_from_disk = true;
159 // First check if the block already exists
160 //block = map.getBlockNoCreate(p);
164 //dstream<<"Calling emergeBlock"<<std::endl;
165 block = map.emergeBlock(
169 lighting_invalidated_blocks);
173 While we're at it, generate some other blocks too
181 lighting_invalidated_blocks);
186 lighting_invalidated_blocks);
188 catch(InvalidPositionException &e)
194 // If it is a dummy, block was not found on disk
197 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
200 if(only_from_disk == false)
202 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
207 catch(InvalidPositionException &e)
210 // This happens when position is over limit.
216 if(debug && changed_blocks.size() > 0)
218 dout_server<<DTIME<<"Got changed_blocks: ";
219 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
220 i.atEnd() == false; i++)
222 MapBlock *block = i.getNode()->getValue();
223 v3s16 p = block->getPos();
224 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
226 dout_server<<std::endl;
230 Collect a list of blocks that have been modified in
231 addition to the fetched one.
234 if(lighting_invalidated_blocks.size() > 0)
235 dstream<<"lighting "<<lighting_invalidated_blocks.size()
236 <<" blocks"<<std::endl;
238 // 50-100ms for single block generation
239 //TimeTaker timer("** EmergeThread updateLighting");
241 // Update lighting without locking the environment mutex,
242 // add modified blocks to changed blocks
243 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
245 // Add all from changed_blocks to modified_blocks
246 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
247 i.atEnd() == false; i++)
249 MapBlock *block = i.getNode()->getValue();
250 modified_blocks.insert(block->getPos(), block);
253 // If we got no block, there should be no invalidated blocks
256 assert(lighting_invalidated_blocks.size() == 0);
262 Set sent status of modified blocks on clients
265 // NOTE: Server's clients are also behind the connection mutex
266 JMutexAutoLock lock(m_server->m_con_mutex);
269 Add the originally fetched block to the modified list
273 modified_blocks.insert(p, block);
277 Set the modified blocks unsent for all the clients
280 for(core::map<u16, RemoteClient*>::Iterator
281 i = m_server->m_clients.getIterator();
282 i.atEnd() == false; i++)
284 RemoteClient *client = i.getNode()->getValue();
286 if(modified_blocks.size() > 0)
288 // Remove block from sent history
289 client->SetBlocksNotSent(modified_blocks);
295 END_DEBUG_EXCEPTION_HANDLER
300 void RemoteClient::GetNextBlocks(Server *server, float dtime,
301 core::array<PrioritySortedBlockTransfer> &dest)
303 DSTACK(__FUNCTION_NAME);
306 m_nearest_unsent_reset_timer += dtime;
308 // Won't send anything if already sending
309 if(m_blocks_sending.size() >= g_settings.getU16
310 ("max_simultaneous_block_sends_per_client"))
312 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
316 Player *player = server->m_env.getPlayer(peer_id);
318 assert(player != NULL);
320 v3f playerpos = player->getPosition();
321 v3f playerspeed = player->getSpeed();
323 v3s16 center_nodepos = floatToInt(playerpos, BS);
325 v3s16 center = getNodeBlockPos(center_nodepos);
327 // Camera position and direction
329 playerpos + v3f(0, BS+BS/2, 0);
330 v3f camera_dir = v3f(0,0,1);
331 camera_dir.rotateYZBy(player->getPitch());
332 camera_dir.rotateXZBy(player->getYaw());
335 Get the starting value of the block finder radius.
337 s16 last_nearest_unsent_d;
340 if(m_last_center != center)
342 m_nearest_unsent_d = 0;
343 m_last_center = center;
346 /*dstream<<"m_nearest_unsent_reset_timer="
347 <<m_nearest_unsent_reset_timer<<std::endl;*/
348 if(m_nearest_unsent_reset_timer > 5.0)
350 m_nearest_unsent_reset_timer = 0;
351 m_nearest_unsent_d = 0;
352 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
355 last_nearest_unsent_d = m_nearest_unsent_d;
357 d_start = m_nearest_unsent_d;
359 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
360 ("max_simultaneous_block_sends_per_client");
361 u16 maximum_simultaneous_block_sends =
362 maximum_simultaneous_block_sends_setting;
365 Check the time from last addNode/removeNode.
367 Decrease send rate if player is building stuff.
369 m_time_from_building += dtime;
370 if(m_time_from_building < g_settings.getFloat(
371 "full_block_send_enable_min_time_from_building"))
373 maximum_simultaneous_block_sends
374 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
377 u32 num_blocks_selected = m_blocks_sending.size();
380 next time d will be continued from the d from which the nearest
381 unsent block was found this time.
383 This is because not necessarily any of the blocks found this
384 time are actually sent.
386 s32 new_nearest_unsent_d = -1;
388 s16 d_max = g_settings.getS16("max_block_send_distance");
389 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
391 //dstream<<"Starting from "<<d_start<<std::endl;
393 for(s16 d = d_start; d <= d_max; d++)
395 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
398 If m_nearest_unsent_d was changed by the EmergeThread
399 (it can change it to 0 through SetBlockNotSent),
401 Else update m_nearest_unsent_d
403 if(m_nearest_unsent_d != last_nearest_unsent_d)
405 d = m_nearest_unsent_d;
406 last_nearest_unsent_d = m_nearest_unsent_d;
410 Get the border/face dot coordinates of a "d-radiused"
413 core::list<v3s16> list;
414 getFacePositions(list, d);
416 core::list<v3s16>::Iterator li;
417 for(li=list.begin(); li!=list.end(); li++)
419 v3s16 p = *li + center;
423 - Don't allow too many simultaneous transfers
424 - EXCEPT when the blocks are very close
426 Also, don't send blocks that are already flying.
429 u16 maximum_simultaneous_block_sends_now =
430 maximum_simultaneous_block_sends;
432 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
434 maximum_simultaneous_block_sends_now =
435 maximum_simultaneous_block_sends_setting;
438 // Limit is dynamically lowered when building
439 if(num_blocks_selected
440 >= maximum_simultaneous_block_sends_now)
442 /*dstream<<"Not sending more blocks. Queue full. "
443 <<m_blocks_sending.size()
448 if(m_blocks_sending.find(p) != NULL)
454 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
455 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
456 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
457 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
458 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
459 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
462 // If this is true, inexistent block will be made from scratch
463 bool generate = d <= d_max_gen;
466 /*// Limit the generating area vertically to 2/3
467 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
470 // Limit the send area vertically to 2/3
471 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
477 If block is far away, don't generate it unless it is
480 NOTE: We can't know the ground level this way with the
486 MapSector *sector = NULL;
489 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
491 catch(InvalidPositionException &e)
497 // Get center ground height in nodes
498 f32 gh = sector->getGroundHeight(
499 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
500 // Block center y in nodes
501 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
502 // If differs a lot, don't generate
503 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
510 Don't generate or send if not in sight
513 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
519 Don't send already sent blocks
522 if(m_blocks_sent.find(p) != NULL)
527 Check if map has this block
529 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
531 bool surely_not_found_on_disk = false;
532 bool block_is_invalid = false;
537 surely_not_found_on_disk = true;
540 if(block->isValid() == false)
542 block_is_invalid = true;
546 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
547 v2s16 chunkpos = map->sector_to_chunk(p2d);
548 if(map->chunkNonVolatile(chunkpos) == false)
549 block_is_invalid = true;
553 If block has been marked to not exist on disk (dummy)
554 and generating new ones is not wanted, skip block.
556 if(generate == false && surely_not_found_on_disk == true)
563 Record the lowest d from which a a block has been
564 found being not sent and possibly to exist
566 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
568 new_nearest_unsent_d = d;
572 Add inexistent block to emerge queue.
574 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
576 //TODO: Get value from somewhere
577 // Allow only one block in emerge queue
578 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
579 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
581 //dstream<<"Adding block to emerge queue"<<std::endl;
583 // Add it to the emerge queue and trigger the thread
586 if(generate == false)
587 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
589 server->m_emerge_queue.addBlock(peer_id, p, flags);
590 server->m_emergethread.trigger();
598 Add block to send queue
601 PrioritySortedBlockTransfer q((float)d, p, peer_id);
605 num_blocks_selected += 1;
610 if(new_nearest_unsent_d != -1)
612 m_nearest_unsent_d = new_nearest_unsent_d;
616 void RemoteClient::SendObjectData(
619 core::map<v3s16, bool> &stepped_blocks
622 DSTACK(__FUNCTION_NAME);
624 // Can't send anything without knowing version
625 if(serialization_version == SER_FMT_VER_INVALID)
627 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
633 Send a TOCLIENT_OBJECTDATA packet.
637 u16 number of player positions
648 std::ostringstream os(std::ios_base::binary);
652 writeU16(buf, TOCLIENT_OBJECTDATA);
653 os.write((char*)buf, 2);
656 Get and write player data
659 // Get connected players
660 core::list<Player*> players = server->m_env.getPlayers(true);
662 // Write player count
663 u16 playercount = players.size();
664 writeU16(buf, playercount);
665 os.write((char*)buf, 2);
667 core::list<Player*>::Iterator i;
668 for(i = players.begin();
669 i != players.end(); i++)
673 v3f pf = player->getPosition();
674 v3f sf = player->getSpeed();
676 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
677 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
678 s32 pitch_i (player->getPitch() * 100);
679 s32 yaw_i (player->getYaw() * 100);
681 writeU16(buf, player->peer_id);
682 os.write((char*)buf, 2);
683 writeV3S32(buf, position_i);
684 os.write((char*)buf, 12);
685 writeV3S32(buf, speed_i);
686 os.write((char*)buf, 12);
687 writeS32(buf, pitch_i);
688 os.write((char*)buf, 4);
689 writeS32(buf, yaw_i);
690 os.write((char*)buf, 4);
694 Get and write object data
700 For making players to be able to build to their nearby
701 environment (building is not possible on blocks that are not
704 - Add blocks to emerge queue if they are not found
706 SUGGESTION: These could be ignored from the backside of the player
709 Player *player = server->m_env.getPlayer(peer_id);
713 v3f playerpos = player->getPosition();
714 v3f playerspeed = player->getSpeed();
716 v3s16 center_nodepos = floatToInt(playerpos, BS);
717 v3s16 center = getNodeBlockPos(center_nodepos);
719 s16 d_max = g_settings.getS16("active_object_range");
721 // Number of blocks whose objects were written to bos
724 std::ostringstream bos(std::ios_base::binary);
726 for(s16 d = 0; d <= d_max; d++)
728 core::list<v3s16> list;
729 getFacePositions(list, d);
731 core::list<v3s16>::Iterator li;
732 for(li=list.begin(); li!=list.end(); li++)
734 v3s16 p = *li + center;
737 Ignore blocks that haven't been sent to the client
740 if(m_blocks_sent.find(p) == NULL)
744 // Try stepping block and add it to a send queue
749 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
752 Step block if not in stepped_blocks and add to stepped_blocks.
754 if(stepped_blocks.find(p) == NULL)
756 block->stepObjects(dtime, true, server->getDayNightRatio());
757 stepped_blocks.insert(p, true);
758 block->setChangedFlag();
761 // Skip block if there are no objects
762 if(block->getObjectCount() == 0)
771 bos.write((char*)buf, 6);
774 block->serializeObjects(bos, serialization_version);
779 Stop collecting objects if data is already too big
781 // Sum of player and object data sizes
782 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
783 // break out if data too big
784 if(sum > MAX_OBJECTDATA_SIZE)
786 goto skip_subsequent;
790 catch(InvalidPositionException &e)
793 // Add it to the emerge queue and trigger the thread.
794 // Fetch the block only if it is on disk.
796 // Grab and increment counter
797 /*SharedPtr<JMutexAutoLock> lock
798 (m_num_blocks_in_emerge_queue.getLock());
799 m_num_blocks_in_emerge_queue.m_value++;*/
801 // Add to queue as an anonymous fetch from disk
802 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
803 server->m_emerge_queue.addBlock(0, p, flags);
804 server->m_emergethread.trigger();
812 writeU16(buf, blockcount);
813 os.write((char*)buf, 2);
815 // Write block objects
822 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
825 std::string s = os.str();
826 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
827 // Send as unreliable
828 server->m_con.Send(peer_id, 0, data, false);
831 void RemoteClient::GotBlock(v3s16 p)
833 if(m_blocks_sending.find(p) != NULL)
834 m_blocks_sending.remove(p);
837 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
838 " m_blocks_sending"<<std::endl;*/
839 m_excess_gotblocks++;
841 m_blocks_sent.insert(p, true);
844 void RemoteClient::SentBlock(v3s16 p)
846 if(m_blocks_sending.find(p) == NULL)
847 m_blocks_sending.insert(p, 0.0);
849 dstream<<"RemoteClient::SentBlock(): Sent block"
850 " already in m_blocks_sending"<<std::endl;
853 void RemoteClient::SetBlockNotSent(v3s16 p)
855 m_nearest_unsent_d = 0;
857 if(m_blocks_sending.find(p) != NULL)
858 m_blocks_sending.remove(p);
859 if(m_blocks_sent.find(p) != NULL)
860 m_blocks_sent.remove(p);
863 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
865 m_nearest_unsent_d = 0;
867 for(core::map<v3s16, MapBlock*>::Iterator
868 i = blocks.getIterator();
869 i.atEnd()==false; i++)
871 v3s16 p = i.getNode()->getKey();
873 if(m_blocks_sending.find(p) != NULL)
874 m_blocks_sending.remove(p);
875 if(m_blocks_sent.find(p) != NULL)
876 m_blocks_sent.remove(p);
884 PlayerInfo::PlayerInfo()
889 void PlayerInfo::PrintLine(std::ostream *s)
892 (*s)<<"\""<<name<<"\" ("
893 <<(position.X/10)<<","<<(position.Y/10)
894 <<","<<(position.Z/10)<<") ";
896 (*s)<<" avg_rtt="<<avg_rtt;
900 u32 PIChecksum(core::list<PlayerInfo> &l)
902 core::list<PlayerInfo>::Iterator i;
905 for(i=l.begin(); i!=l.end(); i++)
907 checksum += a * (i->id+1);
908 checksum ^= 0x435aafcd;
919 std::string mapsavedir
921 m_env(new ServerMap(mapsavedir), this),
922 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
924 m_emergethread(this),
927 m_time_of_day_send_timer(0),
929 m_mapsavedir(mapsavedir),
930 m_shutdown_requested(false),
931 m_ignore_map_edit_events(false),
932 m_ignore_map_edit_events_peer_id(0)
934 m_liquid_transform_timer = 0.0;
935 m_print_info_timer = 0.0;
936 m_objectdata_timer = 0.0;
937 m_emergethread_trigger_timer = 0.0;
938 m_savemap_timer = 0.0;
942 m_step_dtime_mutex.Init();
945 m_env.getMap().addEventReceiver(this);
948 m_env.deSerializePlayers(m_mapsavedir);
954 Send shutdown message
957 JMutexAutoLock conlock(m_con_mutex);
959 std::wstring line = L"*** Server shutting down";
962 Send the message to clients
964 for(core::map<u16, RemoteClient*>::Iterator
965 i = m_clients.getIterator();
966 i.atEnd() == false; i++)
968 // Get client and check that it is valid
969 RemoteClient *client = i.getNode()->getValue();
970 assert(client->peer_id == i.getNode()->getKey());
971 if(client->serialization_version == SER_FMT_VER_INVALID)
974 SendChatMessage(client->peer_id, line);
981 m_env.serializePlayers(m_mapsavedir);
992 JMutexAutoLock clientslock(m_con_mutex);
994 for(core::map<u16, RemoteClient*>::Iterator
995 i = m_clients.getIterator();
996 i.atEnd() == false; i++)
999 // NOTE: These are removed by env destructor
1001 u16 peer_id = i.getNode()->getKey();
1002 JMutexAutoLock envlock(m_env_mutex);
1003 m_env.removePlayer(peer_id);
1007 delete i.getNode()->getValue();
1012 void Server::start(unsigned short port)
1014 DSTACK(__FUNCTION_NAME);
1015 // Stop thread if already running
1018 // Initialize connection
1019 m_con.setTimeoutMs(30);
1023 m_thread.setRun(true);
1026 dout_server<<"Server: Started on port "<<port<<std::endl;
1031 DSTACK(__FUNCTION_NAME);
1033 // Stop threads (set run=false first so both start stopping)
1034 m_thread.setRun(false);
1035 m_emergethread.setRun(false);
1037 m_emergethread.stop();
1039 dout_server<<"Server: Threads stopped"<<std::endl;
1041 dout_server<<"Server: Saving players"<<std::endl;
1043 // FIXME: Apparently this does not do anything here
1044 //m_env.serializePlayers(m_mapsavedir);
1047 void Server::step(float dtime)
1049 DSTACK(__FUNCTION_NAME);
1054 JMutexAutoLock lock(m_step_dtime_mutex);
1055 m_step_dtime += dtime;
1059 void Server::AsyncRunStep()
1061 DSTACK(__FUNCTION_NAME);
1065 JMutexAutoLock lock1(m_step_dtime_mutex);
1066 dtime = m_step_dtime;
1069 // Send blocks to clients
1075 //dstream<<"Server steps "<<dtime<<std::endl;
1076 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1079 JMutexAutoLock lock1(m_step_dtime_mutex);
1080 m_step_dtime -= dtime;
1087 m_uptime.set(m_uptime.get() + dtime);
1091 Update m_time_of_day
1094 m_time_counter += dtime;
1095 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1096 u32 units = (u32)(m_time_counter*speed);
1097 m_time_counter -= (f32)units / speed;
1098 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1100 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1103 Send to clients at constant intervals
1106 m_time_of_day_send_timer -= dtime;
1107 if(m_time_of_day_send_timer < 0.0)
1109 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1111 //JMutexAutoLock envlock(m_env_mutex);
1112 JMutexAutoLock conlock(m_con_mutex);
1114 for(core::map<u16, RemoteClient*>::Iterator
1115 i = m_clients.getIterator();
1116 i.atEnd() == false; i++)
1118 RemoteClient *client = i.getNode()->getValue();
1119 //Player *player = m_env.getPlayer(client->peer_id);
1121 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1122 m_time_of_day.get());
1124 m_con.Send(client->peer_id, 0, data, true);
1130 // Process connection's timeouts
1131 JMutexAutoLock lock2(m_con_mutex);
1132 m_con.RunTimeouts(dtime);
1136 // This has to be called so that the client list gets synced
1137 // with the peer list of the connection
1138 handlePeerChanges();
1143 // This also runs Map's timers
1144 JMutexAutoLock lock(m_env_mutex);
1155 m_liquid_transform_timer += dtime;
1156 if(m_liquid_transform_timer >= 1.00)
1158 m_liquid_transform_timer -= 1.00;
1160 JMutexAutoLock lock(m_env_mutex);
1162 core::map<v3s16, MapBlock*> modified_blocks;
1163 m_env.getMap().transformLiquids(modified_blocks);
1168 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1169 ServerMap &map = ((ServerMap&)m_env.getMap());
1170 map.updateLighting(modified_blocks, lighting_modified_blocks);
1172 // Add blocks modified by lighting to modified_blocks
1173 for(core::map<v3s16, MapBlock*>::Iterator
1174 i = lighting_modified_blocks.getIterator();
1175 i.atEnd() == false; i++)
1177 MapBlock *block = i.getNode()->getValue();
1178 modified_blocks.insert(block->getPos(), block);
1182 Set the modified blocks unsent for all the clients
1185 JMutexAutoLock lock2(m_con_mutex);
1187 for(core::map<u16, RemoteClient*>::Iterator
1188 i = m_clients.getIterator();
1189 i.atEnd() == false; i++)
1191 RemoteClient *client = i.getNode()->getValue();
1193 if(modified_blocks.size() > 0)
1195 // Remove block from sent history
1196 client->SetBlocksNotSent(modified_blocks);
1201 // Periodically print some info
1203 float &counter = m_print_info_timer;
1209 JMutexAutoLock lock2(m_con_mutex);
1211 for(core::map<u16, RemoteClient*>::Iterator
1212 i = m_clients.getIterator();
1213 i.atEnd() == false; i++)
1215 //u16 peer_id = i.getNode()->getKey();
1216 RemoteClient *client = i.getNode()->getValue();
1217 client->PrintInfo(std::cout);
1222 if(g_settings.getBool("enable_experimental"))
1226 Check added and deleted active objects
1229 JMutexAutoLock envlock(m_env_mutex);
1230 JMutexAutoLock conlock(m_con_mutex);
1232 // Radius inside which objects are active
1235 for(core::map<u16, RemoteClient*>::Iterator
1236 i = m_clients.getIterator();
1237 i.atEnd() == false; i++)
1239 RemoteClient *client = i.getNode()->getValue();
1240 Player *player = m_env.getPlayer(client->peer_id);
1243 v3s16 pos = floatToInt(player->getPosition(), BS);
1245 core::map<u16, bool> removed_objects;
1246 core::map<u16, bool> added_objects;
1247 m_env.getRemovedActiveObjects(pos, radius,
1248 client->m_known_objects, removed_objects);
1249 m_env.getAddedActiveObjects(pos, radius,
1250 client->m_known_objects, added_objects);
1252 // Ignore if nothing happened
1253 if(removed_objects.size() == 0 && added_objects.size() == 0)
1256 std::string data_buffer;
1260 // Handle removed objects
1261 writeU16((u8*)buf, removed_objects.size());
1262 data_buffer.append(buf, 2);
1263 for(core::map<u16, bool>::Iterator
1264 i = removed_objects.getIterator();
1265 i.atEnd()==false; i++)
1268 u16 id = i.getNode()->getKey();
1269 ServerActiveObject* obj = m_env.getActiveObject(id);
1271 // Add to data buffer for sending
1272 writeU16((u8*)buf, i.getNode()->getKey());
1273 data_buffer.append(buf, 2);
1275 // Remove from known objects
1276 client->m_known_objects.remove(i.getNode()->getKey());
1278 if(obj && obj->m_known_by_count > 0)
1279 obj->m_known_by_count--;
1282 // Handle added objects
1283 writeU16((u8*)buf, added_objects.size());
1284 data_buffer.append(buf, 2);
1285 for(core::map<u16, bool>::Iterator
1286 i = added_objects.getIterator();
1287 i.atEnd()==false; i++)
1290 u16 id = i.getNode()->getKey();
1291 ServerActiveObject* obj = m_env.getActiveObject(id);
1294 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1296 dstream<<"WARNING: "<<__FUNCTION_NAME
1297 <<": NULL object"<<std::endl;
1299 type = obj->getType();
1301 // Add to data buffer for sending
1302 writeU16((u8*)buf, id);
1303 data_buffer.append(buf, 2);
1304 writeU8((u8*)buf, type);
1305 data_buffer.append(buf, 1);
1307 data_buffer.append(serializeLongString(
1308 obj->getClientInitializationData()));
1310 // Add to known objects
1311 client->m_known_objects.insert(i.getNode()->getKey(), false);
1314 obj->m_known_by_count++;
1318 SharedBuffer<u8> reply(2 + data_buffer.size());
1319 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1320 memcpy((char*)&reply[2], data_buffer.c_str(),
1321 data_buffer.size());
1323 m_con.Send(client->peer_id, 0, reply, true);
1325 dstream<<"INFO: Server: Sent object remove/add: "
1326 <<removed_objects.size()<<" removed, "
1327 <<added_objects.size()<<" added, "
1328 <<"packet size is "<<reply.getSize()<<std::endl;
1333 Send object messages
1336 JMutexAutoLock envlock(m_env_mutex);
1337 JMutexAutoLock conlock(m_con_mutex);
1340 // Value = data sent by object
1341 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1343 // Get active object messages from environment
1346 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1350 core::list<ActiveObjectMessage>* message_list = NULL;
1351 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1352 n = buffered_messages.find(aom.id);
1355 message_list = new core::list<ActiveObjectMessage>;
1356 buffered_messages.insert(aom.id, message_list);
1360 message_list = n->getValue();
1362 message_list->push_back(aom);
1365 // Route data to every client
1366 for(core::map<u16, RemoteClient*>::Iterator
1367 i = m_clients.getIterator();
1368 i.atEnd()==false; i++)
1370 RemoteClient *client = i.getNode()->getValue();
1371 std::string reliable_data;
1372 std::string unreliable_data;
1373 // Go through all objects in message buffer
1374 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1375 j = buffered_messages.getIterator();
1376 j.atEnd()==false; j++)
1378 // If object is not known by client, skip it
1379 u16 id = j.getNode()->getKey();
1380 if(client->m_known_objects.find(id) == NULL)
1382 // Get message list of object
1383 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1384 // Go through every message
1385 for(core::list<ActiveObjectMessage>::Iterator
1386 k = list->begin(); k != list->end(); k++)
1388 // Compose the full new data with header
1389 ActiveObjectMessage aom = *k;
1390 std::string new_data;
1393 writeU16((u8*)&buf[0], aom.id);
1394 new_data.append(buf, 2);
1396 new_data += serializeString(aom.datastring);
1397 // Add data to buffer
1399 reliable_data += new_data;
1401 unreliable_data += new_data;
1405 reliable_data and unreliable_data are now ready.
1408 if(reliable_data.size() > 0)
1410 SharedBuffer<u8> reply(2 + reliable_data.size());
1411 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1412 memcpy((char*)&reply[2], reliable_data.c_str(),
1413 reliable_data.size());
1415 m_con.Send(client->peer_id, 0, reply, true);
1417 if(unreliable_data.size() > 0)
1419 SharedBuffer<u8> reply(2 + unreliable_data.size());
1420 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1421 memcpy((char*)&reply[2], unreliable_data.c_str(),
1422 unreliable_data.size());
1423 // Send as unreliable
1424 m_con.Send(client->peer_id, 0, reply, false);
1427 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1429 dstream<<"INFO: Server: Size of object message data: "
1430 <<"reliable: "<<reliable_data.size()
1431 <<", unreliable: "<<unreliable_data.size()
1436 // Clear buffered_messages
1437 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1438 i = buffered_messages.getIterator();
1439 i.atEnd()==false; i++)
1441 delete i.getNode()->getValue();
1445 } // enable_experimental
1448 Send queued-for-sending map edit events.
1451 while(m_unsent_map_edit_queue.size() != 0)
1453 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1455 if(event->type == MEET_ADDNODE)
1457 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1458 sendAddNode(event->p, event->n, event->already_known_by_peer);
1460 else if(event->type == MEET_REMOVENODE)
1462 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1463 sendRemoveNode(event->p, event->already_known_by_peer);
1465 else if(event->type == MEET_OTHER)
1467 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1472 dstream<<"WARNING: Server: Unknown MapEditEvent "
1473 <<((u32)event->type)<<std::endl;
1481 Send object positions
1482 TODO: Get rid of MapBlockObjects
1485 float &counter = m_objectdata_timer;
1487 if(counter >= g_settings.getFloat("objectdata_interval"))
1489 JMutexAutoLock lock1(m_env_mutex);
1490 JMutexAutoLock lock2(m_con_mutex);
1491 SendObjectData(counter);
1498 Trigger emergethread (it somehow gets to a non-triggered but
1499 bysy state sometimes)
1502 float &counter = m_emergethread_trigger_timer;
1508 m_emergethread.trigger();
1514 float &counter = m_savemap_timer;
1516 if(counter >= g_settings.getFloat("server_map_save_interval"))
1520 JMutexAutoLock lock(m_env_mutex);
1522 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1524 // Save only changed parts
1525 m_env.getMap().save(true);
1527 // Delete unused sectors
1528 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1529 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1530 if(deleted_count > 0)
1532 dout_server<<"Server: Unloaded "<<deleted_count
1533 <<" sectors from memory"<<std::endl;
1537 m_env.serializePlayers(m_mapsavedir);
1543 void Server::Receive()
1545 DSTACK(__FUNCTION_NAME);
1546 u32 data_maxsize = 10000;
1547 Buffer<u8> data(data_maxsize);
1552 JMutexAutoLock conlock(m_con_mutex);
1553 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1556 // This has to be called so that the client list gets synced
1557 // with the peer list of the connection
1558 handlePeerChanges();
1560 ProcessData(*data, datasize, peer_id);
1562 catch(con::InvalidIncomingDataException &e)
1564 derr_server<<"Server::Receive(): "
1565 "InvalidIncomingDataException: what()="
1566 <<e.what()<<std::endl;
1568 catch(con::PeerNotFoundException &e)
1570 //NOTE: This is not needed anymore
1572 // The peer has been disconnected.
1573 // Find the associated player and remove it.
1575 /*JMutexAutoLock envlock(m_env_mutex);
1577 dout_server<<"ServerThread: peer_id="<<peer_id
1578 <<" has apparently closed connection. "
1579 <<"Removing player."<<std::endl;
1581 m_env.removePlayer(peer_id);*/
1585 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1587 DSTACK(__FUNCTION_NAME);
1588 // Environment is locked first.
1589 JMutexAutoLock envlock(m_env_mutex);
1590 JMutexAutoLock conlock(m_con_mutex);
1594 peer = m_con.GetPeer(peer_id);
1596 catch(con::PeerNotFoundException &e)
1598 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1599 <<peer_id<<" not found"<<std::endl;
1603 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1611 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1613 if(command == TOSERVER_INIT)
1615 // [0] u16 TOSERVER_INIT
1616 // [2] u8 SER_FMT_VER_HIGHEST
1617 // [3] u8[20] player_name
1622 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1623 <<peer->id<<std::endl;
1625 // First byte after command is maximum supported
1626 // serialization version
1627 u8 client_max = data[2];
1628 u8 our_max = SER_FMT_VER_HIGHEST;
1629 // Use the highest version supported by both
1630 u8 deployed = core::min_(client_max, our_max);
1631 // If it's lower than the lowest supported, give up.
1632 if(deployed < SER_FMT_VER_LOWEST)
1633 deployed = SER_FMT_VER_INVALID;
1635 //peer->serialization_version = deployed;
1636 getClient(peer->id)->pending_serialization_version = deployed;
1638 if(deployed == SER_FMT_VER_INVALID)
1640 derr_server<<DTIME<<"Server: Cannot negotiate "
1641 "serialization version with peer "
1642 <<peer_id<<std::endl;
1651 const u32 playername_size = 20;
1652 char playername[playername_size];
1653 for(u32 i=0; i<playername_size-1; i++)
1655 playername[i] = data[3+i];
1657 playername[playername_size-1] = 0;
1660 Player *player = emergePlayer(playername, "", peer_id);
1661 //Player *player = m_env.getPlayer(peer_id);
1664 // DEBUG: Test serialization
1665 std::ostringstream test_os;
1666 player->serialize(test_os);
1667 dstream<<"Player serialization test: \""<<test_os.str()
1669 std::istringstream test_is(test_os.str());
1670 player->deSerialize(test_is);
1673 // If failed, cancel
1676 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1677 <<": failed to emerge player"<<std::endl;
1682 // If a client is already connected to the player, cancel
1683 if(player->peer_id != 0)
1685 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1686 <<" tried to connect to "
1687 "an already connected player (peer_id="
1688 <<player->peer_id<<")"<<std::endl;
1691 // Set client of player
1692 player->peer_id = peer_id;
1695 // Check if player doesn't exist
1697 throw con::InvalidIncomingDataException
1698 ("Server::ProcessData(): INIT: Player doesn't exist");
1700 /*// update name if it was supplied
1701 if(datasize >= 20+3)
1704 player->updateName((const char*)&data[3]);
1707 // Now answer with a TOCLIENT_INIT
1709 SharedBuffer<u8> reply(2+1+6+8);
1710 writeU16(&reply[0], TOCLIENT_INIT);
1711 writeU8(&reply[2], deployed);
1712 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1713 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1715 m_con.Send(peer_id, 0, reply, true);
1719 if(command == TOSERVER_INIT2)
1721 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1722 <<peer->id<<std::endl;
1725 getClient(peer->id)->serialization_version
1726 = getClient(peer->id)->pending_serialization_version;
1729 Send some initialization data
1732 // Send player info to all players
1735 // Send inventory to player
1736 SendInventory(peer->id);
1740 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1741 m_time_of_day.get());
1742 m_con.Send(peer->id, 0, data, true);
1745 // Send information about server to player in chat
1746 SendChatMessage(peer_id, getStatusString());
1748 // Send information about joining in chat
1750 std::wstring name = L"unknown";
1751 Player *player = m_env.getPlayer(peer_id);
1753 name = narrow_to_wide(player->getName());
1755 std::wstring message;
1758 message += L" joined game";
1759 BroadcastChatMessage(message);
1765 if(peer_ser_ver == SER_FMT_VER_INVALID)
1767 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1768 " serialization format invalid or not initialized."
1769 " Skipping incoming command="<<command<<std::endl;
1773 Player *player = m_env.getPlayer(peer_id);
1776 derr_server<<"Server::ProcessData(): Cancelling: "
1777 "No player for peer_id="<<peer_id
1781 if(command == TOSERVER_PLAYERPOS)
1783 if(datasize < 2+12+12+4+4)
1787 v3s32 ps = readV3S32(&data[start+2]);
1788 v3s32 ss = readV3S32(&data[start+2+12]);
1789 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1790 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1791 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1792 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1793 pitch = wrapDegrees(pitch);
1794 yaw = wrapDegrees(yaw);
1795 player->setPosition(position);
1796 player->setSpeed(speed);
1797 player->setPitch(pitch);
1798 player->setYaw(yaw);
1800 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1801 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1802 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1804 else if(command == TOSERVER_GOTBLOCKS)
1817 u16 count = data[2];
1818 for(u16 i=0; i<count; i++)
1820 if((s16)datasize < 2+1+(i+1)*6)
1821 throw con::InvalidIncomingDataException
1822 ("GOTBLOCKS length is too short");
1823 v3s16 p = readV3S16(&data[2+1+i*6]);
1824 /*dstream<<"Server: GOTBLOCKS ("
1825 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1826 RemoteClient *client = getClient(peer_id);
1827 client->GotBlock(p);
1830 else if(command == TOSERVER_DELETEDBLOCKS)
1843 u16 count = data[2];
1844 for(u16 i=0; i<count; i++)
1846 if((s16)datasize < 2+1+(i+1)*6)
1847 throw con::InvalidIncomingDataException
1848 ("DELETEDBLOCKS length is too short");
1849 v3s16 p = readV3S16(&data[2+1+i*6]);
1850 /*dstream<<"Server: DELETEDBLOCKS ("
1851 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1852 RemoteClient *client = getClient(peer_id);
1853 client->SetBlockNotSent(p);
1856 else if(command == TOSERVER_CLICK_OBJECT)
1863 [2] u8 button (0=left, 1=right)
1868 u8 button = readU8(&data[2]);
1870 p.X = readS16(&data[3]);
1871 p.Y = readS16(&data[5]);
1872 p.Z = readS16(&data[7]);
1873 s16 id = readS16(&data[9]);
1874 //u16 item_i = readU16(&data[11]);
1876 MapBlock *block = NULL;
1879 block = m_env.getMap().getBlockNoCreate(p);
1881 catch(InvalidPositionException &e)
1883 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1887 MapBlockObject *obj = block->getObject(id);
1891 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1895 //TODO: Check that object is reasonably close
1900 InventoryList *ilist = player->inventory.getList("main");
1901 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1904 // Skip if inventory has no free space
1905 if(ilist->getUsedSlots() == ilist->getSize())
1907 dout_server<<"Player inventory has no free space"<<std::endl;
1912 Create the inventory item
1914 InventoryItem *item = NULL;
1915 // If it is an item-object, take the item from it
1916 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1918 item = ((ItemObject*)obj)->createInventoryItem();
1920 // Else create an item of the object
1923 item = new MapBlockObjectItem
1924 (obj->getInventoryString());
1927 // Add to inventory and send inventory
1928 ilist->addItem(item);
1929 SendInventory(player->peer_id);
1932 // Remove from block
1933 block->removeObject(id);
1936 else if(command == TOSERVER_GROUND_ACTION)
1944 [3] v3s16 nodepos_undersurface
1945 [9] v3s16 nodepos_abovesurface
1950 2: stop digging (all parameters ignored)
1951 3: digging completed
1953 u8 action = readU8(&data[2]);
1955 p_under.X = readS16(&data[3]);
1956 p_under.Y = readS16(&data[5]);
1957 p_under.Z = readS16(&data[7]);
1959 p_over.X = readS16(&data[9]);
1960 p_over.Y = readS16(&data[11]);
1961 p_over.Z = readS16(&data[13]);
1962 u16 item_i = readU16(&data[15]);
1964 //TODO: Check that target is reasonably close
1972 NOTE: This can be used in the future to check if
1973 somebody is cheating, by checking the timing.
1980 else if(action == 2)
1983 RemoteClient *client = getClient(peer->id);
1984 JMutexAutoLock digmutex(client->m_dig_mutex);
1985 client->m_dig_tool_item = -1;
1990 3: Digging completed
1992 else if(action == 3)
1994 // Mandatory parameter; actually used for nothing
1995 core::map<v3s16, MapBlock*> modified_blocks;
1998 u8 mineral = MINERAL_NONE;
2002 MapNode n = m_env.getMap().getNode(p_under);
2003 // Get material at position
2005 // If it's not diggable, do nothing
2006 if(content_diggable(material) == false)
2008 derr_server<<"Server: Not finishing digging: Node not diggable"
2011 // Client probably has wrong data.
2012 // Set block not sent, so that client will get
2014 dstream<<"Client "<<peer_id<<" tried to dig "
2015 <<"node from invalid position; setting"
2016 <<" MapBlock not sent."<<std::endl;
2017 RemoteClient *client = getClient(peer_id);
2018 v3s16 blockpos = getNodeBlockPos(p_under);
2019 client->SetBlockNotSent(blockpos);
2024 mineral = n.getMineral();
2026 catch(InvalidPositionException &e)
2028 derr_server<<"Server: Not finishing digging: Node not found."
2029 <<" Adding block to emerge queue."
2031 m_emerge_queue.addBlock(peer_id,
2032 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2037 Send the removal to all other clients
2039 sendRemoveNode(p_over, peer_id);
2042 Update and send inventory
2045 if(g_settings.getBool("creative_mode") == false)
2050 InventoryList *mlist = player->inventory.getList("main");
2053 InventoryItem *item = mlist->getItem(item_i);
2054 if(item && (std::string)item->getName() == "ToolItem")
2056 ToolItem *titem = (ToolItem*)item;
2057 std::string toolname = titem->getToolName();
2059 // Get digging properties for material and tool
2060 DiggingProperties prop =
2061 getDiggingProperties(material, toolname);
2063 if(prop.diggable == false)
2065 derr_server<<"Server: WARNING: Player digged"
2066 <<" with impossible material + tool"
2067 <<" combination"<<std::endl;
2070 bool weared_out = titem->addWear(prop.wear);
2074 mlist->deleteItem(item_i);
2080 Add dug item to inventory
2083 InventoryItem *item = NULL;
2085 if(mineral != MINERAL_NONE)
2086 item = getDiggedMineralItem(mineral);
2091 std::string &dug_s = content_features(material).dug_item;
2094 std::istringstream is(dug_s, std::ios::binary);
2095 item = InventoryItem::deSerialize(is);
2101 // Add a item to inventory
2102 player->inventory.addItem("main", item);
2105 SendInventory(player->peer_id);
2111 (this takes some time so it is done after the quick stuff)
2113 m_ignore_map_edit_events = true;
2114 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2115 m_ignore_map_edit_events = false;
2121 else if(action == 1)
2124 InventoryList *ilist = player->inventory.getList("main");
2129 InventoryItem *item = ilist->getItem(item_i);
2131 // If there is no item, it is not possible to add it anywhere
2136 Handle material items
2138 if(std::string("MaterialItem") == item->getName())
2141 // Don't add a node if this is not a free space
2142 MapNode n2 = m_env.getMap().getNode(p_over);
2143 if(content_buildable_to(n2.d) == false)
2145 // Client probably has wrong data.
2146 // Set block not sent, so that client will get
2148 dstream<<"Client "<<peer_id<<" tried to place"
2149 <<" node in invalid position; setting"
2150 <<" MapBlock not sent."<<std::endl;
2151 RemoteClient *client = getClient(peer_id);
2152 v3s16 blockpos = getNodeBlockPos(p_over);
2153 client->SetBlockNotSent(blockpos);
2157 catch(InvalidPositionException &e)
2159 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2160 <<" Adding block to emerge queue."
2162 m_emerge_queue.addBlock(peer_id,
2163 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2167 // Reset build time counter
2168 getClient(peer->id)->m_time_from_building = 0.0;
2171 MaterialItem *mitem = (MaterialItem*)item;
2173 n.d = mitem->getMaterial();
2174 if(content_features(n.d).wall_mounted)
2175 n.dir = packDir(p_under - p_over);
2180 sendAddNode(p_over, n, 0);
2185 InventoryList *ilist = player->inventory.getList("main");
2186 if(g_settings.getBool("creative_mode") == false && ilist)
2188 // Remove from inventory and send inventory
2189 if(mitem->getCount() == 1)
2190 ilist->deleteItem(item_i);
2194 SendInventory(peer_id);
2200 This takes some time so it is done after the quick stuff
2202 core::map<v3s16, MapBlock*> modified_blocks;
2203 m_ignore_map_edit_events = true;
2204 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2205 m_ignore_map_edit_events = false;
2208 Calculate special events
2211 /*if(n.d == CONTENT_MESE)
2214 for(s16 z=-1; z<=1; z++)
2215 for(s16 y=-1; y<=1; y++)
2216 for(s16 x=-1; x<=1; x++)
2227 v3s16 blockpos = getNodeBlockPos(p_over);
2229 MapBlock *block = NULL;
2232 block = m_env.getMap().getBlockNoCreate(blockpos);
2234 catch(InvalidPositionException &e)
2236 derr_server<<"Error while placing object: "
2237 "block not found"<<std::endl;
2241 v3s16 block_pos_i_on_map = block->getPosRelative();
2242 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2244 v3f pos = intToFloat(p_over, BS);
2245 pos -= block_pos_f_on_map;
2247 /*dout_server<<"pos="
2248 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2251 MapBlockObject *obj = NULL;
2254 Handle block object items
2256 if(std::string("MBOItem") == item->getName())
2258 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2260 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2261 "inventorystring=\""
2262 <<oitem->getInventoryString()
2263 <<"\""<<std::endl;*/
2265 obj = oitem->createObject
2266 (pos, player->getYaw(), player->getPitch());
2273 dout_server<<"Placing a miscellaneous item on map"
2276 Create an ItemObject that contains the item.
2278 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2279 std::ostringstream os(std::ios_base::binary);
2280 item->serialize(os);
2281 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2282 iobj->setItemString(os.str());
2288 derr_server<<"WARNING: item resulted in NULL object, "
2289 <<"not placing onto map"
2294 block->addObject(obj);
2296 dout_server<<"Placed object"<<std::endl;
2298 InventoryList *ilist = player->inventory.getList("main");
2299 if(g_settings.getBool("creative_mode") == false && ilist)
2301 // Remove from inventory and send inventory
2302 ilist->deleteItem(item_i);
2304 SendInventory(peer_id);
2312 Catch invalid actions
2316 derr_server<<"WARNING: Server: Invalid action "
2317 <<action<<std::endl;
2321 else if(command == TOSERVER_RELEASE)
2330 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2333 else if(command == TOSERVER_SIGNTEXT)
2342 std::string datastring((char*)&data[2], datasize-2);
2343 std::istringstream is(datastring, std::ios_base::binary);
2346 is.read((char*)buf, 6);
2347 v3s16 blockpos = readV3S16(buf);
2348 is.read((char*)buf, 2);
2349 s16 id = readS16(buf);
2350 is.read((char*)buf, 2);
2351 u16 textlen = readU16(buf);
2353 for(u16 i=0; i<textlen; i++)
2355 is.read((char*)buf, 1);
2356 text += (char)buf[0];
2359 MapBlock *block = NULL;
2362 block = m_env.getMap().getBlockNoCreate(blockpos);
2364 catch(InvalidPositionException &e)
2366 derr_server<<"Error while setting sign text: "
2367 "block not found"<<std::endl;
2371 MapBlockObject *obj = block->getObject(id);
2374 derr_server<<"Error while setting sign text: "
2375 "object not found"<<std::endl;
2379 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2381 derr_server<<"Error while setting sign text: "
2382 "object is not a sign"<<std::endl;
2386 ((SignObject*)obj)->setText(text);
2388 obj->getBlock()->setChangedFlag();
2390 else if(command == TOSERVER_INVENTORY_ACTION)
2392 /*// Ignore inventory changes if in creative mode
2393 if(g_settings.getBool("creative_mode") == true)
2395 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2399 // Strip command and create a stream
2400 std::string datastring((char*)&data[2], datasize-2);
2401 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2402 std::istringstream is(datastring, std::ios_base::binary);
2404 InventoryAction *a = InventoryAction::deSerialize(is);
2408 Handle craftresult specially if not in creative mode
2410 bool disable_action = false;
2411 if(a->getType() == IACTION_MOVE
2412 && g_settings.getBool("creative_mode") == false)
2414 IMoveAction *ma = (IMoveAction*)a;
2415 // Don't allow moving anything to craftresult
2416 if(ma->to_name == "craftresult")
2419 disable_action = true;
2421 // When something is removed from craftresult
2422 if(ma->from_name == "craftresult")
2424 disable_action = true;
2425 // Remove stuff from craft
2426 InventoryList *clist = player->inventory.getList("craft");
2429 u16 count = ma->count;
2432 clist->decrementMaterials(count);
2435 // Feed action to player inventory
2436 a->apply(&player->inventory);
2439 // If something appeared in craftresult, throw it
2441 InventoryList *rlist = player->inventory.getList("craftresult");
2442 InventoryList *mlist = player->inventory.getList("main");
2443 if(rlist && mlist && rlist->getUsedSlots() == 1)
2445 InventoryItem *item1 = rlist->changeItem(0, NULL);
2446 mlist->addItem(item1);
2450 if(disable_action == false)
2452 // Feed action to player inventory
2453 a->apply(&player->inventory);
2458 SendInventory(player->peer_id);
2462 dstream<<"TOSERVER_INVENTORY_ACTION: "
2463 <<"InventoryAction::deSerialize() returned NULL"
2467 else if(command == TOSERVER_CHAT_MESSAGE)
2475 std::string datastring((char*)&data[2], datasize-2);
2476 std::istringstream is(datastring, std::ios_base::binary);
2479 is.read((char*)buf, 2);
2480 u16 len = readU16(buf);
2482 std::wstring message;
2483 for(u16 i=0; i<len; i++)
2485 is.read((char*)buf, 2);
2486 message += (wchar_t)readU16(buf);
2489 // Get player name of this client
2490 std::wstring name = narrow_to_wide(player->getName());
2492 // Line to send to players
2494 // Whether to send to the player that sent the line
2495 bool send_to_sender = false;
2496 // Whether to send to other players
2497 bool send_to_others = false;
2500 std::wstring commandprefix = L"/#";
2501 if(message.substr(0, commandprefix.size()) == commandprefix)
2503 line += L"Server: ";
2505 message = message.substr(commandprefix.size());
2506 // Get player name as narrow string
2507 std::string name_s = player->getName();
2508 // Convert message to narrow string
2509 std::string message_s = wide_to_narrow(message);
2510 // Operator is the single name defined in config.
2511 std::string operator_name = g_settings.get("name");
2512 bool is_operator = (operator_name != "" &&
2513 wide_to_narrow(name) == operator_name);
2514 bool valid_command = false;
2515 if(message_s == "help")
2517 line += L"-!- Available commands: ";
2521 line += L"shutdown setting ";
2526 send_to_sender = true;
2527 valid_command = true;
2529 else if(message_s == "status")
2531 line = getStatusString();
2532 send_to_sender = true;
2533 valid_command = true;
2535 else if(is_operator)
2537 if(message_s == "shutdown")
2539 dstream<<DTIME<<" Server: Operator requested shutdown."
2541 m_shutdown_requested.set(true);
2543 line += L"*** Server shutting down (operator request)";
2544 send_to_sender = true;
2545 valid_command = true;
2547 else if(message_s.substr(0,8) == "setting ")
2549 std::string confline = message_s.substr(8);
2550 g_settings.parseConfigLine(confline);
2551 line += L"-!- Setting changed.";
2552 send_to_sender = true;
2553 valid_command = true;
2557 if(valid_command == false)
2559 line += L"-!- Invalid command: " + message;
2560 send_to_sender = true;
2571 send_to_others = true;
2576 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2579 Send the message to clients
2581 for(core::map<u16, RemoteClient*>::Iterator
2582 i = m_clients.getIterator();
2583 i.atEnd() == false; i++)
2585 // Get client and check that it is valid
2586 RemoteClient *client = i.getNode()->getValue();
2587 assert(client->peer_id == i.getNode()->getKey());
2588 if(client->serialization_version == SER_FMT_VER_INVALID)
2592 bool sender_selected = (peer_id == client->peer_id);
2593 if(sender_selected == true && send_to_sender == false)
2595 if(sender_selected == false && send_to_others == false)
2598 SendChatMessage(client->peer_id, line);
2604 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2605 "unknown command "<<command<<std::endl;
2609 catch(SendFailedException &e)
2611 derr_server<<"Server::ProcessData(): SendFailedException: "
2617 void Server::onMapEditEvent(MapEditEvent *event)
2619 dstream<<"Server::onMapEditEvent()"<<std::endl;
2620 if(m_ignore_map_edit_events)
2622 MapEditEvent *e = event->clone();
2623 m_unsent_map_edit_queue.push_back(e);
2626 core::list<PlayerInfo> Server::getPlayerInfo()
2628 DSTACK(__FUNCTION_NAME);
2629 JMutexAutoLock envlock(m_env_mutex);
2630 JMutexAutoLock conlock(m_con_mutex);
2632 core::list<PlayerInfo> list;
2634 core::list<Player*> players = m_env.getPlayers();
2636 core::list<Player*>::Iterator i;
2637 for(i = players.begin();
2638 i != players.end(); i++)
2642 Player *player = *i;
2645 con::Peer *peer = m_con.GetPeer(player->peer_id);
2646 // Copy info from peer to info struct
2648 info.address = peer->address;
2649 info.avg_rtt = peer->avg_rtt;
2651 catch(con::PeerNotFoundException &e)
2653 // Set dummy peer info
2655 info.address = Address(0,0,0,0,0);
2659 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2660 info.position = player->getPosition();
2662 list.push_back(info);
2669 void Server::peerAdded(con::Peer *peer)
2671 DSTACK(__FUNCTION_NAME);
2672 dout_server<<"Server::peerAdded(): peer->id="
2673 <<peer->id<<std::endl;
2676 c.type = PEER_ADDED;
2677 c.peer_id = peer->id;
2679 m_peer_change_queue.push_back(c);
2682 void Server::deletingPeer(con::Peer *peer, bool timeout)
2684 DSTACK(__FUNCTION_NAME);
2685 dout_server<<"Server::deletingPeer(): peer->id="
2686 <<peer->id<<", timeout="<<timeout<<std::endl;
2689 c.type = PEER_REMOVED;
2690 c.peer_id = peer->id;
2691 c.timeout = timeout;
2692 m_peer_change_queue.push_back(c);
2695 void Server::SendObjectData(float dtime)
2697 DSTACK(__FUNCTION_NAME);
2699 core::map<v3s16, bool> stepped_blocks;
2701 for(core::map<u16, RemoteClient*>::Iterator
2702 i = m_clients.getIterator();
2703 i.atEnd() == false; i++)
2705 u16 peer_id = i.getNode()->getKey();
2706 RemoteClient *client = i.getNode()->getValue();
2707 assert(client->peer_id == peer_id);
2709 if(client->serialization_version == SER_FMT_VER_INVALID)
2712 client->SendObjectData(this, dtime, stepped_blocks);
2716 void Server::SendPlayerInfos()
2718 DSTACK(__FUNCTION_NAME);
2720 //JMutexAutoLock envlock(m_env_mutex);
2722 // Get connected players
2723 core::list<Player*> players = m_env.getPlayers(true);
2725 u32 player_count = players.getSize();
2726 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2728 SharedBuffer<u8> data(datasize);
2729 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2732 core::list<Player*>::Iterator i;
2733 for(i = players.begin();
2734 i != players.end(); i++)
2736 Player *player = *i;
2738 /*dstream<<"Server sending player info for player with "
2739 "peer_id="<<player->peer_id<<std::endl;*/
2741 writeU16(&data[start], player->peer_id);
2742 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2743 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2744 start += 2+PLAYERNAME_SIZE;
2747 //JMutexAutoLock conlock(m_con_mutex);
2750 m_con.SendToAll(0, data, true);
2754 Craft checking system
2772 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2778 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2784 enum ItemSpecType type;
2785 // Only other one of these is used
2791 items: a pointer to an array of 9 pointers to items
2792 specs: a pointer to an array of 9 ItemSpecs
2794 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2796 u16 items_min_x = 100;
2797 u16 items_max_x = 100;
2798 u16 items_min_y = 100;
2799 u16 items_max_y = 100;
2800 for(u16 y=0; y<3; y++)
2801 for(u16 x=0; x<3; x++)
2803 if(items[y*3 + x] == NULL)
2805 if(items_min_x == 100 || x < items_min_x)
2807 if(items_min_y == 100 || y < items_min_y)
2809 if(items_max_x == 100 || x > items_max_x)
2811 if(items_max_y == 100 || y > items_max_y)
2814 // No items at all, just return false
2815 if(items_min_x == 100)
2818 u16 items_w = items_max_x - items_min_x + 1;
2819 u16 items_h = items_max_y - items_min_y + 1;
2821 u16 specs_min_x = 100;
2822 u16 specs_max_x = 100;
2823 u16 specs_min_y = 100;
2824 u16 specs_max_y = 100;
2825 for(u16 y=0; y<3; y++)
2826 for(u16 x=0; x<3; x++)
2828 if(specs[y*3 + x].type == ITEM_NONE)
2830 if(specs_min_x == 100 || x < specs_min_x)
2832 if(specs_min_y == 100 || y < specs_min_y)
2834 if(specs_max_x == 100 || x > specs_max_x)
2836 if(specs_max_y == 100 || y > specs_max_y)
2839 // No specs at all, just return false
2840 if(specs_min_x == 100)
2843 u16 specs_w = specs_max_x - specs_min_x + 1;
2844 u16 specs_h = specs_max_y - specs_min_y + 1;
2847 if(items_w != specs_w || items_h != specs_h)
2850 for(u16 y=0; y<specs_h; y++)
2851 for(u16 x=0; x<specs_w; x++)
2853 u16 items_x = items_min_x + x;
2854 u16 items_y = items_min_y + y;
2855 u16 specs_x = specs_min_x + x;
2856 u16 specs_y = specs_min_y + y;
2857 InventoryItem *item = items[items_y * 3 + items_x];
2858 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2860 if(spec.type == ITEM_NONE)
2862 // Has to be no item
2868 // There should be an item
2872 std::string itemname = item->getName();
2874 if(spec.type == ITEM_MATERIAL)
2876 if(itemname != "MaterialItem")
2878 MaterialItem *mitem = (MaterialItem*)item;
2879 if(mitem->getMaterial() != spec.num)
2882 else if(spec.type == ITEM_CRAFT)
2884 if(itemname != "CraftItem")
2886 CraftItem *mitem = (CraftItem*)item;
2887 if(mitem->getSubName() != spec.name)
2890 else if(spec.type == ITEM_TOOL)
2892 // Not supported yet
2895 else if(spec.type == ITEM_MBO)
2897 // Not supported yet
2902 // Not supported yet
2910 void Server::SendInventory(u16 peer_id)
2912 DSTACK(__FUNCTION_NAME);
2914 Player* player = m_env.getPlayer(peer_id);
2917 Calculate crafting stuff
2919 if(g_settings.getBool("creative_mode") == false)
2921 InventoryList *clist = player->inventory.getList("craft");
2922 InventoryList *rlist = player->inventory.getList("craftresult");
2925 rlist->clearItems();
2929 InventoryItem *items[9];
2930 for(u16 i=0; i<9; i++)
2932 items[i] = clist->getItem(i);
2941 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2942 if(checkItemCombination(items, specs))
2944 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2953 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2954 if(checkItemCombination(items, specs))
2956 rlist->addItem(new CraftItem("Stick", 4));
2965 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2966 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2967 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2968 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2969 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2970 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2971 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2972 if(checkItemCombination(items, specs))
2974 rlist->addItem(new MapBlockObjectItem("Sign"));
2983 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2984 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2985 if(checkItemCombination(items, specs))
2987 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2996 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2997 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2998 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2999 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3000 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3001 if(checkItemCombination(items, specs))
3003 rlist->addItem(new ToolItem("WPick", 0));
3012 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3013 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3014 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3015 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3016 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3017 if(checkItemCombination(items, specs))
3019 rlist->addItem(new ToolItem("STPick", 0));
3028 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3029 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3030 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3031 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3032 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3033 if(checkItemCombination(items, specs))
3035 rlist->addItem(new ToolItem("MesePick", 0));
3040 } // if creative_mode == false
3046 std::ostringstream os;
3047 //os.imbue(std::locale("C"));
3049 player->inventory.serialize(os);
3051 std::string s = os.str();
3053 SharedBuffer<u8> data(s.size()+2);
3054 writeU16(&data[0], TOCLIENT_INVENTORY);
3055 memcpy(&data[2], s.c_str(), s.size());
3058 m_con.Send(peer_id, 0, data, true);
3061 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3063 DSTACK(__FUNCTION_NAME);
3065 std::ostringstream os(std::ios_base::binary);
3069 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3070 os.write((char*)buf, 2);
3073 writeU16(buf, message.size());
3074 os.write((char*)buf, 2);
3077 for(u32 i=0; i<message.size(); i++)
3081 os.write((char*)buf, 2);
3085 std::string s = os.str();
3086 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3088 m_con.Send(peer_id, 0, data, true);
3091 void Server::BroadcastChatMessage(const std::wstring &message)
3093 for(core::map<u16, RemoteClient*>::Iterator
3094 i = m_clients.getIterator();
3095 i.atEnd() == false; i++)
3097 // Get client and check that it is valid
3098 RemoteClient *client = i.getNode()->getValue();
3099 assert(client->peer_id == i.getNode()->getKey());
3100 if(client->serialization_version == SER_FMT_VER_INVALID)
3103 SendChatMessage(client->peer_id, message);
3107 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3111 SharedBuffer<u8> reply(replysize);
3112 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3113 writeS16(&reply[2], p.X);
3114 writeS16(&reply[4], p.Y);
3115 writeS16(&reply[6], p.Z);
3117 for(core::map<u16, RemoteClient*>::Iterator
3118 i = m_clients.getIterator();
3119 i.atEnd() == false; i++)
3121 // Get client and check that it is valid
3122 RemoteClient *client = i.getNode()->getValue();
3123 assert(client->peer_id == i.getNode()->getKey());
3124 if(client->serialization_version == SER_FMT_VER_INVALID)
3127 // Don't send if it's the same one
3128 if(client->peer_id == ignore_id)
3132 m_con.Send(client->peer_id, 0, reply, true);
3136 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3138 for(core::map<u16, RemoteClient*>::Iterator
3139 i = m_clients.getIterator();
3140 i.atEnd() == false; i++)
3142 // Get client and check that it is valid
3143 RemoteClient *client = i.getNode()->getValue();
3144 assert(client->peer_id == i.getNode()->getKey());
3145 if(client->serialization_version == SER_FMT_VER_INVALID)
3148 // Don't send if it's the same one
3149 if(client->peer_id == ignore_id)
3153 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3154 SharedBuffer<u8> reply(replysize);
3155 writeU16(&reply[0], TOCLIENT_ADDNODE);
3156 writeS16(&reply[2], p.X);
3157 writeS16(&reply[4], p.Y);
3158 writeS16(&reply[6], p.Z);
3159 n.serialize(&reply[8], client->serialization_version);
3162 m_con.Send(client->peer_id, 0, reply, true);
3166 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3168 DSTACK(__FUNCTION_NAME);
3170 Create a packet with the block in the right format
3173 std::ostringstream os(std::ios_base::binary);
3174 block->serialize(os, ver);
3175 std::string s = os.str();
3176 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3178 u32 replysize = 8 + blockdata.getSize();
3179 SharedBuffer<u8> reply(replysize);
3180 v3s16 p = block->getPos();
3181 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3182 writeS16(&reply[2], p.X);
3183 writeS16(&reply[4], p.Y);
3184 writeS16(&reply[6], p.Z);
3185 memcpy(&reply[8], *blockdata, blockdata.getSize());
3187 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3188 <<": \tpacket size: "<<replysize<<std::endl;*/
3193 m_con.Send(peer_id, 1, reply, true);
3196 void Server::SendBlocks(float dtime)
3198 DSTACK(__FUNCTION_NAME);
3200 JMutexAutoLock envlock(m_env_mutex);
3201 JMutexAutoLock conlock(m_con_mutex);
3203 //TimeTaker timer("Server::SendBlocks");
3205 core::array<PrioritySortedBlockTransfer> queue;
3207 s32 total_sending = 0;
3209 for(core::map<u16, RemoteClient*>::Iterator
3210 i = m_clients.getIterator();
3211 i.atEnd() == false; i++)
3213 RemoteClient *client = i.getNode()->getValue();
3214 assert(client->peer_id == i.getNode()->getKey());
3216 total_sending += client->SendingCount();
3218 if(client->serialization_version == SER_FMT_VER_INVALID)
3221 client->GetNextBlocks(this, dtime, queue);
3225 // Lowest priority number comes first.
3226 // Lowest is most important.
3229 for(u32 i=0; i<queue.size(); i++)
3231 //TODO: Calculate limit dynamically
3232 if(total_sending >= g_settings.getS32
3233 ("max_simultaneous_block_sends_server_total"))
3236 PrioritySortedBlockTransfer q = queue[i];
3238 MapBlock *block = NULL;
3241 block = m_env.getMap().getBlockNoCreate(q.pos);
3243 catch(InvalidPositionException &e)
3248 RemoteClient *client = getClient(q.peer_id);
3250 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3252 client->SentBlock(q.pos);
3259 RemoteClient* Server::getClient(u16 peer_id)
3261 DSTACK(__FUNCTION_NAME);
3262 //JMutexAutoLock lock(m_con_mutex);
3263 core::map<u16, RemoteClient*>::Node *n;
3264 n = m_clients.find(peer_id);
3265 // A client should exist for all peers
3267 return n->getValue();
3270 std::wstring Server::getStatusString()
3272 std::wostringstream os(std::ios_base::binary);
3275 os<<L"uptime="<<m_uptime.get();
3276 // Information about clients
3278 for(core::map<u16, RemoteClient*>::Iterator
3279 i = m_clients.getIterator();
3280 i.atEnd() == false; i++)
3282 // Get client and check that it is valid
3283 RemoteClient *client = i.getNode()->getValue();
3284 assert(client->peer_id == i.getNode()->getKey());
3285 if(client->serialization_version == SER_FMT_VER_INVALID)
3288 Player *player = m_env.getPlayer(client->peer_id);
3289 // Get name of player
3290 std::wstring name = L"unknown";
3292 name = narrow_to_wide(player->getName());
3293 // Add name to information string
3297 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3298 os<<" WARNING: Map saving is disabled."<<std::endl;
3303 void setCreativeInventory(Player *player)
3305 player->resetInventory();
3307 // Give some good picks
3309 InventoryItem *item = new ToolItem("STPick", 0);
3310 void* r = player->inventory.addItem("main", item);
3314 InventoryItem *item = new ToolItem("MesePick", 0);
3315 void* r = player->inventory.addItem("main", item);
3322 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3325 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3326 player->inventory.addItem("main", item);
3329 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3331 // Skip some materials
3332 if(i == CONTENT_WATER || i == CONTENT_TORCH
3333 || i == CONTENT_COALSTONE)
3336 InventoryItem *item = new MaterialItem(i, 1);
3337 player->inventory.addItem("main", item);
3341 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3342 void* r = player->inventory.addItem("main", item);
3347 Player *Server::emergePlayer(const char *name, const char *password,
3351 Try to get an existing player
3353 Player *player = m_env.getPlayer(name);
3356 // If player is already connected, cancel
3357 if(player->peer_id != 0)
3359 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3364 player->peer_id = peer_id;
3366 // Reset inventory to creative if in creative mode
3367 if(g_settings.getBool("creative_mode"))
3369 setCreativeInventory(player);
3376 If player with the wanted peer_id already exists, cancel.
3378 if(m_env.getPlayer(peer_id) != NULL)
3380 dstream<<"emergePlayer(): Player with wrong name but same"
3381 " peer_id already exists"<<std::endl;
3389 player = new ServerRemotePlayer();
3390 //player->peer_id = c.peer_id;
3391 //player->peer_id = PEER_ID_INEXISTENT;
3392 player->peer_id = peer_id;
3393 player->updateName(name);
3399 dstream<<"Server: Finding spawn place for player \""
3400 <<player->getName()<<"\""<<std::endl;
3404 player->setPosition(intToFloat(v3s16(
3411 s16 groundheight = 0;
3413 // Try to find a good place a few times
3414 for(s32 i=0; i<500; i++)
3417 // We're going to try to throw the player to this position
3418 nodepos = v2s16(-range + (myrand()%(range*2)),
3419 -range + (myrand()%(range*2)));
3420 v2s16 sectorpos = getNodeSectorPos(nodepos);
3422 Ignore position if it is near a chunk edge.
3423 Otherwise it would cause excessive loading time at
3427 if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1))
3428 != m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1)))
3432 m_env.getMap().emergeSector(sectorpos);
3433 // Get ground height at point
3434 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3435 // Don't go underwater
3436 if(groundheight < WATER_LEVEL)
3438 //dstream<<"-> Underwater"<<std::endl;
3441 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3442 // Get block at point
3444 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3445 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3446 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3447 // Don't go inside ground
3449 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3450 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3451 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3452 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3453 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3454 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3456 dstream<<"-> Inside ground"<<std::endl;
3460 }catch(InvalidPositionException &e)
3462 dstream<<"-> Invalid position"<<std::endl;
3463 // Ignore invalid position
3467 // Found a good place
3468 dstream<<"Searched through "<<i<<" places."<<std::endl;
3473 // If no suitable place was not found, go above water at least.
3474 if(groundheight < WATER_LEVEL)
3475 groundheight = WATER_LEVEL;
3477 player->setPosition(intToFloat(v3s16(
3485 Add player to environment
3488 m_env.addPlayer(player);
3491 Add stuff to inventory
3494 if(g_settings.getBool("creative_mode"))
3496 setCreativeInventory(player);
3501 InventoryItem *item = new ToolItem("WPick", 32000);
3502 void* r = player->inventory.addItem("main", item);
3506 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3507 void* r = player->inventory.addItem("main", item);
3511 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3512 void* r = player->inventory.addItem("main", item);
3516 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3517 void* r = player->inventory.addItem("main", item);
3521 InventoryItem *item = new CraftItem("Stick", 4);
3522 void* r = player->inventory.addItem("main", item);
3526 InventoryItem *item = new ToolItem("WPick", 32000);
3527 void* r = player->inventory.addItem("main", item);
3531 InventoryItem *item = new ToolItem("STPick", 32000);
3532 void* r = player->inventory.addItem("main", item);
3535 /*// Give some lights
3537 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3538 bool r = player->inventory.addItem("main", item);
3542 for(u16 i=0; i<4; i++)
3544 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3545 bool r = player->inventory.addItem("main", item);
3548 /*// Give some other stuff
3550 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3551 bool r = player->inventory.addItem("main", item);
3558 } // create new player
3562 void Server::UpdateBlockWaterPressure(MapBlock *block,
3563 core::map<v3s16, MapBlock*> &modified_blocks)
3565 MapVoxelManipulator v(&m_env.getMap());
3566 v.m_disable_water_climb =
3567 g_settings.getBool("disable_water_climb");
3569 VoxelArea area(block->getPosRelative(),
3570 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3574 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3576 catch(ProcessingLimitException &e)
3578 dstream<<"Processing limit reached (1)"<<std::endl;
3581 v.blitBack(modified_blocks);
3585 void Server::handlePeerChange(PeerChange &c)
3587 JMutexAutoLock envlock(m_env_mutex);
3588 JMutexAutoLock conlock(m_con_mutex);
3590 if(c.type == PEER_ADDED)
3597 core::map<u16, RemoteClient*>::Node *n;
3598 n = m_clients.find(c.peer_id);
3599 // The client shouldn't already exist
3603 RemoteClient *client = new RemoteClient();
3604 client->peer_id = c.peer_id;
3605 m_clients.insert(client->peer_id, client);
3608 else if(c.type == PEER_REMOVED)
3615 core::map<u16, RemoteClient*>::Node *n;
3616 n = m_clients.find(c.peer_id);
3617 // The client should exist
3620 // Collect information about leaving in chat
3621 std::wstring message;
3623 std::wstring name = L"unknown";
3624 Player *player = m_env.getPlayer(c.peer_id);
3626 name = narrow_to_wide(player->getName());
3630 message += L" left game";
3632 message += L" (timed out)";
3637 m_env.removePlayer(c.peer_id);
3640 // Set player client disconnected
3642 Player *player = m_env.getPlayer(c.peer_id);
3644 player->peer_id = 0;
3648 delete m_clients[c.peer_id];
3649 m_clients.remove(c.peer_id);
3651 // Send player info to all remaining clients
3654 // Send leave chat message to all remaining clients
3655 BroadcastChatMessage(message);
3664 void Server::handlePeerChanges()
3666 while(m_peer_change_queue.size() > 0)
3668 PeerChange c = m_peer_change_queue.pop_front();
3670 dout_server<<"Server: Handling peer change: "
3671 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3674 handlePeerChange(c);
3678 void dedicated_server_loop(Server &server, bool &kill)
3680 DSTACK(__FUNCTION_NAME);
3682 std::cout<<DTIME<<std::endl;
3683 std::cout<<"========================"<<std::endl;
3684 std::cout<<"Running dedicated server"<<std::endl;
3685 std::cout<<"========================"<<std::endl;
3686 std::cout<<std::endl;
3690 // This is kind of a hack but can be done like this
3691 // because server.step() is very light
3695 if(server.getShutdownRequested() || kill)
3697 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3701 static int counter = 0;
3707 core::list<PlayerInfo> list = server.getPlayerInfo();
3708 core::list<PlayerInfo>::Iterator i;
3709 static u32 sum_old = 0;
3710 u32 sum = PIChecksum(list);
3713 std::cout<<DTIME<<"Player info:"<<std::endl;
3714 for(i=list.begin(); i!=list.end(); i++)
3716 i->PrintLine(&std::cout);