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.
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
27 #include "clientserver.h"
29 #include "jmutexautolock.h"
31 #include "constants.h"
33 #include "materials.h"
36 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
38 void * ServerThread::Thread()
42 DSTACK(__FUNCTION_NAME);
44 BEGIN_DEBUG_EXCEPTION_HANDLER
49 //TimeTaker timer("AsyncRunStep() + Receive()");
52 //TimeTaker timer("AsyncRunStep()");
53 m_server->AsyncRunStep();
56 //dout_server<<"Running m_server->Receive()"<<std::endl;
59 catch(con::NoIncomingDataException &e)
62 catch(con::PeerNotFoundException &e)
64 dout_server<<"Server: PeerNotFoundException"<<std::endl;
68 END_DEBUG_EXCEPTION_HANDLER
73 void * EmergeThread::Thread()
77 DSTACK(__FUNCTION_NAME);
81 BEGIN_DEBUG_EXCEPTION_HANDLER
84 Get block info from queue, emerge them and send them
87 After queue is empty, exit.
91 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
95 SharedPtr<QueuedBlockEmerge> q(qptr);
99 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
101 //TimeTaker timer("block emerge");
104 Try to emerge it from somewhere.
106 If it is only wanted as optional, only loading from disk
111 Check if any peer wants it as non-optional. In that case it
114 Also decrement the emerge queue count in clients.
117 bool optional = true;
120 core::map<u16, u8>::Iterator i;
121 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
123 //u16 peer_id = i.getNode()->getKey();
126 u8 flags = i.getNode()->getValue();
127 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
133 /*dstream<<"EmergeThread: p="
134 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
135 <<"optional="<<optional<<std::endl;*/
137 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
139 core::map<v3s16, MapBlock*> changed_blocks;
140 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
142 MapBlock *block = NULL;
143 bool got_block = true;
144 core::map<v3s16, MapBlock*> modified_blocks;
148 //TimeTaker envlockwaittimer("block emerge envlock wait time");
151 JMutexAutoLock envlock(m_server->m_env_mutex);
153 //envlockwaittimer.stop();
155 //TimeTaker timer("block emerge (while env locked)");
158 bool only_from_disk = false;
161 only_from_disk = true;
163 // First check if the block already exists
164 //block = map.getBlockNoCreate(p);
168 //dstream<<"Calling emergeBlock"<<std::endl;
169 block = map.emergeBlock(
173 lighting_invalidated_blocks);
176 // If it is a dummy, block was not found on disk
179 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
182 if(only_from_disk == false)
184 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
189 catch(InvalidPositionException &e)
192 // This happens when position is over limit.
198 if(debug && changed_blocks.size() > 0)
200 dout_server<<DTIME<<"Got changed_blocks: ";
201 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
202 i.atEnd() == false; i++)
204 MapBlock *block = i.getNode()->getValue();
205 v3s16 p = block->getPos();
206 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
208 dout_server<<std::endl;
212 Collect a list of blocks that have been modified in
213 addition to the fetched one.
216 // Add all the "changed blocks" to modified_blocks
217 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
218 i.atEnd() == false; i++)
220 MapBlock *block = i.getNode()->getValue();
221 modified_blocks.insert(block->getPos(), block);
224 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
225 <<" blocks"<<std::endl;*/
227 //TimeTaker timer("** updateLighting");
229 // Update lighting without locking the environment mutex,
230 // add modified blocks to changed blocks
231 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
233 // If we got no block, there should be no invalidated blocks
236 assert(lighting_invalidated_blocks.size() == 0);
242 Set sent status of modified blocks on clients
245 // NOTE: Server's clients are also behind the connection mutex
246 JMutexAutoLock lock(m_server->m_con_mutex);
249 Add the originally fetched block to the modified list
253 modified_blocks.insert(p, block);
257 Set the modified blocks unsent for all the clients
260 for(core::map<u16, RemoteClient*>::Iterator
261 i = m_server->m_clients.getIterator();
262 i.atEnd() == false; i++)
264 RemoteClient *client = i.getNode()->getValue();
266 if(modified_blocks.size() > 0)
268 // Remove block from sent history
269 client->SetBlocksNotSent(modified_blocks);
275 END_DEBUG_EXCEPTION_HANDLER
280 void RemoteClient::GetNextBlocks(Server *server, float dtime,
281 core::array<PrioritySortedBlockTransfer> &dest)
283 DSTACK(__FUNCTION_NAME);
286 m_nearest_unsent_reset_timer += dtime;
288 // Won't send anything if already sending
289 if(m_blocks_sending.size() >= g_settings.getU16
290 ("max_simultaneous_block_sends_per_client"))
292 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
296 Player *player = server->m_env.getPlayer(peer_id);
298 assert(player != NULL);
300 v3f playerpos = player->getPosition();
301 v3f playerspeed = player->getSpeed();
303 v3s16 center_nodepos = floatToInt(playerpos, BS);
305 v3s16 center = getNodeBlockPos(center_nodepos);
307 // Camera position and direction
309 playerpos + v3f(0, BS+BS/2, 0);
310 v3f camera_dir = v3f(0,0,1);
311 camera_dir.rotateYZBy(player->getPitch());
312 camera_dir.rotateXZBy(player->getYaw());
315 Get the starting value of the block finder radius.
317 s16 last_nearest_unsent_d;
320 if(m_last_center != center)
322 m_nearest_unsent_d = 0;
323 m_last_center = center;
326 /*dstream<<"m_nearest_unsent_reset_timer="
327 <<m_nearest_unsent_reset_timer<<std::endl;*/
328 if(m_nearest_unsent_reset_timer > 5.0)
330 m_nearest_unsent_reset_timer = 0;
331 m_nearest_unsent_d = 0;
332 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
335 last_nearest_unsent_d = m_nearest_unsent_d;
337 d_start = m_nearest_unsent_d;
339 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
340 ("max_simultaneous_block_sends_per_client");
341 u16 maximum_simultaneous_block_sends =
342 maximum_simultaneous_block_sends_setting;
345 Check the time from last addNode/removeNode.
347 Decrease send rate if player is building stuff.
349 m_time_from_building += dtime;
350 if(m_time_from_building < g_settings.getFloat(
351 "full_block_send_enable_min_time_from_building"))
353 maximum_simultaneous_block_sends
354 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
357 u32 num_blocks_selected = m_blocks_sending.size();
360 next time d will be continued from the d from which the nearest
361 unsent block was found this time.
363 This is because not necessarily any of the blocks found this
364 time are actually sent.
366 s32 new_nearest_unsent_d = -1;
368 s16 d_max = g_settings.getS16("max_block_send_distance");
369 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
371 //dstream<<"Starting from "<<d_start<<std::endl;
373 for(s16 d = d_start; d <= d_max; d++)
375 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
378 If m_nearest_unsent_d was changed by the EmergeThread
379 (it can change it to 0 through SetBlockNotSent),
381 Else update m_nearest_unsent_d
383 if(m_nearest_unsent_d != last_nearest_unsent_d)
385 d = m_nearest_unsent_d;
386 last_nearest_unsent_d = m_nearest_unsent_d;
390 Get the border/face dot coordinates of a "d-radiused"
393 core::list<v3s16> list;
394 getFacePositions(list, d);
396 core::list<v3s16>::Iterator li;
397 for(li=list.begin(); li!=list.end(); li++)
399 v3s16 p = *li + center;
403 - Don't allow too many simultaneous transfers
404 - EXCEPT when the blocks are very close
406 Also, don't send blocks that are already flying.
409 u16 maximum_simultaneous_block_sends_now =
410 maximum_simultaneous_block_sends;
412 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
414 maximum_simultaneous_block_sends_now =
415 maximum_simultaneous_block_sends_setting;
418 // Limit is dynamically lowered when building
419 if(num_blocks_selected
420 >= maximum_simultaneous_block_sends_now)
422 /*dstream<<"Not sending more blocks. Queue full. "
423 <<m_blocks_sending.size()
428 if(m_blocks_sending.find(p) != NULL)
434 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
435 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
436 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
437 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
438 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
439 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
442 // If this is true, inexistent block will be made from scratch
443 bool generate = d <= d_max_gen;
446 /*// Limit the generating area vertically to 2/3
447 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
450 // Limit the send area vertically to 2/3
451 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
457 If block is far away, don't generate it unless it is
460 NOTE: We can't know the ground level this way with the
466 MapSector *sector = NULL;
469 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
471 catch(InvalidPositionException &e)
477 // Get center ground height in nodes
478 f32 gh = sector->getGroundHeight(
479 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
480 // Block center y in nodes
481 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
482 // If differs a lot, don't generate
483 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
490 Don't generate or send if not in sight
493 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
499 Don't send already sent blocks
502 if(m_blocks_sent.find(p) != NULL)
507 Check if map has this block
509 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
511 bool surely_not_found_on_disk = false;
512 bool block_is_invalid = false;
517 surely_not_found_on_disk = true;
520 if(block->isValid() == false)
522 block_is_invalid = true;
526 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
527 v2s16 chunkpos = map->sector_to_chunk(p2d);
528 if(map->chunkNonVolatile(chunkpos) == false)
529 block_is_invalid = true;
533 If block has been marked to not exist on disk (dummy)
534 and generating new ones is not wanted, skip block.
536 if(generate == false && surely_not_found_on_disk == true)
543 Record the lowest d from which a a block has been
544 found being not sent and possibly to exist
546 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
548 new_nearest_unsent_d = d;
552 Add inexistent block to emerge queue.
554 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
556 //TODO: Get value from somewhere
557 // Allow only one block in emerge queue
558 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
560 //dstream<<"Adding block to emerge queue"<<std::endl;
562 // Add it to the emerge queue and trigger the thread
565 if(generate == false)
566 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
568 server->m_emerge_queue.addBlock(peer_id, p, flags);
569 server->m_emergethread.trigger();
577 Add block to send queue
580 PrioritySortedBlockTransfer q((float)d, p, peer_id);
584 num_blocks_selected += 1;
589 if(new_nearest_unsent_d != -1)
591 m_nearest_unsent_d = new_nearest_unsent_d;
595 void RemoteClient::SendObjectData(
598 core::map<v3s16, bool> &stepped_blocks
601 DSTACK(__FUNCTION_NAME);
603 // Can't send anything without knowing version
604 if(serialization_version == SER_FMT_VER_INVALID)
606 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
612 Send a TOCLIENT_OBJECTDATA packet.
616 u16 number of player positions
627 std::ostringstream os(std::ios_base::binary);
631 writeU16(buf, TOCLIENT_OBJECTDATA);
632 os.write((char*)buf, 2);
635 Get and write player data
638 // Get connected players
639 core::list<Player*> players = server->m_env.getPlayers(true);
641 // Write player count
642 u16 playercount = players.size();
643 writeU16(buf, playercount);
644 os.write((char*)buf, 2);
646 core::list<Player*>::Iterator i;
647 for(i = players.begin();
648 i != players.end(); i++)
652 v3f pf = player->getPosition();
653 v3f sf = player->getSpeed();
655 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
656 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
657 s32 pitch_i (player->getPitch() * 100);
658 s32 yaw_i (player->getYaw() * 100);
660 writeU16(buf, player->peer_id);
661 os.write((char*)buf, 2);
662 writeV3S32(buf, position_i);
663 os.write((char*)buf, 12);
664 writeV3S32(buf, speed_i);
665 os.write((char*)buf, 12);
666 writeS32(buf, pitch_i);
667 os.write((char*)buf, 4);
668 writeS32(buf, yaw_i);
669 os.write((char*)buf, 4);
673 Get and write object data
679 For making players to be able to build to their nearby
680 environment (building is not possible on blocks that are not
683 - Add blocks to emerge queue if they are not found
685 SUGGESTION: These could be ignored from the backside of the player
688 Player *player = server->m_env.getPlayer(peer_id);
692 v3f playerpos = player->getPosition();
693 v3f playerspeed = player->getSpeed();
695 v3s16 center_nodepos = floatToInt(playerpos, BS);
696 v3s16 center = getNodeBlockPos(center_nodepos);
698 s16 d_max = g_settings.getS16("active_object_range");
700 // Number of blocks whose objects were written to bos
703 std::ostringstream bos(std::ios_base::binary);
705 for(s16 d = 0; d <= d_max; d++)
707 core::list<v3s16> list;
708 getFacePositions(list, d);
710 core::list<v3s16>::Iterator li;
711 for(li=list.begin(); li!=list.end(); li++)
713 v3s16 p = *li + center;
716 Ignore blocks that haven't been sent to the client
719 if(m_blocks_sent.find(p) == NULL)
723 // Try stepping block and add it to a send queue
728 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
731 Step block if not in stepped_blocks and add to stepped_blocks.
733 if(stepped_blocks.find(p) == NULL)
735 block->stepObjects(dtime, true, server->getDayNightRatio());
736 stepped_blocks.insert(p, true);
737 block->setChangedFlag();
740 // Skip block if there are no objects
741 if(block->getObjectCount() == 0)
750 bos.write((char*)buf, 6);
753 block->serializeObjects(bos, serialization_version);
758 Stop collecting objects if data is already too big
760 // Sum of player and object data sizes
761 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
762 // break out if data too big
763 if(sum > MAX_OBJECTDATA_SIZE)
765 goto skip_subsequent;
769 catch(InvalidPositionException &e)
772 // Add it to the emerge queue and trigger the thread.
773 // Fetch the block only if it is on disk.
775 // Grab and increment counter
776 /*SharedPtr<JMutexAutoLock> lock
777 (m_num_blocks_in_emerge_queue.getLock());
778 m_num_blocks_in_emerge_queue.m_value++;*/
780 // Add to queue as an anonymous fetch from disk
781 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
782 server->m_emerge_queue.addBlock(0, p, flags);
783 server->m_emergethread.trigger();
791 writeU16(buf, blockcount);
792 os.write((char*)buf, 2);
794 // Write block objects
801 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
804 std::string s = os.str();
805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
806 // Send as unreliable
807 server->m_con.Send(peer_id, 0, data, false);
810 void RemoteClient::GotBlock(v3s16 p)
812 if(m_blocks_sending.find(p) != NULL)
813 m_blocks_sending.remove(p);
816 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
817 " m_blocks_sending"<<std::endl;*/
818 m_excess_gotblocks++;
820 m_blocks_sent.insert(p, true);
823 void RemoteClient::SentBlock(v3s16 p)
825 if(m_blocks_sending.find(p) == NULL)
826 m_blocks_sending.insert(p, 0.0);
828 dstream<<"RemoteClient::SentBlock(): Sent block"
829 " already in m_blocks_sending"<<std::endl;
832 void RemoteClient::SetBlockNotSent(v3s16 p)
834 m_nearest_unsent_d = 0;
836 if(m_blocks_sending.find(p) != NULL)
837 m_blocks_sending.remove(p);
838 if(m_blocks_sent.find(p) != NULL)
839 m_blocks_sent.remove(p);
842 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
844 m_nearest_unsent_d = 0;
846 for(core::map<v3s16, MapBlock*>::Iterator
847 i = blocks.getIterator();
848 i.atEnd()==false; i++)
850 v3s16 p = i.getNode()->getKey();
852 if(m_blocks_sending.find(p) != NULL)
853 m_blocks_sending.remove(p);
854 if(m_blocks_sent.find(p) != NULL)
855 m_blocks_sent.remove(p);
863 PlayerInfo::PlayerInfo()
868 void PlayerInfo::PrintLine(std::ostream *s)
871 (*s)<<"\""<<name<<"\" ("
872 <<(position.X/10)<<","<<(position.Y/10)
873 <<","<<(position.Z/10)<<") ";
875 (*s)<<" avg_rtt="<<avg_rtt;
879 u32 PIChecksum(core::list<PlayerInfo> &l)
881 core::list<PlayerInfo>::Iterator i;
884 for(i=l.begin(); i!=l.end(); i++)
886 checksum += a * (i->id+1);
887 checksum ^= 0x435aafcd;
898 std::string mapsavedir
900 m_env(new ServerMap(mapsavedir)),
901 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
903 m_emergethread(this),
906 m_time_of_day_send_timer(0),
908 m_mapsavedir(mapsavedir),
909 m_shutdown_requested(false)
911 //m_flowwater_timer = 0.0;
912 m_liquid_transform_timer = 0.0;
913 m_print_info_timer = 0.0;
914 m_objectdata_timer = 0.0;
915 m_emergethread_trigger_timer = 0.0;
916 m_savemap_timer = 0.0;
920 m_step_dtime_mutex.Init();
924 m_env.deSerializePlayers(m_mapsavedir);
930 Send shutdown message
933 JMutexAutoLock conlock(m_con_mutex);
935 std::wstring line = L"*** Server shutting down";
938 Send the message to clients
940 for(core::map<u16, RemoteClient*>::Iterator
941 i = m_clients.getIterator();
942 i.atEnd() == false; i++)
944 // Get client and check that it is valid
945 RemoteClient *client = i.getNode()->getValue();
946 assert(client->peer_id == i.getNode()->getKey());
947 if(client->serialization_version == SER_FMT_VER_INVALID)
950 SendChatMessage(client->peer_id, line);
957 m_env.serializePlayers(m_mapsavedir);
968 JMutexAutoLock clientslock(m_con_mutex);
970 for(core::map<u16, RemoteClient*>::Iterator
971 i = m_clients.getIterator();
972 i.atEnd() == false; i++)
975 // NOTE: These are removed by env destructor
977 u16 peer_id = i.getNode()->getKey();
978 JMutexAutoLock envlock(m_env_mutex);
979 m_env.removePlayer(peer_id);
983 delete i.getNode()->getValue();
988 void Server::start(unsigned short port)
990 DSTACK(__FUNCTION_NAME);
991 // Stop thread if already running
994 // Initialize connection
995 m_con.setTimeoutMs(30);
999 m_thread.setRun(true);
1002 dout_server<<"Server started on port "<<port<<std::endl;
1007 DSTACK(__FUNCTION_NAME);
1008 // Stop threads (set run=false first so both start stopping)
1009 m_thread.setRun(false);
1010 m_emergethread.setRun(false);
1012 m_emergethread.stop();
1014 dout_server<<"Server threads stopped"<<std::endl;
1017 void Server::step(float dtime)
1019 DSTACK(__FUNCTION_NAME);
1024 JMutexAutoLock lock(m_step_dtime_mutex);
1025 m_step_dtime += dtime;
1029 void Server::AsyncRunStep()
1031 DSTACK(__FUNCTION_NAME);
1035 JMutexAutoLock lock1(m_step_dtime_mutex);
1036 dtime = m_step_dtime;
1039 // Send blocks to clients
1045 //dstream<<"Server steps "<<dtime<<std::endl;
1046 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1049 JMutexAutoLock lock1(m_step_dtime_mutex);
1050 m_step_dtime -= dtime;
1057 m_uptime.set(m_uptime.get() + dtime);
1061 Update m_time_of_day
1064 m_time_counter += dtime;
1065 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1066 u32 units = (u32)(m_time_counter*speed);
1067 m_time_counter -= (f32)units / speed;
1068 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1070 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1073 Send to clients at constant intervals
1076 m_time_of_day_send_timer -= dtime;
1077 if(m_time_of_day_send_timer < 0.0)
1079 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1081 //JMutexAutoLock envlock(m_env_mutex);
1082 JMutexAutoLock conlock(m_con_mutex);
1084 for(core::map<u16, RemoteClient*>::Iterator
1085 i = m_clients.getIterator();
1086 i.atEnd() == false; i++)
1088 RemoteClient *client = i.getNode()->getValue();
1089 //Player *player = m_env.getPlayer(client->peer_id);
1091 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1092 m_time_of_day.get());
1094 m_con.Send(client->peer_id, 0, data, true);
1100 // Process connection's timeouts
1101 JMutexAutoLock lock2(m_con_mutex);
1102 m_con.RunTimeouts(dtime);
1106 // This has to be called so that the client list gets synced
1107 // with the peer list of the connection
1108 handlePeerChanges();
1113 // This also runs Map's timers
1114 JMutexAutoLock lock(m_env_mutex);
1125 m_liquid_transform_timer += dtime;
1126 if(m_liquid_transform_timer >= 1.00)
1128 m_liquid_transform_timer -= 1.00;
1130 JMutexAutoLock lock(m_env_mutex);
1132 core::map<v3s16, MapBlock*> modified_blocks;
1133 m_env.getMap().transformLiquids(modified_blocks);
1138 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1139 ServerMap &map = ((ServerMap&)m_env.getMap());
1140 map.updateLighting(modified_blocks, lighting_modified_blocks);
1142 // Add blocks modified by lighting to modified_blocks
1143 for(core::map<v3s16, MapBlock*>::Iterator
1144 i = lighting_modified_blocks.getIterator();
1145 i.atEnd() == false; i++)
1147 MapBlock *block = i.getNode()->getValue();
1148 modified_blocks.insert(block->getPos(), block);
1152 Set the modified blocks unsent for all the clients
1155 JMutexAutoLock lock2(m_con_mutex);
1157 for(core::map<u16, RemoteClient*>::Iterator
1158 i = m_clients.getIterator();
1159 i.atEnd() == false; i++)
1161 RemoteClient *client = i.getNode()->getValue();
1163 if(modified_blocks.size() > 0)
1165 // Remove block from sent history
1166 client->SetBlocksNotSent(modified_blocks);
1171 // Periodically print some info
1173 float &counter = m_print_info_timer;
1179 JMutexAutoLock lock2(m_con_mutex);
1181 for(core::map<u16, RemoteClient*>::Iterator
1182 i = m_clients.getIterator();
1183 i.atEnd() == false; i++)
1185 //u16 peer_id = i.getNode()->getKey();
1186 RemoteClient *client = i.getNode()->getValue();
1187 client->PrintInfo(std::cout);
1193 Check added and deleted active objects
1196 JMutexAutoLock envlock(m_env_mutex);
1197 JMutexAutoLock conlock(m_con_mutex);
1199 for(core::map<u16, RemoteClient*>::Iterator
1200 i = m_clients.getIterator();
1201 i.atEnd() == false; i++)
1203 RemoteClient *client = i.getNode()->getValue();
1204 Player *player = m_env.getPlayer(client->peer_id);
1205 v3s16 pos = floatToInt(player->getPosition(), BS);
1208 core::map<u16, bool> removed_objects;
1209 core::map<u16, bool> added_objects;
1210 m_env.getRemovedActiveObjects(pos, radius,
1211 client->m_known_objects, removed_objects);
1212 m_env.getAddedActiveObjects(pos, radius,
1213 client->m_known_objects, added_objects);
1215 // Ignore if nothing happened
1216 if(removed_objects.size() == 0 && added_objects.size() == 0)
1219 std::string data_buffer;
1223 // Handle removed objects
1224 writeU16((u8*)buf, removed_objects.size());
1225 data_buffer.append(buf, 2);
1226 for(core::map<u16, bool>::Iterator
1227 i = removed_objects.getIterator();
1228 i.atEnd()==false; i++)
1231 u16 id = i.getNode()->getKey();
1232 ServerActiveObject* obj = m_env.getActiveObject(id);
1234 // Add to data buffer for sending
1235 writeU16((u8*)buf, i.getNode()->getKey());
1236 data_buffer.append(buf, 2);
1238 // Remove from known objects
1239 client->m_known_objects.remove(i.getNode()->getKey());
1241 if(obj && obj->m_known_by_count > 0)
1242 obj->m_known_by_count--;
1245 // Handle added objects
1246 writeU16((u8*)buf, added_objects.size());
1247 data_buffer.append(buf, 2);
1248 for(core::map<u16, bool>::Iterator
1249 i = added_objects.getIterator();
1250 i.atEnd()==false; i++)
1253 u16 id = i.getNode()->getKey();
1254 ServerActiveObject* obj = m_env.getActiveObject(id);
1257 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1259 dstream<<"WARNING: "<<__FUNCTION_NAME
1260 <<": NULL object"<<std::endl;
1262 type = obj->getType();
1264 // Add to data buffer for sending
1265 writeU16((u8*)buf, id);
1266 data_buffer.append(buf, 2);
1267 writeU8((u8*)buf, type);
1268 data_buffer.append(buf, 1);
1270 // Add to known objects
1271 client->m_known_objects.insert(i.getNode()->getKey(), false);
1274 obj->m_known_by_count++;
1278 SharedBuffer<u8> reply(2 + data_buffer.size());
1279 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1280 memcpy((char*)&reply[2], data_buffer.c_str(),
1281 data_buffer.size());
1283 m_con.Send(client->peer_id, 0, reply, true);
1285 dstream<<"INFO: Server: Sent object remove/add: "
1286 <<removed_objects.size()<<" removed, "
1287 <<added_objects.size()<<" added, "
1288 <<"packet size is "<<reply.getSize()<<std::endl;
1293 Send object messages
1296 JMutexAutoLock envlock(m_env_mutex);
1297 JMutexAutoLock conlock(m_con_mutex);
1300 // Value = data sent by object
1301 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1303 // Get active object messages from environment
1306 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1310 core::list<ActiveObjectMessage>* message_list = NULL;
1311 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1312 n = buffered_messages.find(aom.id);
1315 message_list = new core::list<ActiveObjectMessage>;
1316 buffered_messages.insert(aom.id, message_list);
1320 message_list = n->getValue();
1322 message_list->push_back(aom);
1325 // Route data to every client
1326 for(core::map<u16, RemoteClient*>::Iterator
1327 i = m_clients.getIterator();
1328 i.atEnd()==false; i++)
1330 RemoteClient *client = i.getNode()->getValue();
1331 std::string reliable_data;
1332 std::string unreliable_data;
1333 // Go through all objects in message buffer
1334 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1335 j = buffered_messages.getIterator();
1336 j.atEnd()==false; j++)
1338 // If object is not known by client, skip it
1339 u16 id = j.getNode()->getKey();
1340 if(client->m_known_objects.find(id) == NULL)
1342 // Get message list of object
1343 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1344 // Go through every message
1345 for(core::list<ActiveObjectMessage>::Iterator
1346 k = list->begin(); k != list->end(); k++)
1348 // Compose the full new data with header
1349 ActiveObjectMessage aom = *k;
1350 std::string new_data;
1351 // Add header (object id + length)
1353 writeU16((u8*)&header[0], aom.id);
1354 writeU16((u8*)&header[2], aom.datastring.size());
1355 new_data.append(header, 4);
1357 new_data += aom.datastring;
1358 // Add data to buffer
1360 reliable_data += new_data;
1362 unreliable_data += new_data;
1366 reliable_data and unreliable_data are now ready.
1369 if(reliable_data.size() > 0)
1371 SharedBuffer<u8> reply(2 + reliable_data.size());
1372 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1373 memcpy((char*)&reply[2], reliable_data.c_str(),
1374 reliable_data.size());
1376 m_con.Send(client->peer_id, 0, reply, true);
1378 if(unreliable_data.size() > 0)
1380 SharedBuffer<u8> reply(2 + unreliable_data.size());
1381 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1382 memcpy((char*)&reply[2], unreliable_data.c_str(),
1383 unreliable_data.size());
1384 // Send as unreliable
1385 m_con.Send(client->peer_id, 0, reply, false);
1387 if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1389 dstream<<"INFO: Server: Size of object message data: "
1390 <<"reliable: "<<reliable_data.size()
1391 <<", unreliable: "<<unreliable_data.size()
1396 // Clear buffered_messages
1397 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1398 i = buffered_messages.getIterator();
1399 i.atEnd()==false; i++)
1401 delete i.getNode()->getValue();
1406 Send object positions
1409 float &counter = m_objectdata_timer;
1411 if(counter >= g_settings.getFloat("objectdata_interval"))
1413 JMutexAutoLock lock1(m_env_mutex);
1414 JMutexAutoLock lock2(m_con_mutex);
1415 SendObjectData(counter);
1422 Trigger emergethread (it somehow gets to a non-triggered but
1423 bysy state sometimes)
1426 float &counter = m_emergethread_trigger_timer;
1432 m_emergethread.trigger();
1438 float &counter = m_savemap_timer;
1440 if(counter >= g_settings.getFloat("server_map_save_interval"))
1444 JMutexAutoLock lock(m_env_mutex);
1446 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1448 // Save only changed parts
1449 m_env.getMap().save(true);
1451 // Delete unused sectors
1452 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1453 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1454 if(deleted_count > 0)
1456 dout_server<<"Server: Unloaded "<<deleted_count
1457 <<" sectors from memory"<<std::endl;
1461 m_env.serializePlayers(m_mapsavedir);
1467 void Server::Receive()
1469 DSTACK(__FUNCTION_NAME);
1470 u32 data_maxsize = 10000;
1471 Buffer<u8> data(data_maxsize);
1476 JMutexAutoLock conlock(m_con_mutex);
1477 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1480 // This has to be called so that the client list gets synced
1481 // with the peer list of the connection
1482 handlePeerChanges();
1484 ProcessData(*data, datasize, peer_id);
1486 catch(con::InvalidIncomingDataException &e)
1488 derr_server<<"Server::Receive(): "
1489 "InvalidIncomingDataException: what()="
1490 <<e.what()<<std::endl;
1492 catch(con::PeerNotFoundException &e)
1494 //NOTE: This is not needed anymore
1496 // The peer has been disconnected.
1497 // Find the associated player and remove it.
1499 /*JMutexAutoLock envlock(m_env_mutex);
1501 dout_server<<"ServerThread: peer_id="<<peer_id
1502 <<" has apparently closed connection. "
1503 <<"Removing player."<<std::endl;
1505 m_env.removePlayer(peer_id);*/
1509 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1511 DSTACK(__FUNCTION_NAME);
1512 // Environment is locked first.
1513 JMutexAutoLock envlock(m_env_mutex);
1514 JMutexAutoLock conlock(m_con_mutex);
1518 peer = m_con.GetPeer(peer_id);
1520 catch(con::PeerNotFoundException &e)
1522 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1523 <<peer_id<<" not found"<<std::endl;
1527 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1535 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1537 if(command == TOSERVER_INIT)
1539 // [0] u16 TOSERVER_INIT
1540 // [2] u8 SER_FMT_VER_HIGHEST
1541 // [3] u8[20] player_name
1546 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1547 <<peer->id<<std::endl;
1549 // First byte after command is maximum supported
1550 // serialization version
1551 u8 client_max = data[2];
1552 u8 our_max = SER_FMT_VER_HIGHEST;
1553 // Use the highest version supported by both
1554 u8 deployed = core::min_(client_max, our_max);
1555 // If it's lower than the lowest supported, give up.
1556 if(deployed < SER_FMT_VER_LOWEST)
1557 deployed = SER_FMT_VER_INVALID;
1559 //peer->serialization_version = deployed;
1560 getClient(peer->id)->pending_serialization_version = deployed;
1562 if(deployed == SER_FMT_VER_INVALID)
1564 derr_server<<DTIME<<"Server: Cannot negotiate "
1565 "serialization version with peer "
1566 <<peer_id<<std::endl;
1575 const u32 playername_size = 20;
1576 char playername[playername_size];
1577 for(u32 i=0; i<playername_size-1; i++)
1579 playername[i] = data[3+i];
1581 playername[playername_size-1] = 0;
1584 Player *player = emergePlayer(playername, "", peer_id);
1585 //Player *player = m_env.getPlayer(peer_id);
1588 // DEBUG: Test serialization
1589 std::ostringstream test_os;
1590 player->serialize(test_os);
1591 dstream<<"Player serialization test: \""<<test_os.str()
1593 std::istringstream test_is(test_os.str());
1594 player->deSerialize(test_is);
1597 // If failed, cancel
1600 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1601 <<": failed to emerge player"<<std::endl;
1606 // If a client is already connected to the player, cancel
1607 if(player->peer_id != 0)
1609 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1610 <<" tried to connect to "
1611 "an already connected player (peer_id="
1612 <<player->peer_id<<")"<<std::endl;
1615 // Set client of player
1616 player->peer_id = peer_id;
1619 // Check if player doesn't exist
1621 throw con::InvalidIncomingDataException
1622 ("Server::ProcessData(): INIT: Player doesn't exist");
1624 /*// update name if it was supplied
1625 if(datasize >= 20+3)
1628 player->updateName((const char*)&data[3]);
1631 // Now answer with a TOCLIENT_INIT
1633 SharedBuffer<u8> reply(2+1+6);
1634 writeU16(&reply[0], TOCLIENT_INIT);
1635 writeU8(&reply[2], deployed);
1636 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1638 m_con.Send(peer_id, 0, reply, true);
1642 if(command == TOSERVER_INIT2)
1644 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1645 <<peer->id<<std::endl;
1648 getClient(peer->id)->serialization_version
1649 = getClient(peer->id)->pending_serialization_version;
1652 Send some initialization data
1655 // Send player info to all players
1658 // Send inventory to player
1659 SendInventory(peer->id);
1663 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1664 m_time_of_day.get());
1665 m_con.Send(peer->id, 0, data, true);
1668 // Send information about server to player in chat
1669 SendChatMessage(peer_id, getStatusString());
1671 // Send information about joining in chat
1673 std::wstring name = L"unknown";
1674 Player *player = m_env.getPlayer(peer_id);
1676 name = narrow_to_wide(player->getName());
1678 std::wstring message;
1681 message += L" joined game";
1682 BroadcastChatMessage(message);
1688 if(peer_ser_ver == SER_FMT_VER_INVALID)
1690 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1691 " serialization format invalid or not initialized."
1692 " Skipping incoming command="<<command<<std::endl;
1696 Player *player = m_env.getPlayer(peer_id);
1699 derr_server<<"Server::ProcessData(): Cancelling: "
1700 "No player for peer_id="<<peer_id
1704 if(command == TOSERVER_PLAYERPOS)
1706 if(datasize < 2+12+12+4+4)
1710 v3s32 ps = readV3S32(&data[start+2]);
1711 v3s32 ss = readV3S32(&data[start+2+12]);
1712 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1713 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1714 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1715 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1716 pitch = wrapDegrees(pitch);
1717 yaw = wrapDegrees(yaw);
1718 player->setPosition(position);
1719 player->setSpeed(speed);
1720 player->setPitch(pitch);
1721 player->setYaw(yaw);
1723 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1724 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1725 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1727 else if(command == TOSERVER_GOTBLOCKS)
1740 u16 count = data[2];
1741 for(u16 i=0; i<count; i++)
1743 if((s16)datasize < 2+1+(i+1)*6)
1744 throw con::InvalidIncomingDataException
1745 ("GOTBLOCKS length is too short");
1746 v3s16 p = readV3S16(&data[2+1+i*6]);
1747 /*dstream<<"Server: GOTBLOCKS ("
1748 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1749 RemoteClient *client = getClient(peer_id);
1750 client->GotBlock(p);
1753 else if(command == TOSERVER_DELETEDBLOCKS)
1766 u16 count = data[2];
1767 for(u16 i=0; i<count; i++)
1769 if((s16)datasize < 2+1+(i+1)*6)
1770 throw con::InvalidIncomingDataException
1771 ("DELETEDBLOCKS length is too short");
1772 v3s16 p = readV3S16(&data[2+1+i*6]);
1773 /*dstream<<"Server: DELETEDBLOCKS ("
1774 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1775 RemoteClient *client = getClient(peer_id);
1776 client->SetBlockNotSent(p);
1779 else if(command == TOSERVER_CLICK_OBJECT)
1786 [2] u8 button (0=left, 1=right)
1791 u8 button = readU8(&data[2]);
1793 p.X = readS16(&data[3]);
1794 p.Y = readS16(&data[5]);
1795 p.Z = readS16(&data[7]);
1796 s16 id = readS16(&data[9]);
1797 //u16 item_i = readU16(&data[11]);
1799 MapBlock *block = NULL;
1802 block = m_env.getMap().getBlockNoCreate(p);
1804 catch(InvalidPositionException &e)
1806 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1810 MapBlockObject *obj = block->getObject(id);
1814 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1818 //TODO: Check that object is reasonably close
1823 InventoryList *ilist = player->inventory.getList("main");
1824 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1827 // Skip if inventory has no free space
1828 if(ilist->getUsedSlots() == ilist->getSize())
1830 dout_server<<"Player inventory has no free space"<<std::endl;
1835 Create the inventory item
1837 InventoryItem *item = NULL;
1838 // If it is an item-object, take the item from it
1839 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1841 item = ((ItemObject*)obj)->createInventoryItem();
1843 // Else create an item of the object
1846 item = new MapBlockObjectItem
1847 (obj->getInventoryString());
1850 // Add to inventory and send inventory
1851 ilist->addItem(item);
1852 SendInventory(player->peer_id);
1855 // Remove from block
1856 block->removeObject(id);
1859 else if(command == TOSERVER_GROUND_ACTION)
1867 [3] v3s16 nodepos_undersurface
1868 [9] v3s16 nodepos_abovesurface
1873 2: stop digging (all parameters ignored)
1874 3: digging completed
1876 u8 action = readU8(&data[2]);
1878 p_under.X = readS16(&data[3]);
1879 p_under.Y = readS16(&data[5]);
1880 p_under.Z = readS16(&data[7]);
1882 p_over.X = readS16(&data[9]);
1883 p_over.Y = readS16(&data[11]);
1884 p_over.Z = readS16(&data[13]);
1885 u16 item_i = readU16(&data[15]);
1887 //TODO: Check that target is reasonably close
1895 NOTE: This can be used in the future to check if
1896 somebody is cheating, by checking the timing.
1903 else if(action == 2)
1906 RemoteClient *client = getClient(peer->id);
1907 JMutexAutoLock digmutex(client->m_dig_mutex);
1908 client->m_dig_tool_item = -1;
1913 3: Digging completed
1915 else if(action == 3)
1917 // Mandatory parameter; actually used for nothing
1918 core::map<v3s16, MapBlock*> modified_blocks;
1921 u8 mineral = MINERAL_NONE;
1925 MapNode n = m_env.getMap().getNode(p_under);
1926 // Get material at position
1928 // If it's not diggable, do nothing
1929 if(content_diggable(material) == false)
1931 derr_server<<"Server: Not finishing digging: Node not diggable"
1934 // Client probably has wrong data.
1935 // Set block not sent, so that client will get
1937 dstream<<"Client "<<peer_id<<" tried to dig "
1938 <<"node from invalid position; setting"
1939 <<" MapBlock not sent."<<std::endl;
1940 RemoteClient *client = getClient(peer_id);
1941 v3s16 blockpos = getNodeBlockPos(p_under);
1942 client->SetBlockNotSent(blockpos);
1947 mineral = n.getMineral();
1949 catch(InvalidPositionException &e)
1951 derr_server<<"Server: Not finishing digging: Node not found."
1952 <<" Adding block to emerge queue."
1954 m_emerge_queue.addBlock(peer_id,
1955 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1960 Send the removal to all other clients
1965 SharedBuffer<u8> reply(replysize);
1966 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1967 writeS16(&reply[2], p_under.X);
1968 writeS16(&reply[4], p_under.Y);
1969 writeS16(&reply[6], p_under.Z);
1971 for(core::map<u16, RemoteClient*>::Iterator
1972 i = m_clients.getIterator();
1973 i.atEnd() == false; i++)
1975 // Get client and check that it is valid
1976 RemoteClient *client = i.getNode()->getValue();
1977 assert(client->peer_id == i.getNode()->getKey());
1978 if(client->serialization_version == SER_FMT_VER_INVALID)
1981 // Don't send if it's the same one
1982 if(peer_id == client->peer_id)
1986 m_con.Send(client->peer_id, 0, reply, true);
1990 Update and send inventory
1993 if(g_settings.getBool("creative_mode") == false)
1998 InventoryList *mlist = player->inventory.getList("main");
2001 InventoryItem *item = mlist->getItem(item_i);
2002 if(item && (std::string)item->getName() == "ToolItem")
2004 ToolItem *titem = (ToolItem*)item;
2005 std::string toolname = titem->getToolName();
2007 // Get digging properties for material and tool
2008 DiggingProperties prop =
2009 getDiggingProperties(material, toolname);
2011 if(prop.diggable == false)
2013 derr_server<<"Server: WARNING: Player digged"
2014 <<" with impossible material + tool"
2015 <<" combination"<<std::endl;
2018 bool weared_out = titem->addWear(prop.wear);
2022 mlist->deleteItem(item_i);
2028 Add dug item to inventory
2031 InventoryItem *item = NULL;
2033 if(mineral != MINERAL_NONE)
2034 item = getDiggedMineralItem(mineral);
2039 std::string &dug_s = content_features(material).dug_item;
2042 std::istringstream is(dug_s, std::ios::binary);
2043 item = InventoryItem::deSerialize(is);
2049 // Add a item to inventory
2050 player->inventory.addItem("main", item);
2053 SendInventory(player->peer_id);
2059 (this takes some time so it is done after the quick stuff)
2061 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2068 // Update water pressure around modification
2069 // This also adds it to m_flow_active_nodes if appropriate
2071 MapVoxelManipulator v(&m_env.getMap());
2072 v.m_disable_water_climb =
2073 g_settings.getBool("disable_water_climb");
2075 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2079 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2081 catch(ProcessingLimitException &e)
2083 dstream<<"Processing limit reached (1)"<<std::endl;
2086 v.blitBack(modified_blocks);
2093 else if(action == 1)
2096 InventoryList *ilist = player->inventory.getList("main");
2101 InventoryItem *item = ilist->getItem(item_i);
2103 // If there is no item, it is not possible to add it anywhere
2108 Handle material items
2110 if(std::string("MaterialItem") == item->getName())
2113 // Don't add a node if this is not a free space
2114 MapNode n2 = m_env.getMap().getNode(p_over);
2115 if(content_buildable_to(n2.d) == false)
2117 // Client probably has wrong data.
2118 // Set block not sent, so that client will get
2120 dstream<<"Client "<<peer_id<<" tried to place"
2121 <<" node in invalid position; setting"
2122 <<" MapBlock not sent."<<std::endl;
2123 RemoteClient *client = getClient(peer_id);
2124 v3s16 blockpos = getNodeBlockPos(p_over);
2125 client->SetBlockNotSent(blockpos);
2129 catch(InvalidPositionException &e)
2131 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2132 <<" Adding block to emerge queue."
2134 m_emerge_queue.addBlock(peer_id,
2135 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2139 // Reset build time counter
2140 getClient(peer->id)->m_time_from_building = 0.0;
2143 MaterialItem *mitem = (MaterialItem*)item;
2145 n.d = mitem->getMaterial();
2146 if(content_features(n.d).wall_mounted)
2147 n.dir = packDir(p_under - p_over);
2150 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2151 SharedBuffer<u8> reply(replysize);
2152 writeU16(&reply[0], TOCLIENT_ADDNODE);
2153 writeS16(&reply[2], p_over.X);
2154 writeS16(&reply[4], p_over.Y);
2155 writeS16(&reply[6], p_over.Z);
2156 n.serialize(&reply[8], peer_ser_ver);
2158 m_con.SendToAll(0, reply, true);
2163 InventoryList *ilist = player->inventory.getList("main");
2164 if(g_settings.getBool("creative_mode") == false && ilist)
2166 // Remove from inventory and send inventory
2167 if(mitem->getCount() == 1)
2168 ilist->deleteItem(item_i);
2172 SendInventory(peer_id);
2178 This takes some time so it is done after the quick stuff
2180 core::map<v3s16, MapBlock*> modified_blocks;
2181 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2184 Calculate special events
2187 /*if(n.d == CONTENT_MESE)
2190 for(s16 z=-1; z<=1; z++)
2191 for(s16 y=-1; y<=1; y++)
2192 for(s16 x=-1; x<=1; x++)
2203 v3s16 blockpos = getNodeBlockPos(p_over);
2205 MapBlock *block = NULL;
2208 block = m_env.getMap().getBlockNoCreate(blockpos);
2210 catch(InvalidPositionException &e)
2212 derr_server<<"Error while placing object: "
2213 "block not found"<<std::endl;
2217 v3s16 block_pos_i_on_map = block->getPosRelative();
2218 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2220 v3f pos = intToFloat(p_over, BS);
2221 pos -= block_pos_f_on_map;
2223 /*dout_server<<"pos="
2224 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2227 MapBlockObject *obj = NULL;
2230 Handle block object items
2232 if(std::string("MBOItem") == item->getName())
2234 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2236 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2237 "inventorystring=\""
2238 <<oitem->getInventoryString()
2239 <<"\""<<std::endl;*/
2241 obj = oitem->createObject
2242 (pos, player->getYaw(), player->getPitch());
2249 dout_server<<"Placing a miscellaneous item on map"
2252 Create an ItemObject that contains the item.
2254 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2255 std::ostringstream os(std::ios_base::binary);
2256 item->serialize(os);
2257 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2258 iobj->setItemString(os.str());
2264 derr_server<<"WARNING: item resulted in NULL object, "
2265 <<"not placing onto map"
2270 block->addObject(obj);
2272 dout_server<<"Placed object"<<std::endl;
2274 InventoryList *ilist = player->inventory.getList("main");
2275 if(g_settings.getBool("creative_mode") == false && ilist)
2277 // Remove from inventory and send inventory
2278 ilist->deleteItem(item_i);
2280 SendInventory(peer_id);
2288 Catch invalid actions
2292 derr_server<<"WARNING: Server: Invalid action "
2293 <<action<<std::endl;
2297 else if(command == TOSERVER_RELEASE)
2306 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2309 else if(command == TOSERVER_SIGNTEXT)
2318 std::string datastring((char*)&data[2], datasize-2);
2319 std::istringstream is(datastring, std::ios_base::binary);
2322 is.read((char*)buf, 6);
2323 v3s16 blockpos = readV3S16(buf);
2324 is.read((char*)buf, 2);
2325 s16 id = readS16(buf);
2326 is.read((char*)buf, 2);
2327 u16 textlen = readU16(buf);
2329 for(u16 i=0; i<textlen; i++)
2331 is.read((char*)buf, 1);
2332 text += (char)buf[0];
2335 MapBlock *block = NULL;
2338 block = m_env.getMap().getBlockNoCreate(blockpos);
2340 catch(InvalidPositionException &e)
2342 derr_server<<"Error while setting sign text: "
2343 "block not found"<<std::endl;
2347 MapBlockObject *obj = block->getObject(id);
2350 derr_server<<"Error while setting sign text: "
2351 "object not found"<<std::endl;
2355 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2357 derr_server<<"Error while setting sign text: "
2358 "object is not a sign"<<std::endl;
2362 ((SignObject*)obj)->setText(text);
2364 obj->getBlock()->setChangedFlag();
2366 else if(command == TOSERVER_INVENTORY_ACTION)
2368 /*// Ignore inventory changes if in creative mode
2369 if(g_settings.getBool("creative_mode") == true)
2371 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2375 // Strip command and create a stream
2376 std::string datastring((char*)&data[2], datasize-2);
2377 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2378 std::istringstream is(datastring, std::ios_base::binary);
2380 InventoryAction *a = InventoryAction::deSerialize(is);
2384 Handle craftresult specially if not in creative mode
2386 bool disable_action = false;
2387 if(a->getType() == IACTION_MOVE
2388 && g_settings.getBool("creative_mode") == false)
2390 IMoveAction *ma = (IMoveAction*)a;
2391 // Don't allow moving anything to craftresult
2392 if(ma->to_name == "craftresult")
2395 disable_action = true;
2397 // When something is removed from craftresult
2398 if(ma->from_name == "craftresult")
2400 disable_action = true;
2401 // Remove stuff from craft
2402 InventoryList *clist = player->inventory.getList("craft");
2405 u16 count = ma->count;
2408 clist->decrementMaterials(count);
2411 // Feed action to player inventory
2412 a->apply(&player->inventory);
2415 // If something appeared in craftresult, throw it
2417 InventoryList *rlist = player->inventory.getList("craftresult");
2418 InventoryList *mlist = player->inventory.getList("main");
2419 if(rlist && mlist && rlist->getUsedSlots() == 1)
2421 InventoryItem *item1 = rlist->changeItem(0, NULL);
2422 mlist->addItem(item1);
2426 if(disable_action == false)
2428 // Feed action to player inventory
2429 a->apply(&player->inventory);
2434 SendInventory(player->peer_id);
2438 dstream<<"TOSERVER_INVENTORY_ACTION: "
2439 <<"InventoryAction::deSerialize() returned NULL"
2443 else if(command == TOSERVER_CHAT_MESSAGE)
2451 std::string datastring((char*)&data[2], datasize-2);
2452 std::istringstream is(datastring, std::ios_base::binary);
2455 is.read((char*)buf, 2);
2456 u16 len = readU16(buf);
2458 std::wstring message;
2459 for(u16 i=0; i<len; i++)
2461 is.read((char*)buf, 2);
2462 message += (wchar_t)readU16(buf);
2465 // Get player name of this client
2466 std::wstring name = narrow_to_wide(player->getName());
2468 // Line to send to players
2470 // Whether to send to the player that sent the line
2471 bool send_to_sender = false;
2472 // Whether to send to other players
2473 bool send_to_others = false;
2476 std::wstring commandprefix = L"/#";
2477 if(message.substr(0, commandprefix.size()) == commandprefix)
2479 line += L"Server: ";
2481 message = message.substr(commandprefix.size());
2482 // Get player name as narrow string
2483 std::string name_s = player->getName();
2484 // Convert message to narrow string
2485 std::string message_s = wide_to_narrow(message);
2486 // Operator is the single name defined in config.
2487 std::string operator_name = g_settings.get("name");
2488 bool is_operator = (operator_name != "" &&
2489 wide_to_narrow(name) == operator_name);
2490 bool valid_command = false;
2491 if(message_s == "help")
2493 line += L"-!- Available commands: ";
2497 line += L"shutdown setting ";
2502 send_to_sender = true;
2503 valid_command = true;
2505 else if(message_s == "status")
2507 line = getStatusString();
2508 send_to_sender = true;
2509 valid_command = true;
2511 else if(is_operator)
2513 if(message_s == "shutdown")
2515 dstream<<DTIME<<" Server: Operator requested shutdown."
2517 m_shutdown_requested.set(true);
2519 line += L"*** Server shutting down (operator request)";
2520 send_to_sender = true;
2521 valid_command = true;
2523 else if(message_s.substr(0,8) == "setting ")
2525 std::string confline = message_s.substr(8);
2526 g_settings.parseConfigLine(confline);
2527 line += L"-!- Setting changed.";
2528 send_to_sender = true;
2529 valid_command = true;
2533 if(valid_command == false)
2535 line += L"-!- Invalid command: " + message;
2536 send_to_sender = true;
2547 send_to_others = true;
2552 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2555 Send the message to clients
2557 for(core::map<u16, RemoteClient*>::Iterator
2558 i = m_clients.getIterator();
2559 i.atEnd() == false; i++)
2561 // Get client and check that it is valid
2562 RemoteClient *client = i.getNode()->getValue();
2563 assert(client->peer_id == i.getNode()->getKey());
2564 if(client->serialization_version == SER_FMT_VER_INVALID)
2568 bool sender_selected = (peer_id == client->peer_id);
2569 if(sender_selected == true && send_to_sender == false)
2571 if(sender_selected == false && send_to_others == false)
2574 SendChatMessage(client->peer_id, line);
2580 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2581 "unknown command "<<command<<std::endl;
2585 catch(SendFailedException &e)
2587 derr_server<<"Server::ProcessData(): SendFailedException: "
2593 /*void Server::Send(u16 peer_id, u16 channelnum,
2594 SharedBuffer<u8> data, bool reliable)
2596 JMutexAutoLock lock(m_con_mutex);
2597 m_con.Send(peer_id, channelnum, data, reliable);
2600 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2602 DSTACK(__FUNCTION_NAME);
2604 Create a packet with the block in the right format
2607 std::ostringstream os(std::ios_base::binary);
2608 block->serialize(os, ver);
2609 std::string s = os.str();
2610 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2612 u32 replysize = 8 + blockdata.getSize();
2613 SharedBuffer<u8> reply(replysize);
2614 v3s16 p = block->getPos();
2615 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2616 writeS16(&reply[2], p.X);
2617 writeS16(&reply[4], p.Y);
2618 writeS16(&reply[6], p.Z);
2619 memcpy(&reply[8], *blockdata, blockdata.getSize());
2621 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2622 <<": \tpacket size: "<<replysize<<std::endl;*/
2627 m_con.Send(peer_id, 1, reply, true);
2630 core::list<PlayerInfo> Server::getPlayerInfo()
2632 DSTACK(__FUNCTION_NAME);
2633 JMutexAutoLock envlock(m_env_mutex);
2634 JMutexAutoLock conlock(m_con_mutex);
2636 core::list<PlayerInfo> list;
2638 core::list<Player*> players = m_env.getPlayers();
2640 core::list<Player*>::Iterator i;
2641 for(i = players.begin();
2642 i != players.end(); i++)
2646 Player *player = *i;
2649 con::Peer *peer = m_con.GetPeer(player->peer_id);
2650 // Copy info from peer to info struct
2652 info.address = peer->address;
2653 info.avg_rtt = peer->avg_rtt;
2655 catch(con::PeerNotFoundException &e)
2657 // Set dummy peer info
2659 info.address = Address(0,0,0,0,0);
2663 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2664 info.position = player->getPosition();
2666 list.push_back(info);
2673 void Server::peerAdded(con::Peer *peer)
2675 DSTACK(__FUNCTION_NAME);
2676 dout_server<<"Server::peerAdded(): peer->id="
2677 <<peer->id<<std::endl;
2680 c.type = PEER_ADDED;
2681 c.peer_id = peer->id;
2683 m_peer_change_queue.push_back(c);
2686 void Server::deletingPeer(con::Peer *peer, bool timeout)
2688 DSTACK(__FUNCTION_NAME);
2689 dout_server<<"Server::deletingPeer(): peer->id="
2690 <<peer->id<<", timeout="<<timeout<<std::endl;
2693 c.type = PEER_REMOVED;
2694 c.peer_id = peer->id;
2695 c.timeout = timeout;
2696 m_peer_change_queue.push_back(c);
2699 void Server::SendObjectData(float dtime)
2701 DSTACK(__FUNCTION_NAME);
2703 core::map<v3s16, bool> stepped_blocks;
2705 for(core::map<u16, RemoteClient*>::Iterator
2706 i = m_clients.getIterator();
2707 i.atEnd() == false; i++)
2709 u16 peer_id = i.getNode()->getKey();
2710 RemoteClient *client = i.getNode()->getValue();
2711 assert(client->peer_id == peer_id);
2713 if(client->serialization_version == SER_FMT_VER_INVALID)
2716 client->SendObjectData(this, dtime, stepped_blocks);
2720 void Server::SendPlayerInfos()
2722 DSTACK(__FUNCTION_NAME);
2724 //JMutexAutoLock envlock(m_env_mutex);
2726 // Get connected players
2727 core::list<Player*> players = m_env.getPlayers(true);
2729 u32 player_count = players.getSize();
2730 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2732 SharedBuffer<u8> data(datasize);
2733 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2736 core::list<Player*>::Iterator i;
2737 for(i = players.begin();
2738 i != players.end(); i++)
2740 Player *player = *i;
2742 /*dstream<<"Server sending player info for player with "
2743 "peer_id="<<player->peer_id<<std::endl;*/
2745 writeU16(&data[start], player->peer_id);
2746 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2747 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2748 start += 2+PLAYERNAME_SIZE;
2751 //JMutexAutoLock conlock(m_con_mutex);
2754 m_con.SendToAll(0, data, true);
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::SendBlocks(float dtime)
3109 DSTACK(__FUNCTION_NAME);
3111 JMutexAutoLock envlock(m_env_mutex);
3112 JMutexAutoLock conlock(m_con_mutex);
3114 //TimeTaker timer("Server::SendBlocks");
3116 core::array<PrioritySortedBlockTransfer> queue;
3118 s32 total_sending = 0;
3120 for(core::map<u16, RemoteClient*>::Iterator
3121 i = m_clients.getIterator();
3122 i.atEnd() == false; i++)
3124 RemoteClient *client = i.getNode()->getValue();
3125 assert(client->peer_id == i.getNode()->getKey());
3127 total_sending += client->SendingCount();
3129 if(client->serialization_version == SER_FMT_VER_INVALID)
3132 client->GetNextBlocks(this, dtime, queue);
3136 // Lowest priority number comes first.
3137 // Lowest is most important.
3140 for(u32 i=0; i<queue.size(); i++)
3142 //TODO: Calculate limit dynamically
3143 if(total_sending >= g_settings.getS32
3144 ("max_simultaneous_block_sends_server_total"))
3147 PrioritySortedBlockTransfer q = queue[i];
3149 MapBlock *block = NULL;
3152 block = m_env.getMap().getBlockNoCreate(q.pos);
3154 catch(InvalidPositionException &e)
3159 RemoteClient *client = getClient(q.peer_id);
3161 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3163 client->SentBlock(q.pos);
3170 RemoteClient* Server::getClient(u16 peer_id)
3172 DSTACK(__FUNCTION_NAME);
3173 //JMutexAutoLock lock(m_con_mutex);
3174 core::map<u16, RemoteClient*>::Node *n;
3175 n = m_clients.find(peer_id);
3176 // A client should exist for all peers
3178 return n->getValue();
3181 std::wstring Server::getStatusString()
3183 std::wostringstream os(std::ios_base::binary);
3186 os<<L"uptime="<<m_uptime.get();
3187 // Information about clients
3189 for(core::map<u16, RemoteClient*>::Iterator
3190 i = m_clients.getIterator();
3191 i.atEnd() == false; i++)
3193 // Get client and check that it is valid
3194 RemoteClient *client = i.getNode()->getValue();
3195 assert(client->peer_id == i.getNode()->getKey());
3196 if(client->serialization_version == SER_FMT_VER_INVALID)
3199 Player *player = m_env.getPlayer(client->peer_id);
3200 // Get name of player
3201 std::wstring name = L"unknown";
3203 name = narrow_to_wide(player->getName());
3204 // Add name to information string
3208 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3209 os<<" WARNING: Map saving is disabled."<<std::endl;
3214 void setCreativeInventory(Player *player)
3216 player->resetInventory();
3218 // Give some good picks
3220 InventoryItem *item = new ToolItem("STPick", 0);
3221 void* r = player->inventory.addItem("main", item);
3225 InventoryItem *item = new ToolItem("MesePick", 0);
3226 void* r = player->inventory.addItem("main", item);
3233 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3236 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3237 player->inventory.addItem("main", item);
3240 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3242 // Skip some materials
3243 if(i == CONTENT_WATER || i == CONTENT_TORCH
3244 || i == CONTENT_COALSTONE)
3247 InventoryItem *item = new MaterialItem(i, 1);
3248 player->inventory.addItem("main", item);
3252 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3253 void* r = player->inventory.addItem("main", item);
3258 Player *Server::emergePlayer(const char *name, const char *password,
3262 Try to get an existing player
3264 Player *player = m_env.getPlayer(name);
3267 // If player is already connected, cancel
3268 if(player->peer_id != 0)
3270 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3275 player->peer_id = peer_id;
3277 // Reset inventory to creative if in creative mode
3278 if(g_settings.getBool("creative_mode"))
3280 setCreativeInventory(player);
3287 If player with the wanted peer_id already exists, cancel.
3289 if(m_env.getPlayer(peer_id) != NULL)
3291 dstream<<"emergePlayer(): Player with wrong name but same"
3292 " peer_id already exists"<<std::endl;
3300 player = new ServerRemotePlayer();
3301 //player->peer_id = c.peer_id;
3302 //player->peer_id = PEER_ID_INEXISTENT;
3303 player->peer_id = peer_id;
3304 player->updateName(name);
3310 dstream<<"Server: Finding spawn place for player \""
3311 <<player->getName()<<"\""<<std::endl;
3315 player->setPosition(intToFloat(v3s16(
3322 f32 groundheight = 0;
3324 // Try to find a good place a few times
3325 for(s32 i=0; i<500; i++)
3328 // We're going to try to throw the player to this position
3329 nodepos = v2s16(-range + (myrand()%(range*2)),
3330 -range + (myrand()%(range*2)));
3331 v2s16 sectorpos = getNodeSectorPos(nodepos);
3333 m_env.getMap().emergeSector(sectorpos);
3334 // Get ground height at point
3335 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3336 // The sector should have been generated -> groundheight exists
3337 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3338 // Don't go underwater
3339 if(groundheight < WATER_LEVEL)
3341 //dstream<<"-> Underwater"<<std::endl;
3344 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3345 // Get block at point
3347 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3348 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3349 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3350 // Don't go inside ground
3352 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3353 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3354 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3355 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3356 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3357 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3359 dstream<<"-> Inside ground"<<std::endl;
3363 }catch(InvalidPositionException &e)
3365 dstream<<"-> Invalid position"<<std::endl;
3366 // Ignore invalid position
3370 // Found a good place
3371 dstream<<"Searched through "<<i<<" places."<<std::endl;
3376 // If no suitable place was not found, go above water at least.
3377 if(groundheight < WATER_LEVEL)
3378 groundheight = WATER_LEVEL;
3380 player->setPosition(intToFloat(v3s16(
3389 Add player to environment
3392 m_env.addPlayer(player);
3395 Add stuff to inventory
3398 if(g_settings.getBool("creative_mode"))
3400 setCreativeInventory(player);
3405 InventoryItem *item = new ToolItem("WPick", 32000);
3406 void* r = player->inventory.addItem("main", item);
3410 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3411 void* r = player->inventory.addItem("main", item);
3415 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3416 void* r = player->inventory.addItem("main", item);
3420 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3421 void* r = player->inventory.addItem("main", item);
3425 InventoryItem *item = new CraftItem("Stick", 4);
3426 void* r = player->inventory.addItem("main", item);
3430 InventoryItem *item = new ToolItem("WPick", 32000);
3431 void* r = player->inventory.addItem("main", item);
3435 InventoryItem *item = new ToolItem("STPick", 32000);
3436 void* r = player->inventory.addItem("main", item);
3439 /*// Give some lights
3441 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3442 bool r = player->inventory.addItem("main", item);
3446 for(u16 i=0; i<4; i++)
3448 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3449 bool r = player->inventory.addItem("main", item);
3452 /*// Give some other stuff
3454 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3455 bool r = player->inventory.addItem("main", item);
3462 } // create new player
3466 void Server::UpdateBlockWaterPressure(MapBlock *block,
3467 core::map<v3s16, MapBlock*> &modified_blocks)
3469 MapVoxelManipulator v(&m_env.getMap());
3470 v.m_disable_water_climb =
3471 g_settings.getBool("disable_water_climb");
3473 VoxelArea area(block->getPosRelative(),
3474 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3478 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3480 catch(ProcessingLimitException &e)
3482 dstream<<"Processing limit reached (1)"<<std::endl;
3485 v.blitBack(modified_blocks);
3489 void Server::handlePeerChange(PeerChange &c)
3491 JMutexAutoLock envlock(m_env_mutex);
3492 JMutexAutoLock conlock(m_con_mutex);
3494 if(c.type == PEER_ADDED)
3501 core::map<u16, RemoteClient*>::Node *n;
3502 n = m_clients.find(c.peer_id);
3503 // The client shouldn't already exist
3507 RemoteClient *client = new RemoteClient();
3508 client->peer_id = c.peer_id;
3509 m_clients.insert(client->peer_id, client);
3512 else if(c.type == PEER_REMOVED)
3519 core::map<u16, RemoteClient*>::Node *n;
3520 n = m_clients.find(c.peer_id);
3521 // The client should exist
3524 // Collect information about leaving in chat
3525 std::wstring message;
3527 std::wstring name = L"unknown";
3528 Player *player = m_env.getPlayer(c.peer_id);
3530 name = narrow_to_wide(player->getName());
3534 message += L" left game";
3536 message += L" (timed out)";
3541 m_env.removePlayer(c.peer_id);
3544 // Set player client disconnected
3546 Player *player = m_env.getPlayer(c.peer_id);
3548 player->peer_id = 0;
3552 delete m_clients[c.peer_id];
3553 m_clients.remove(c.peer_id);
3555 // Send player info to all remaining clients
3558 // Send leave chat message to all remaining clients
3559 BroadcastChatMessage(message);
3568 void Server::handlePeerChanges()
3570 while(m_peer_change_queue.size() > 0)
3572 PeerChange c = m_peer_change_queue.pop_front();
3574 dout_server<<"Server: Handling peer change: "
3575 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3578 handlePeerChange(c);
3582 void dedicated_server_loop(Server &server, bool &kill)
3584 DSTACK(__FUNCTION_NAME);
3586 std::cout<<DTIME<<std::endl;
3587 std::cout<<"========================"<<std::endl;
3588 std::cout<<"Running dedicated server"<<std::endl;
3589 std::cout<<"========================"<<std::endl;
3590 std::cout<<std::endl;
3594 // This is kind of a hack but can be done like this
3595 // because server.step() is very light
3599 if(server.getShutdownRequested() || kill)
3601 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3605 static int counter = 0;
3611 core::list<PlayerInfo> list = server.getPlayerInfo();
3612 core::list<PlayerInfo>::Iterator i;
3613 static u32 sum_old = 0;
3614 u32 sum = PIChecksum(list);
3617 std::cout<<DTIME<<"Player info:"<<std::endl;
3618 for(i=list.begin(); i!=list.end(); i++)
3620 i->PrintLine(&std::cout);