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);
287 JMutexAutoLock lock(m_blocks_sent_mutex);
288 m_nearest_unsent_reset_timer += dtime;
291 // Won't send anything if already sending
293 JMutexAutoLock lock(m_blocks_sending_mutex);
295 if(m_blocks_sending.size() >= g_settings.getU16
296 ("max_simultaneous_block_sends_per_client"))
298 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
303 Player *player = server->m_env.getPlayer(peer_id);
305 assert(player != NULL);
307 v3f playerpos = player->getPosition();
308 v3f playerspeed = player->getSpeed();
310 v3s16 center_nodepos = floatToInt(playerpos);
312 v3s16 center = getNodeBlockPos(center_nodepos);
314 // Camera position and direction
316 playerpos + v3f(0, BS+BS/2, 0);
317 v3f camera_dir = v3f(0,0,1);
318 camera_dir.rotateYZBy(player->getPitch());
319 camera_dir.rotateXZBy(player->getYaw());
322 Get the starting value of the block finder radius.
324 s16 last_nearest_unsent_d;
327 JMutexAutoLock lock(m_blocks_sent_mutex);
329 if(m_last_center != center)
331 m_nearest_unsent_d = 0;
332 m_last_center = center;
335 /*dstream<<"m_nearest_unsent_reset_timer="
336 <<m_nearest_unsent_reset_timer<<std::endl;*/
337 if(m_nearest_unsent_reset_timer > 5.0)
339 m_nearest_unsent_reset_timer = 0;
340 m_nearest_unsent_d = 0;
341 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
344 last_nearest_unsent_d = m_nearest_unsent_d;
346 d_start = m_nearest_unsent_d;
349 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
350 ("max_simultaneous_block_sends_per_client");
351 u16 maximum_simultaneous_block_sends =
352 maximum_simultaneous_block_sends_setting;
355 Check the time from last addNode/removeNode.
357 Decrease send rate if player is building stuff.
360 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
361 m_time_from_building.m_value += dtime;
362 /*if(m_time_from_building.m_value
363 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
364 if(m_time_from_building.m_value < g_settings.getFloat(
365 "full_block_send_enable_min_time_from_building"))
367 maximum_simultaneous_block_sends
368 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
372 u32 num_blocks_selected;
374 JMutexAutoLock lock(m_blocks_sending_mutex);
375 num_blocks_selected = m_blocks_sending.size();
379 next time d will be continued from the d from which the nearest
380 unsent block was found this time.
382 This is because not necessarily any of the blocks found this
383 time are actually sent.
385 s32 new_nearest_unsent_d = -1;
387 // Serialization version used
388 //u8 ser_version = serialization_version;
390 //bool has_incomplete_blocks = false;
392 s16 d_max = g_settings.getS16("max_block_send_distance");
393 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
395 //dstream<<"Starting from "<<d_start<<std::endl;
397 for(s16 d = d_start; d <= d_max; d++)
399 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
401 //if(has_incomplete_blocks == false)
403 JMutexAutoLock lock(m_blocks_sent_mutex);
405 If m_nearest_unsent_d was changed by the EmergeThread
406 (it can change it to 0 through SetBlockNotSent),
408 Else update m_nearest_unsent_d
410 if(m_nearest_unsent_d != last_nearest_unsent_d)
412 d = m_nearest_unsent_d;
413 last_nearest_unsent_d = m_nearest_unsent_d;
418 Get the border/face dot coordinates of a "d-radiused"
421 core::list<v3s16> list;
422 getFacePositions(list, d);
424 core::list<v3s16>::Iterator li;
425 for(li=list.begin(); li!=list.end(); li++)
427 v3s16 p = *li + center;
431 - Don't allow too many simultaneous transfers
432 - EXCEPT when the blocks are very close
434 Also, don't send blocks that are already flying.
437 u16 maximum_simultaneous_block_sends_now =
438 maximum_simultaneous_block_sends;
440 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
442 maximum_simultaneous_block_sends_now =
443 maximum_simultaneous_block_sends_setting;
447 JMutexAutoLock lock(m_blocks_sending_mutex);
449 // Limit is dynamically lowered when building
450 if(num_blocks_selected
451 >= maximum_simultaneous_block_sends_now)
453 /*dstream<<"Not sending more blocks. Queue full. "
454 <<m_blocks_sending.size()
459 if(m_blocks_sending.find(p) != NULL)
466 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
467 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
468 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
469 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
470 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
471 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
474 // If this is true, inexistent block will be made from scratch
475 bool generate = d <= d_max_gen;
478 /*// Limit the generating area vertically to 2/3
479 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
482 // Limit the send area vertically to 2/3
483 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
489 If block is far away, don't generate it unless it is
492 NOTE: We can't know the ground level this way with the
498 MapSector *sector = NULL;
501 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
503 catch(InvalidPositionException &e)
509 // Get center ground height in nodes
510 f32 gh = sector->getGroundHeight(
511 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
512 // Block center y in nodes
513 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
514 // If differs a lot, don't generate
515 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
522 Don't draw if not in sight
525 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
531 Don't send already sent blocks
534 JMutexAutoLock lock(m_blocks_sent_mutex);
536 if(m_blocks_sent.find(p) != NULL)
541 Check if map has this block
543 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
545 bool surely_not_found_on_disk = false;
546 bool block_is_invalid = false;
549 /*if(block->isIncomplete())
551 has_incomplete_blocks = true;
557 surely_not_found_on_disk = true;
560 if(block->isValid() == false)
562 block_is_invalid = true;
566 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
567 v2s16 chunkpos = map->sector_to_chunk(p2d);
568 if(map->chunkNonVolatile(chunkpos) == false)
569 block_is_invalid = true;
570 /*MapChunk *chunk = map->getChunk(chunkpos);
572 block_is_invalid = true;
573 else if(chunk->getIsVolatile() == true)
574 block_is_invalid = true;*/
578 If block has been marked to not exist on disk (dummy)
579 and generating new ones is not wanted, skip block.
581 if(generate == false && surely_not_found_on_disk == true)
588 Record the lowest d from which a a block has been
589 found being not sent and possibly to exist
591 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
593 new_nearest_unsent_d = d;
597 Add inexistent block to emerge queue.
599 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
601 //dstream<<"asd"<<std::endl;
603 /*SharedPtr<JMutexAutoLock> lock
604 (m_num_blocks_in_emerge_queue.getLock());*/
606 //TODO: Get value from somewhere
607 // Allow only one block in emerge queue
608 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
610 //dstream<<"Adding block to emerge queue"<<std::endl;
612 // Add it to the emerge queue and trigger the thread
615 if(generate == false)
616 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
618 server->m_emerge_queue.addBlock(peer_id, p, flags);
619 server->m_emergethread.trigger();
630 PrioritySortedBlockTransfer q((float)d, p, peer_id);
634 num_blocks_selected += 1;
639 if(new_nearest_unsent_d != -1)
641 JMutexAutoLock lock(m_blocks_sent_mutex);
642 m_nearest_unsent_d = new_nearest_unsent_d;
646 void RemoteClient::SendObjectData(
649 core::map<v3s16, bool> &stepped_blocks
652 DSTACK(__FUNCTION_NAME);
654 // Can't send anything without knowing version
655 if(serialization_version == SER_FMT_VER_INVALID)
657 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
663 Send a TOCLIENT_OBJECTDATA packet.
667 u16 number of player positions
678 std::ostringstream os(std::ios_base::binary);
682 writeU16(buf, TOCLIENT_OBJECTDATA);
683 os.write((char*)buf, 2);
686 Get and write player data
689 // Get connected players
690 core::list<Player*> players = server->m_env.getPlayers(true);
692 // Write player count
693 u16 playercount = players.size();
694 writeU16(buf, playercount);
695 os.write((char*)buf, 2);
697 core::list<Player*>::Iterator i;
698 for(i = players.begin();
699 i != players.end(); i++)
703 v3f pf = player->getPosition();
704 v3f sf = player->getSpeed();
706 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
707 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
708 s32 pitch_i (player->getPitch() * 100);
709 s32 yaw_i (player->getYaw() * 100);
711 writeU16(buf, player->peer_id);
712 os.write((char*)buf, 2);
713 writeV3S32(buf, position_i);
714 os.write((char*)buf, 12);
715 writeV3S32(buf, speed_i);
716 os.write((char*)buf, 12);
717 writeS32(buf, pitch_i);
718 os.write((char*)buf, 4);
719 writeS32(buf, yaw_i);
720 os.write((char*)buf, 4);
724 Get and write object data
730 For making players to be able to build to their nearby
731 environment (building is not possible on blocks that are not
734 - Add blocks to emerge queue if they are not found
736 SUGGESTION: These could be ignored from the backside of the player
739 Player *player = server->m_env.getPlayer(peer_id);
743 v3f playerpos = player->getPosition();
744 v3f playerspeed = player->getSpeed();
746 v3s16 center_nodepos = floatToInt(playerpos);
747 v3s16 center = getNodeBlockPos(center_nodepos);
749 s16 d_max = g_settings.getS16("active_object_range");
751 // Number of blocks whose objects were written to bos
754 std::ostringstream bos(std::ios_base::binary);
756 for(s16 d = 0; d <= d_max; d++)
758 core::list<v3s16> list;
759 getFacePositions(list, d);
761 core::list<v3s16>::Iterator li;
762 for(li=list.begin(); li!=list.end(); li++)
764 v3s16 p = *li + center;
767 Ignore blocks that haven't been sent to the client
770 JMutexAutoLock sentlock(m_blocks_sent_mutex);
771 if(m_blocks_sent.find(p) == NULL)
775 // Try stepping block and add it to a send queue
780 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
783 Step block if not in stepped_blocks and add to stepped_blocks.
785 if(stepped_blocks.find(p) == NULL)
787 block->stepObjects(dtime, true, server->getDayNightRatio());
788 stepped_blocks.insert(p, true);
789 block->setChangedFlag();
792 // Skip block if there are no objects
793 if(block->getObjectCount() == 0)
802 bos.write((char*)buf, 6);
805 block->serializeObjects(bos, serialization_version);
810 Stop collecting objects if data is already too big
812 // Sum of player and object data sizes
813 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
814 // break out if data too big
815 if(sum > MAX_OBJECTDATA_SIZE)
817 goto skip_subsequent;
821 catch(InvalidPositionException &e)
824 // Add it to the emerge queue and trigger the thread.
825 // Fetch the block only if it is on disk.
827 // Grab and increment counter
828 /*SharedPtr<JMutexAutoLock> lock
829 (m_num_blocks_in_emerge_queue.getLock());
830 m_num_blocks_in_emerge_queue.m_value++;*/
832 // Add to queue as an anonymous fetch from disk
833 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
834 server->m_emerge_queue.addBlock(0, p, flags);
835 server->m_emergethread.trigger();
843 writeU16(buf, blockcount);
844 os.write((char*)buf, 2);
846 // Write block objects
853 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
856 std::string s = os.str();
857 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
858 // Send as unreliable
859 server->m_con.Send(peer_id, 0, data, false);
862 void RemoteClient::GotBlock(v3s16 p)
864 JMutexAutoLock lock(m_blocks_sending_mutex);
865 JMutexAutoLock lock2(m_blocks_sent_mutex);
866 if(m_blocks_sending.find(p) != NULL)
867 m_blocks_sending.remove(p);
870 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
871 " m_blocks_sending"<<std::endl;*/
872 m_excess_gotblocks++;
874 m_blocks_sent.insert(p, true);
877 void RemoteClient::SentBlock(v3s16 p)
879 JMutexAutoLock lock(m_blocks_sending_mutex);
880 /*if(m_blocks_sending.size() > 15)
882 dstream<<"RemoteClient::SentBlock(): "
883 <<"m_blocks_sending.size()="
884 <<m_blocks_sending.size()<<std::endl;
886 if(m_blocks_sending.find(p) == NULL)
887 m_blocks_sending.insert(p, 0.0);
889 dstream<<"RemoteClient::SentBlock(): Sent block"
890 " already in m_blocks_sending"<<std::endl;
893 void RemoteClient::SetBlockNotSent(v3s16 p)
895 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
896 JMutexAutoLock sentlock(m_blocks_sent_mutex);
898 m_nearest_unsent_d = 0;
900 if(m_blocks_sending.find(p) != NULL)
901 m_blocks_sending.remove(p);
902 if(m_blocks_sent.find(p) != NULL)
903 m_blocks_sent.remove(p);
906 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
908 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
909 JMutexAutoLock sentlock(m_blocks_sent_mutex);
911 m_nearest_unsent_d = 0;
913 for(core::map<v3s16, MapBlock*>::Iterator
914 i = blocks.getIterator();
915 i.atEnd()==false; i++)
917 v3s16 p = i.getNode()->getKey();
919 if(m_blocks_sending.find(p) != NULL)
920 m_blocks_sending.remove(p);
921 if(m_blocks_sent.find(p) != NULL)
922 m_blocks_sent.remove(p);
930 PlayerInfo::PlayerInfo()
935 void PlayerInfo::PrintLine(std::ostream *s)
938 (*s)<<"\""<<name<<"\" ("
939 <<(position.X/10)<<","<<(position.Y/10)
940 <<","<<(position.Z/10)<<") ";
942 (*s)<<" avg_rtt="<<avg_rtt;
946 u32 PIChecksum(core::list<PlayerInfo> &l)
948 core::list<PlayerInfo>::Iterator i;
951 for(i=l.begin(); i!=l.end(); i++)
953 checksum += a * (i->id+1);
954 checksum ^= 0x435aafcd;
965 std::string mapsavedir
967 m_env(new ServerMap(mapsavedir), dout_server),
968 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
970 m_emergethread(this),
973 m_time_of_day_send_timer(0),
975 m_mapsavedir(mapsavedir),
976 m_shutdown_requested(false)
978 //m_flowwater_timer = 0.0;
979 m_liquid_transform_timer = 0.0;
980 m_print_info_timer = 0.0;
981 m_objectdata_timer = 0.0;
982 m_emergethread_trigger_timer = 0.0;
983 m_savemap_timer = 0.0;
987 m_step_dtime_mutex.Init();
991 m_env.deSerializePlayers(m_mapsavedir);
997 Send shutdown message
1000 JMutexAutoLock conlock(m_con_mutex);
1002 std::wstring line = L"*** Server shutting down";
1005 Send the message to clients
1007 for(core::map<u16, RemoteClient*>::Iterator
1008 i = m_clients.getIterator();
1009 i.atEnd() == false; i++)
1011 // Get client and check that it is valid
1012 RemoteClient *client = i.getNode()->getValue();
1013 assert(client->peer_id == i.getNode()->getKey());
1014 if(client->serialization_version == SER_FMT_VER_INVALID)
1017 SendChatMessage(client->peer_id, line);
1024 m_env.serializePlayers(m_mapsavedir);
1035 JMutexAutoLock clientslock(m_con_mutex);
1037 for(core::map<u16, RemoteClient*>::Iterator
1038 i = m_clients.getIterator();
1039 i.atEnd() == false; i++)
1042 // NOTE: These are removed by env destructor
1044 u16 peer_id = i.getNode()->getKey();
1045 JMutexAutoLock envlock(m_env_mutex);
1046 m_env.removePlayer(peer_id);
1050 delete i.getNode()->getValue();
1055 void Server::start(unsigned short port)
1057 DSTACK(__FUNCTION_NAME);
1058 // Stop thread if already running
1061 // Initialize connection
1062 m_con.setTimeoutMs(30);
1066 m_thread.setRun(true);
1069 dout_server<<"Server started on port "<<port<<std::endl;
1074 DSTACK(__FUNCTION_NAME);
1075 // Stop threads (set run=false first so both start stopping)
1076 m_thread.setRun(false);
1077 m_emergethread.setRun(false);
1079 m_emergethread.stop();
1081 dout_server<<"Server threads stopped"<<std::endl;
1084 void Server::step(float dtime)
1086 DSTACK(__FUNCTION_NAME);
1091 JMutexAutoLock lock(m_step_dtime_mutex);
1092 m_step_dtime += dtime;
1096 void Server::AsyncRunStep()
1098 DSTACK(__FUNCTION_NAME);
1102 JMutexAutoLock lock1(m_step_dtime_mutex);
1103 dtime = m_step_dtime;
1106 // Send blocks to clients
1112 //dstream<<"Server steps "<<dtime<<std::endl;
1113 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1116 JMutexAutoLock lock1(m_step_dtime_mutex);
1117 m_step_dtime -= dtime;
1124 m_uptime.set(m_uptime.get() + dtime);
1128 Update m_time_of_day
1131 m_time_counter += dtime;
1132 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1133 u32 units = (u32)(m_time_counter*speed);
1134 m_time_counter -= (f32)units / speed;
1135 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1137 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1140 Send to clients at constant intervals
1143 m_time_of_day_send_timer -= dtime;
1144 if(m_time_of_day_send_timer < 0.0)
1146 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1148 //JMutexAutoLock envlock(m_env_mutex);
1149 JMutexAutoLock conlock(m_con_mutex);
1151 for(core::map<u16, RemoteClient*>::Iterator
1152 i = m_clients.getIterator();
1153 i.atEnd() == false; i++)
1155 RemoteClient *client = i.getNode()->getValue();
1156 //Player *player = m_env.getPlayer(client->peer_id);
1158 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1159 m_time_of_day.get());
1161 m_con.Send(client->peer_id, 0, data, true);
1167 // Process connection's timeouts
1168 JMutexAutoLock lock2(m_con_mutex);
1169 m_con.RunTimeouts(dtime);
1173 // This has to be called so that the client list gets synced
1174 // with the peer list of the connection
1175 handlePeerChanges();
1180 // This also runs Map's timers
1181 JMutexAutoLock lock(m_env_mutex);
1192 m_liquid_transform_timer += dtime;
1193 if(m_liquid_transform_timer >= 1.00)
1195 m_liquid_transform_timer -= 1.00;
1197 JMutexAutoLock lock(m_env_mutex);
1199 core::map<v3s16, MapBlock*> modified_blocks;
1200 m_env.getMap().transformLiquids(modified_blocks);
1205 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1206 ServerMap &map = ((ServerMap&)m_env.getMap());
1207 map.updateLighting(modified_blocks, lighting_modified_blocks);
1209 // Add blocks modified by lighting to modified_blocks
1210 for(core::map<v3s16, MapBlock*>::Iterator
1211 i = lighting_modified_blocks.getIterator();
1212 i.atEnd() == false; i++)
1214 MapBlock *block = i.getNode()->getValue();
1215 modified_blocks.insert(block->getPos(), block);
1219 Set the modified blocks unsent for all the clients
1222 JMutexAutoLock lock2(m_con_mutex);
1224 for(core::map<u16, RemoteClient*>::Iterator
1225 i = m_clients.getIterator();
1226 i.atEnd() == false; i++)
1228 RemoteClient *client = i.getNode()->getValue();
1230 if(modified_blocks.size() > 0)
1232 // Remove block from sent history
1233 client->SetBlocksNotSent(modified_blocks);
1238 // Periodically print some info
1240 float &counter = m_print_info_timer;
1246 JMutexAutoLock lock2(m_con_mutex);
1248 for(core::map<u16, RemoteClient*>::Iterator
1249 i = m_clients.getIterator();
1250 i.atEnd() == false; i++)
1252 //u16 peer_id = i.getNode()->getKey();
1253 RemoteClient *client = i.getNode()->getValue();
1254 client->PrintInfo(std::cout);
1262 NOTE: Some of this could be moved to RemoteClient
1266 JMutexAutoLock envlock(m_env_mutex);
1267 JMutexAutoLock conlock(m_con_mutex);
1269 for(core::map<u16, RemoteClient*>::Iterator
1270 i = m_clients.getIterator();
1271 i.atEnd() == false; i++)
1273 RemoteClient *client = i.getNode()->getValue();
1274 Player *player = m_env.getPlayer(client->peer_id);
1276 JMutexAutoLock digmutex(client->m_dig_mutex);
1278 if(client->m_dig_tool_item == -1)
1281 client->m_dig_time_remaining -= dtime;
1283 if(client->m_dig_time_remaining > 0)
1285 client->m_time_from_building.set(0.0);
1289 v3s16 p_under = client->m_dig_position;
1291 // Mandatory parameter; actually used for nothing
1292 core::map<v3s16, MapBlock*> modified_blocks;
1298 // Get material at position
1299 material = m_env.getMap().getNode(p_under).d;
1300 // If it's not diggable, do nothing
1301 if(content_diggable(material) == false)
1303 derr_server<<"Server: Not finishing digging: Node not diggable"
1305 client->m_dig_tool_item = -1;
1309 catch(InvalidPositionException &e)
1311 derr_server<<"Server: Not finishing digging: Node not found"
1313 client->m_dig_tool_item = -1;
1319 SharedBuffer<u8> reply(replysize);
1320 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1321 writeS16(&reply[2], p_under.X);
1322 writeS16(&reply[4], p_under.Y);
1323 writeS16(&reply[6], p_under.Z);
1325 m_con.SendToAll(0, reply, true);
1327 if(g_settings.getBool("creative_mode") == false)
1329 // Add to inventory and send inventory
1330 InventoryItem *item = new MaterialItem(material, 1);
1331 player->inventory.addItem("main", item);
1332 SendInventory(player->peer_id);
1337 (this takes some time so it is done after the quick stuff)
1339 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1345 // Update water pressure around modification
1346 // This also adds it to m_flow_active_nodes if appropriate
1348 MapVoxelManipulator v(&m_env.getMap());
1349 v.m_disable_water_climb =
1350 g_settings.getBool("disable_water_climb");
1352 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1356 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1358 catch(ProcessingLimitException &e)
1360 dstream<<"Processing limit reached (1)"<<std::endl;
1363 v.blitBack(modified_blocks);
1368 // Send object positions
1370 float &counter = m_objectdata_timer;
1372 if(counter >= g_settings.getFloat("objectdata_interval"))
1374 JMutexAutoLock lock1(m_env_mutex);
1375 JMutexAutoLock lock2(m_con_mutex);
1376 SendObjectData(counter);
1383 Trigger emergethread (it somehow gets to a non-triggered but
1384 bysy state sometimes)
1387 float &counter = m_emergethread_trigger_timer;
1393 m_emergethread.trigger();
1399 float &counter = m_savemap_timer;
1401 if(counter >= g_settings.getFloat("server_map_save_interval"))
1405 JMutexAutoLock lock(m_env_mutex);
1407 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1409 // Save only changed parts
1410 m_env.getMap().save(true);
1412 // Delete unused sectors
1413 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1414 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1415 if(deleted_count > 0)
1417 dout_server<<"Server: Unloaded "<<deleted_count
1418 <<" sectors from memory"<<std::endl;
1422 m_env.serializePlayers(m_mapsavedir);
1428 void Server::Receive()
1430 DSTACK(__FUNCTION_NAME);
1431 u32 data_maxsize = 10000;
1432 Buffer<u8> data(data_maxsize);
1437 JMutexAutoLock conlock(m_con_mutex);
1438 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1441 // This has to be called so that the client list gets synced
1442 // with the peer list of the connection
1443 handlePeerChanges();
1445 ProcessData(*data, datasize, peer_id);
1447 catch(con::InvalidIncomingDataException &e)
1449 derr_server<<"Server::Receive(): "
1450 "InvalidIncomingDataException: what()="
1451 <<e.what()<<std::endl;
1453 catch(con::PeerNotFoundException &e)
1455 //NOTE: This is not needed anymore
1457 // The peer has been disconnected.
1458 // Find the associated player and remove it.
1460 /*JMutexAutoLock envlock(m_env_mutex);
1462 dout_server<<"ServerThread: peer_id="<<peer_id
1463 <<" has apparently closed connection. "
1464 <<"Removing player."<<std::endl;
1466 m_env.removePlayer(peer_id);*/
1470 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1472 DSTACK(__FUNCTION_NAME);
1473 // Environment is locked first.
1474 JMutexAutoLock envlock(m_env_mutex);
1475 JMutexAutoLock conlock(m_con_mutex);
1479 peer = m_con.GetPeer(peer_id);
1481 catch(con::PeerNotFoundException &e)
1483 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1484 <<peer_id<<" not found"<<std::endl;
1488 //u8 peer_ser_ver = peer->serialization_version;
1489 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1497 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1499 if(command == TOSERVER_INIT)
1501 // [0] u16 TOSERVER_INIT
1502 // [2] u8 SER_FMT_VER_HIGHEST
1503 // [3] u8[20] player_name
1508 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1509 <<peer->id<<std::endl;
1511 // First byte after command is maximum supported
1512 // serialization version
1513 u8 client_max = data[2];
1514 u8 our_max = SER_FMT_VER_HIGHEST;
1515 // Use the highest version supported by both
1516 u8 deployed = core::min_(client_max, our_max);
1517 // If it's lower than the lowest supported, give up.
1518 if(deployed < SER_FMT_VER_LOWEST)
1519 deployed = SER_FMT_VER_INVALID;
1521 //peer->serialization_version = deployed;
1522 getClient(peer->id)->pending_serialization_version = deployed;
1524 if(deployed == SER_FMT_VER_INVALID)
1526 derr_server<<DTIME<<"Server: Cannot negotiate "
1527 "serialization version with peer "
1528 <<peer_id<<std::endl;
1537 const u32 playername_size = 20;
1538 char playername[playername_size];
1539 for(u32 i=0; i<playername_size-1; i++)
1541 playername[i] = data[3+i];
1543 playername[playername_size-1] = 0;
1546 Player *player = emergePlayer(playername, "", peer_id);
1547 //Player *player = m_env.getPlayer(peer_id);
1550 // DEBUG: Test serialization
1551 std::ostringstream test_os;
1552 player->serialize(test_os);
1553 dstream<<"Player serialization test: \""<<test_os.str()
1555 std::istringstream test_is(test_os.str());
1556 player->deSerialize(test_is);
1559 // If failed, cancel
1562 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1563 <<": failed to emerge player"<<std::endl;
1568 // If a client is already connected to the player, cancel
1569 if(player->peer_id != 0)
1571 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1572 <<" tried to connect to "
1573 "an already connected player (peer_id="
1574 <<player->peer_id<<")"<<std::endl;
1577 // Set client of player
1578 player->peer_id = peer_id;
1581 // Check if player doesn't exist
1583 throw con::InvalidIncomingDataException
1584 ("Server::ProcessData(): INIT: Player doesn't exist");
1586 /*// update name if it was supplied
1587 if(datasize >= 20+3)
1590 player->updateName((const char*)&data[3]);
1593 // Now answer with a TOCLIENT_INIT
1595 SharedBuffer<u8> reply(2+1+6);
1596 writeU16(&reply[0], TOCLIENT_INIT);
1597 writeU8(&reply[2], deployed);
1598 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1600 m_con.Send(peer_id, 0, reply, true);
1604 if(command == TOSERVER_INIT2)
1606 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1607 <<peer->id<<std::endl;
1610 getClient(peer->id)->serialization_version
1611 = getClient(peer->id)->pending_serialization_version;
1614 Send some initialization data
1617 // Send player info to all players
1620 // Send inventory to player
1621 SendInventory(peer->id);
1625 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1626 m_time_of_day.get());
1627 m_con.Send(peer->id, 0, data, true);
1630 // Send information about server to player in chat
1631 SendChatMessage(peer_id, getStatusString());
1633 // Send information about joining in chat
1635 std::wstring name = L"unknown";
1636 Player *player = m_env.getPlayer(peer_id);
1638 name = narrow_to_wide(player->getName());
1640 std::wstring message;
1643 message += L" joined game";
1644 BroadcastChatMessage(message);
1650 if(peer_ser_ver == SER_FMT_VER_INVALID)
1652 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1653 " serialization format invalid or not initialized."
1654 " Skipping incoming command="<<command<<std::endl;
1658 Player *player = m_env.getPlayer(peer_id);
1661 derr_server<<"Server::ProcessData(): Cancelling: "
1662 "No player for peer_id="<<peer_id
1666 if(command == TOSERVER_PLAYERPOS)
1668 if(datasize < 2+12+12+4+4)
1672 v3s32 ps = readV3S32(&data[start+2]);
1673 v3s32 ss = readV3S32(&data[start+2+12]);
1674 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1675 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1676 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1677 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1678 pitch = wrapDegrees(pitch);
1679 yaw = wrapDegrees(yaw);
1680 player->setPosition(position);
1681 player->setSpeed(speed);
1682 player->setPitch(pitch);
1683 player->setYaw(yaw);
1685 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1686 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1687 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1689 else if(command == TOSERVER_GOTBLOCKS)
1702 u16 count = data[2];
1703 for(u16 i=0; i<count; i++)
1705 if((s16)datasize < 2+1+(i+1)*6)
1706 throw con::InvalidIncomingDataException
1707 ("GOTBLOCKS length is too short");
1708 v3s16 p = readV3S16(&data[2+1+i*6]);
1709 /*dstream<<"Server: GOTBLOCKS ("
1710 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1711 RemoteClient *client = getClient(peer_id);
1712 client->GotBlock(p);
1715 else if(command == TOSERVER_DELETEDBLOCKS)
1728 u16 count = data[2];
1729 for(u16 i=0; i<count; i++)
1731 if((s16)datasize < 2+1+(i+1)*6)
1732 throw con::InvalidIncomingDataException
1733 ("DELETEDBLOCKS length is too short");
1734 v3s16 p = readV3S16(&data[2+1+i*6]);
1735 /*dstream<<"Server: DELETEDBLOCKS ("
1736 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1737 RemoteClient *client = getClient(peer_id);
1738 client->SetBlockNotSent(p);
1741 else if(command == TOSERVER_CLICK_OBJECT)
1748 [2] u8 button (0=left, 1=right)
1753 u8 button = readU8(&data[2]);
1755 p.X = readS16(&data[3]);
1756 p.Y = readS16(&data[5]);
1757 p.Z = readS16(&data[7]);
1758 s16 id = readS16(&data[9]);
1759 //u16 item_i = readU16(&data[11]);
1761 MapBlock *block = NULL;
1764 block = m_env.getMap().getBlockNoCreate(p);
1766 catch(InvalidPositionException &e)
1768 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1772 MapBlockObject *obj = block->getObject(id);
1776 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1780 //TODO: Check that object is reasonably close
1785 InventoryList *ilist = player->inventory.getList("main");
1786 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1789 // Skip if inventory has no free space
1790 if(ilist->getUsedSlots() == ilist->getSize())
1792 dout_server<<"Player inventory has no free space"<<std::endl;
1797 Create the inventory item
1799 InventoryItem *item = NULL;
1800 // If it is an item-object, take the item from it
1801 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1803 item = ((ItemObject*)obj)->createInventoryItem();
1805 // Else create an item of the object
1808 item = new MapBlockObjectItem
1809 (obj->getInventoryString());
1812 // Add to inventory and send inventory
1813 ilist->addItem(item);
1814 SendInventory(player->peer_id);
1817 // Remove from block
1818 block->removeObject(id);
1821 else if(command == TOSERVER_GROUND_ACTION)
1829 [3] v3s16 nodepos_undersurface
1830 [9] v3s16 nodepos_abovesurface
1835 2: stop digging (all parameters ignored)
1836 3: digging completed
1838 u8 action = readU8(&data[2]);
1840 p_under.X = readS16(&data[3]);
1841 p_under.Y = readS16(&data[5]);
1842 p_under.Z = readS16(&data[7]);
1844 p_over.X = readS16(&data[9]);
1845 p_over.Y = readS16(&data[11]);
1846 p_over.Z = readS16(&data[13]);
1847 u16 item_i = readU16(&data[15]);
1849 //TODO: Check that target is reasonably close
1857 NOTE: This can be used in the future to check if
1858 somebody is cheating, by checking the timing.
1865 else if(action == 2)
1868 RemoteClient *client = getClient(peer->id);
1869 JMutexAutoLock digmutex(client->m_dig_mutex);
1870 client->m_dig_tool_item = -1;
1875 3: Digging completed
1877 else if(action == 3)
1879 // Mandatory parameter; actually used for nothing
1880 core::map<v3s16, MapBlock*> modified_blocks;
1883 u8 mineral = MINERAL_NONE;
1887 MapNode n = m_env.getMap().getNode(p_under);
1888 // Get material at position
1890 // If it's not diggable, do nothing
1891 if(content_diggable(material) == false)
1893 derr_server<<"Server: Not finishing digging: Node not diggable"
1898 mineral = n.getMineral();
1900 catch(InvalidPositionException &e)
1902 derr_server<<"Server: Not finishing digging: Node not found."
1903 <<" Adding block to emerge queue."
1905 m_emerge_queue.addBlock(peer_id,
1906 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1911 Send the removal to all other clients
1916 SharedBuffer<u8> reply(replysize);
1917 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1918 writeS16(&reply[2], p_under.X);
1919 writeS16(&reply[4], p_under.Y);
1920 writeS16(&reply[6], p_under.Z);
1922 for(core::map<u16, RemoteClient*>::Iterator
1923 i = m_clients.getIterator();
1924 i.atEnd() == false; i++)
1926 // Get client and check that it is valid
1927 RemoteClient *client = i.getNode()->getValue();
1928 assert(client->peer_id == i.getNode()->getKey());
1929 if(client->serialization_version == SER_FMT_VER_INVALID)
1932 // Don't send if it's the same one
1933 if(peer_id == client->peer_id)
1937 m_con.Send(client->peer_id, 0, reply, true);
1941 Update and send inventory
1944 if(g_settings.getBool("creative_mode") == false)
1949 InventoryList *mlist = player->inventory.getList("main");
1952 InventoryItem *item = mlist->getItem(item_i);
1953 if(item && (std::string)item->getName() == "ToolItem")
1955 ToolItem *titem = (ToolItem*)item;
1956 std::string toolname = titem->getToolName();
1958 // Get digging properties for material and tool
1959 DiggingProperties prop =
1960 getDiggingProperties(material, toolname);
1962 if(prop.diggable == false)
1964 derr_server<<"Server: WARNING: Player digged"
1965 <<" with impossible material + tool"
1966 <<" combination"<<std::endl;
1969 bool weared_out = titem->addWear(prop.wear);
1973 mlist->deleteItem(item_i);
1979 Add dug item to inventory
1982 InventoryItem *item = NULL;
1984 if(mineral != MINERAL_NONE)
1985 item = getDiggedMineralItem(mineral);
1990 std::string &dug_s = content_features(material).dug_item;
1993 std::istringstream is(dug_s, std::ios::binary);
1994 item = InventoryItem::deSerialize(is);
2000 // Add a item to inventory
2001 player->inventory.addItem("main", item);
2004 SendInventory(player->peer_id);
2010 (this takes some time so it is done after the quick stuff)
2012 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2019 // Update water pressure around modification
2020 // This also adds it to m_flow_active_nodes if appropriate
2022 MapVoxelManipulator v(&m_env.getMap());
2023 v.m_disable_water_climb =
2024 g_settings.getBool("disable_water_climb");
2026 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2030 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2032 catch(ProcessingLimitException &e)
2034 dstream<<"Processing limit reached (1)"<<std::endl;
2037 v.blitBack(modified_blocks);
2044 else if(action == 1)
2047 InventoryList *ilist = player->inventory.getList("main");
2052 InventoryItem *item = ilist->getItem(item_i);
2054 // If there is no item, it is not possible to add it anywhere
2059 Handle material items
2061 if(std::string("MaterialItem") == item->getName())
2064 // Don't add a node if this is not a free space
2065 MapNode n2 = m_env.getMap().getNode(p_over);
2066 if(content_buildable_to(n2.d) == false)
2068 // Client probably has wrong data.
2069 // Set block not sent, so that client will get
2071 dstream<<"Client "<<peer_id<<" tried to place"
2072 <<" node in invalid position; setting"
2073 <<" MapBlock not sent."<<std::endl;
2074 RemoteClient *client = getClient(peer_id);
2075 v3s16 blockpos = getNodeBlockPos(p_over);
2076 client->SetBlockNotSent(blockpos);
2080 catch(InvalidPositionException &e)
2082 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2083 <<" Adding block to emerge queue."
2085 m_emerge_queue.addBlock(peer_id,
2086 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2090 // Reset build time counter
2091 getClient(peer->id)->m_time_from_building.set(0.0);
2094 MaterialItem *mitem = (MaterialItem*)item;
2096 n.d = mitem->getMaterial();
2097 if(content_features(n.d).wall_mounted)
2098 n.dir = packDir(p_under - p_over);
2102 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2103 SharedBuffer<u8> reply(replysize);
2104 writeU16(&reply[0], TOCLIENT_ADDNODE);
2105 writeS16(&reply[2], p_over.X);
2106 writeS16(&reply[4], p_over.Y);
2107 writeS16(&reply[6], p_over.Z);
2108 n.serialize(&reply[8], peer_ser_ver);
2110 m_con.SendToAll(0, reply, true);
2115 InventoryList *ilist = player->inventory.getList("main");
2116 if(g_settings.getBool("creative_mode") == false && ilist)
2118 // Remove from inventory and send inventory
2119 if(mitem->getCount() == 1)
2120 ilist->deleteItem(item_i);
2124 SendInventory(peer_id);
2130 This takes some time so it is done after the quick stuff
2132 core::map<v3s16, MapBlock*> modified_blocks;
2133 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2139 InventoryList *ilist = player->inventory.getList("main");
2140 if(g_settings.getBool("creative_mode") == false && ilist)
2142 // Remove from inventory and send inventory
2143 if(mitem->getCount() == 1)
2144 ilist->deleteItem(item_i);
2148 SendInventory(peer_id);
2154 This takes some time so it is done after the quick stuff
2156 core::map<v3s16, MapBlock*> modified_blocks;
2157 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2160 Set the modified blocks unsent for all the clients
2163 //JMutexAutoLock lock2(m_con_mutex);
2165 for(core::map<u16, RemoteClient*>::Iterator
2166 i = m_clients.getIterator();
2167 i.atEnd() == false; i++)
2169 RemoteClient *client = i.getNode()->getValue();
2171 if(modified_blocks.size() > 0)
2173 // Remove block from sent history
2174 client->SetBlocksNotSent(modified_blocks);
2184 // Update water pressure around modification
2185 // This also adds it to m_flow_active_nodes if appropriate
2187 MapVoxelManipulator v(&m_env.getMap());
2188 v.m_disable_water_climb =
2189 g_settings.getBool("disable_water_climb");
2191 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2195 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2197 catch(ProcessingLimitException &e)
2199 dstream<<"Processing limit reached (1)"<<std::endl;
2202 v.blitBack(modified_blocks);
2210 v3s16 blockpos = getNodeBlockPos(p_over);
2212 MapBlock *block = NULL;
2215 block = m_env.getMap().getBlockNoCreate(blockpos);
2217 catch(InvalidPositionException &e)
2219 derr_server<<"Error while placing object: "
2220 "block not found"<<std::endl;
2224 v3s16 block_pos_i_on_map = block->getPosRelative();
2225 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2227 v3f pos = intToFloat(p_over);
2228 pos -= block_pos_f_on_map;
2230 /*dout_server<<"pos="
2231 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2234 MapBlockObject *obj = NULL;
2237 Handle block object items
2239 if(std::string("MBOItem") == item->getName())
2241 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2243 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2244 "inventorystring=\""
2245 <<oitem->getInventoryString()
2246 <<"\""<<std::endl;*/
2248 obj = oitem->createObject
2249 (pos, player->getYaw(), player->getPitch());
2256 dout_server<<"Placing a miscellaneous item on map"
2259 Create an ItemObject that contains the item.
2261 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2262 std::ostringstream os(std::ios_base::binary);
2263 item->serialize(os);
2264 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2265 iobj->setItemString(os.str());
2271 derr_server<<"WARNING: item resulted in NULL object, "
2272 <<"not placing onto map"
2277 block->addObject(obj);
2279 dout_server<<"Placed object"<<std::endl;
2281 InventoryList *ilist = player->inventory.getList("main");
2282 if(g_settings.getBool("creative_mode") == false && ilist)
2284 // Remove from inventory and send inventory
2285 ilist->deleteItem(item_i);
2287 SendInventory(peer_id);
2295 Catch invalid actions
2299 derr_server<<"WARNING: Server: Invalid action "
2300 <<action<<std::endl;
2304 else if(command == TOSERVER_RELEASE)
2313 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2316 else if(command == TOSERVER_SIGNTEXT)
2325 std::string datastring((char*)&data[2], datasize-2);
2326 std::istringstream is(datastring, std::ios_base::binary);
2329 is.read((char*)buf, 6);
2330 v3s16 blockpos = readV3S16(buf);
2331 is.read((char*)buf, 2);
2332 s16 id = readS16(buf);
2333 is.read((char*)buf, 2);
2334 u16 textlen = readU16(buf);
2336 for(u16 i=0; i<textlen; i++)
2338 is.read((char*)buf, 1);
2339 text += (char)buf[0];
2342 MapBlock *block = NULL;
2345 block = m_env.getMap().getBlockNoCreate(blockpos);
2347 catch(InvalidPositionException &e)
2349 derr_server<<"Error while setting sign text: "
2350 "block not found"<<std::endl;
2354 MapBlockObject *obj = block->getObject(id);
2357 derr_server<<"Error while setting sign text: "
2358 "object not found"<<std::endl;
2362 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2364 derr_server<<"Error while setting sign text: "
2365 "object is not a sign"<<std::endl;
2369 ((SignObject*)obj)->setText(text);
2371 obj->getBlock()->setChangedFlag();
2373 else if(command == TOSERVER_INVENTORY_ACTION)
2375 /*// Ignore inventory changes if in creative mode
2376 if(g_settings.getBool("creative_mode") == true)
2378 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2382 // Strip command and create a stream
2383 std::string datastring((char*)&data[2], datasize-2);
2384 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2385 std::istringstream is(datastring, std::ios_base::binary);
2387 InventoryAction *a = InventoryAction::deSerialize(is);
2391 Handle craftresult specially if not in creative mode
2393 bool disable_action = false;
2394 if(a->getType() == IACTION_MOVE
2395 && g_settings.getBool("creative_mode") == false)
2397 IMoveAction *ma = (IMoveAction*)a;
2398 // Don't allow moving anything to craftresult
2399 if(ma->to_name == "craftresult")
2402 disable_action = true;
2404 // When something is removed from craftresult
2405 if(ma->from_name == "craftresult")
2407 disable_action = true;
2408 // Remove stuff from craft
2409 InventoryList *clist = player->inventory.getList("craft");
2412 u16 count = ma->count;
2415 clist->decrementMaterials(count);
2418 // Feed action to player inventory
2419 a->apply(&player->inventory);
2422 // If something appeared in craftresult, throw it
2424 InventoryList *rlist = player->inventory.getList("craftresult");
2425 InventoryList *mlist = player->inventory.getList("main");
2426 if(rlist && mlist && rlist->getUsedSlots() == 1)
2428 InventoryItem *item1 = rlist->changeItem(0, NULL);
2429 mlist->addItem(item1);
2433 if(disable_action == false)
2435 // Feed action to player inventory
2436 a->apply(&player->inventory);
2441 SendInventory(player->peer_id);
2445 dstream<<"TOSERVER_INVENTORY_ACTION: "
2446 <<"InventoryAction::deSerialize() returned NULL"
2450 else if(command == TOSERVER_CHAT_MESSAGE)
2458 std::string datastring((char*)&data[2], datasize-2);
2459 std::istringstream is(datastring, std::ios_base::binary);
2462 is.read((char*)buf, 2);
2463 u16 len = readU16(buf);
2465 std::wstring message;
2466 for(u16 i=0; i<len; i++)
2468 is.read((char*)buf, 2);
2469 message += (wchar_t)readU16(buf);
2472 // Get player name of this client
2473 std::wstring name = narrow_to_wide(player->getName());
2475 // Line to send to players
2477 // Whether to send to the player that sent the line
2478 bool send_to_sender = false;
2479 // Whether to send to other players
2480 bool send_to_others = false;
2483 std::wstring commandprefix = L"/#";
2484 if(message.substr(0, commandprefix.size()) == commandprefix)
2486 line += L"Server: ";
2488 message = message.substr(commandprefix.size());
2489 // Get player name as narrow string
2490 std::string name_s = player->getName();
2491 // Convert message to narrow string
2492 std::string message_s = wide_to_narrow(message);
2493 // Operator is the single name defined in config.
2494 std::string operator_name = g_settings.get("name");
2495 bool is_operator = (operator_name != "" &&
2496 wide_to_narrow(name) == operator_name);
2497 bool valid_command = false;
2498 if(message_s == "help")
2500 line += L"-!- Available commands: ";
2504 line += L"shutdown setting ";
2509 send_to_sender = true;
2510 valid_command = true;
2512 else if(message_s == "status")
2514 line = getStatusString();
2515 send_to_sender = true;
2516 valid_command = true;
2518 else if(is_operator)
2520 if(message_s == "shutdown")
2522 dstream<<DTIME<<" Server: Operator requested shutdown."
2524 m_shutdown_requested.set(true);
2526 line += L"*** Server shutting down (operator request)";
2527 send_to_sender = true;
2528 valid_command = true;
2530 else if(message_s.substr(0,8) == "setting ")
2532 std::string confline = message_s.substr(8);
2533 g_settings.parseConfigLine(confline);
2534 line += L"-!- Setting changed.";
2535 send_to_sender = true;
2536 valid_command = true;
2540 if(valid_command == false)
2542 line += L"-!- Invalid command: " + message;
2543 send_to_sender = true;
2554 send_to_others = true;
2559 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2562 Send the message to clients
2564 for(core::map<u16, RemoteClient*>::Iterator
2565 i = m_clients.getIterator();
2566 i.atEnd() == false; i++)
2568 // Get client and check that it is valid
2569 RemoteClient *client = i.getNode()->getValue();
2570 assert(client->peer_id == i.getNode()->getKey());
2571 if(client->serialization_version == SER_FMT_VER_INVALID)
2575 bool sender_selected = (peer_id == client->peer_id);
2576 if(sender_selected == true && send_to_sender == false)
2578 if(sender_selected == false && send_to_others == false)
2581 SendChatMessage(client->peer_id, line);
2587 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2588 "unknown command "<<command<<std::endl;
2592 catch(SendFailedException &e)
2594 derr_server<<"Server::ProcessData(): SendFailedException: "
2600 /*void Server::Send(u16 peer_id, u16 channelnum,
2601 SharedBuffer<u8> data, bool reliable)
2603 JMutexAutoLock lock(m_con_mutex);
2604 m_con.Send(peer_id, channelnum, data, reliable);
2607 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2609 DSTACK(__FUNCTION_NAME);
2611 Create a packet with the block in the right format
2614 std::ostringstream os(std::ios_base::binary);
2615 block->serialize(os, ver);
2616 std::string s = os.str();
2617 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2619 u32 replysize = 8 + blockdata.getSize();
2620 SharedBuffer<u8> reply(replysize);
2621 v3s16 p = block->getPos();
2622 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2623 writeS16(&reply[2], p.X);
2624 writeS16(&reply[4], p.Y);
2625 writeS16(&reply[6], p.Z);
2626 memcpy(&reply[8], *blockdata, blockdata.getSize());
2628 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2629 <<": \tpacket size: "<<replysize<<std::endl;*/
2634 m_con.Send(peer_id, 1, reply, true);
2637 core::list<PlayerInfo> Server::getPlayerInfo()
2639 DSTACK(__FUNCTION_NAME);
2640 JMutexAutoLock envlock(m_env_mutex);
2641 JMutexAutoLock conlock(m_con_mutex);
2643 core::list<PlayerInfo> list;
2645 core::list<Player*> players = m_env.getPlayers();
2647 core::list<Player*>::Iterator i;
2648 for(i = players.begin();
2649 i != players.end(); i++)
2653 Player *player = *i;
2656 con::Peer *peer = m_con.GetPeer(player->peer_id);
2657 // Copy info from peer to info struct
2659 info.address = peer->address;
2660 info.avg_rtt = peer->avg_rtt;
2662 catch(con::PeerNotFoundException &e)
2664 // Set dummy peer info
2666 info.address = Address(0,0,0,0,0);
2670 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2671 info.position = player->getPosition();
2673 list.push_back(info);
2680 void Server::peerAdded(con::Peer *peer)
2682 DSTACK(__FUNCTION_NAME);
2683 dout_server<<"Server::peerAdded(): peer->id="
2684 <<peer->id<<std::endl;
2687 c.type = PEER_ADDED;
2688 c.peer_id = peer->id;
2690 m_peer_change_queue.push_back(c);
2693 void Server::deletingPeer(con::Peer *peer, bool timeout)
2695 DSTACK(__FUNCTION_NAME);
2696 dout_server<<"Server::deletingPeer(): peer->id="
2697 <<peer->id<<", timeout="<<timeout<<std::endl;
2700 c.type = PEER_REMOVED;
2701 c.peer_id = peer->id;
2702 c.timeout = timeout;
2703 m_peer_change_queue.push_back(c);
2706 void Server::SendObjectData(float dtime)
2708 DSTACK(__FUNCTION_NAME);
2710 core::map<v3s16, bool> stepped_blocks;
2712 for(core::map<u16, RemoteClient*>::Iterator
2713 i = m_clients.getIterator();
2714 i.atEnd() == false; i++)
2716 u16 peer_id = i.getNode()->getKey();
2717 RemoteClient *client = i.getNode()->getValue();
2718 assert(client->peer_id == peer_id);
2720 if(client->serialization_version == SER_FMT_VER_INVALID)
2723 client->SendObjectData(this, dtime, stepped_blocks);
2727 void Server::SendPlayerInfos()
2729 DSTACK(__FUNCTION_NAME);
2731 //JMutexAutoLock envlock(m_env_mutex);
2733 // Get connected players
2734 core::list<Player*> players = m_env.getPlayers(true);
2736 u32 player_count = players.getSize();
2737 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2739 SharedBuffer<u8> data(datasize);
2740 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2743 core::list<Player*>::Iterator i;
2744 for(i = players.begin();
2745 i != players.end(); i++)
2747 Player *player = *i;
2749 /*dstream<<"Server sending player info for player with "
2750 "peer_id="<<player->peer_id<<std::endl;*/
2752 writeU16(&data[start], player->peer_id);
2753 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2754 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2755 start += 2+PLAYERNAME_SIZE;
2758 //JMutexAutoLock conlock(m_con_mutex);
2761 m_con.SendToAll(0, data, true);
2779 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2785 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2791 enum ItemSpecType type;
2792 // Only other one of these is used
2798 items: a pointer to an array of 9 pointers to items
2799 specs: a pointer to an array of 9 ItemSpecs
2801 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2803 u16 items_min_x = 100;
2804 u16 items_max_x = 100;
2805 u16 items_min_y = 100;
2806 u16 items_max_y = 100;
2807 for(u16 y=0; y<3; y++)
2808 for(u16 x=0; x<3; x++)
2810 if(items[y*3 + x] == NULL)
2812 if(items_min_x == 100 || x < items_min_x)
2814 if(items_min_y == 100 || y < items_min_y)
2816 if(items_max_x == 100 || x > items_max_x)
2818 if(items_max_y == 100 || y > items_max_y)
2821 // No items at all, just return false
2822 if(items_min_x == 100)
2825 u16 items_w = items_max_x - items_min_x + 1;
2826 u16 items_h = items_max_y - items_min_y + 1;
2828 u16 specs_min_x = 100;
2829 u16 specs_max_x = 100;
2830 u16 specs_min_y = 100;
2831 u16 specs_max_y = 100;
2832 for(u16 y=0; y<3; y++)
2833 for(u16 x=0; x<3; x++)
2835 if(specs[y*3 + x].type == ITEM_NONE)
2837 if(specs_min_x == 100 || x < specs_min_x)
2839 if(specs_min_y == 100 || y < specs_min_y)
2841 if(specs_max_x == 100 || x > specs_max_x)
2843 if(specs_max_y == 100 || y > specs_max_y)
2846 // No specs at all, just return false
2847 if(specs_min_x == 100)
2850 u16 specs_w = specs_max_x - specs_min_x + 1;
2851 u16 specs_h = specs_max_y - specs_min_y + 1;
2854 if(items_w != specs_w || items_h != specs_h)
2857 for(u16 y=0; y<specs_h; y++)
2858 for(u16 x=0; x<specs_w; x++)
2860 u16 items_x = items_min_x + x;
2861 u16 items_y = items_min_y + y;
2862 u16 specs_x = specs_min_x + x;
2863 u16 specs_y = specs_min_y + y;
2864 InventoryItem *item = items[items_y * 3 + items_x];
2865 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2867 if(spec.type == ITEM_NONE)
2869 // Has to be no item
2875 // There should be an item
2879 std::string itemname = item->getName();
2881 if(spec.type == ITEM_MATERIAL)
2883 if(itemname != "MaterialItem")
2885 MaterialItem *mitem = (MaterialItem*)item;
2886 if(mitem->getMaterial() != spec.num)
2889 else if(spec.type == ITEM_CRAFT)
2891 if(itemname != "CraftItem")
2893 CraftItem *mitem = (CraftItem*)item;
2894 if(mitem->getSubName() != spec.name)
2897 else if(spec.type == ITEM_TOOL)
2899 // Not supported yet
2902 else if(spec.type == ITEM_MBO)
2904 // Not supported yet
2909 // Not supported yet
2917 void Server::SendInventory(u16 peer_id)
2919 DSTACK(__FUNCTION_NAME);
2921 Player* player = m_env.getPlayer(peer_id);
2924 Calculate crafting stuff
2926 if(g_settings.getBool("creative_mode") == false)
2928 InventoryList *clist = player->inventory.getList("craft");
2929 InventoryList *rlist = player->inventory.getList("craftresult");
2932 rlist->clearItems();
2936 InventoryItem *items[9];
2937 for(u16 i=0; i<9; i++)
2939 items[i] = clist->getItem(i);
2948 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2949 if(checkItemCombination(items, specs))
2951 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2960 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2961 if(checkItemCombination(items, specs))
2963 rlist->addItem(new CraftItem("Stick", 4));
2972 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2973 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2974 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2975 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2976 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2977 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2978 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2979 if(checkItemCombination(items, specs))
2981 rlist->addItem(new MapBlockObjectItem("Sign"));
2990 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2991 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2992 if(checkItemCombination(items, specs))
2994 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3003 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3004 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3005 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3006 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3007 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3008 if(checkItemCombination(items, specs))
3010 rlist->addItem(new ToolItem("WPick", 0));
3019 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3020 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3021 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3022 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3023 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3024 if(checkItemCombination(items, specs))
3026 rlist->addItem(new ToolItem("STPick", 0));
3035 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3036 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3037 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3038 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3039 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3040 if(checkItemCombination(items, specs))
3042 rlist->addItem(new ToolItem("MesePick", 0));
3047 } // if creative_mode == false
3053 std::ostringstream os;
3054 //os.imbue(std::locale("C"));
3056 player->inventory.serialize(os);
3058 std::string s = os.str();
3060 SharedBuffer<u8> data(s.size()+2);
3061 writeU16(&data[0], TOCLIENT_INVENTORY);
3062 memcpy(&data[2], s.c_str(), s.size());
3065 m_con.Send(peer_id, 0, data, true);
3068 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3070 DSTACK(__FUNCTION_NAME);
3072 std::ostringstream os(std::ios_base::binary);
3076 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3077 os.write((char*)buf, 2);
3080 writeU16(buf, message.size());
3081 os.write((char*)buf, 2);
3084 for(u32 i=0; i<message.size(); i++)
3088 os.write((char*)buf, 2);
3092 std::string s = os.str();
3093 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3095 m_con.Send(peer_id, 0, data, true);
3098 void Server::BroadcastChatMessage(const std::wstring &message)
3100 for(core::map<u16, RemoteClient*>::Iterator
3101 i = m_clients.getIterator();
3102 i.atEnd() == false; i++)
3104 // Get client and check that it is valid
3105 RemoteClient *client = i.getNode()->getValue();
3106 assert(client->peer_id == i.getNode()->getKey());
3107 if(client->serialization_version == SER_FMT_VER_INVALID)
3110 SendChatMessage(client->peer_id, message);
3114 void Server::SendBlocks(float dtime)
3116 DSTACK(__FUNCTION_NAME);
3118 JMutexAutoLock envlock(m_env_mutex);
3120 //TimeTaker timer("Server::SendBlocks");
3122 core::array<PrioritySortedBlockTransfer> queue;
3124 s32 total_sending = 0;
3126 for(core::map<u16, RemoteClient*>::Iterator
3127 i = m_clients.getIterator();
3128 i.atEnd() == false; i++)
3130 RemoteClient *client = i.getNode()->getValue();
3131 assert(client->peer_id == i.getNode()->getKey());
3133 total_sending += client->SendingCount();
3135 if(client->serialization_version == SER_FMT_VER_INVALID)
3138 client->GetNextBlocks(this, dtime, queue);
3142 // Lowest priority number comes first.
3143 // Lowest is most important.
3146 JMutexAutoLock conlock(m_con_mutex);
3148 for(u32 i=0; i<queue.size(); i++)
3150 //TODO: Calculate limit dynamically
3151 if(total_sending >= g_settings.getS32
3152 ("max_simultaneous_block_sends_server_total"))
3155 PrioritySortedBlockTransfer q = queue[i];
3157 MapBlock *block = NULL;
3160 block = m_env.getMap().getBlockNoCreate(q.pos);
3162 catch(InvalidPositionException &e)
3167 RemoteClient *client = getClient(q.peer_id);
3169 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3171 client->SentBlock(q.pos);
3178 RemoteClient* Server::getClient(u16 peer_id)
3180 DSTACK(__FUNCTION_NAME);
3181 //JMutexAutoLock lock(m_con_mutex);
3182 core::map<u16, RemoteClient*>::Node *n;
3183 n = m_clients.find(peer_id);
3184 // A client should exist for all peers
3186 return n->getValue();
3189 std::wstring Server::getStatusString()
3191 std::wostringstream os(std::ios_base::binary);
3194 os<<L"uptime="<<m_uptime.get();
3195 // Information about clients
3197 for(core::map<u16, RemoteClient*>::Iterator
3198 i = m_clients.getIterator();
3199 i.atEnd() == false; i++)
3201 // Get client and check that it is valid
3202 RemoteClient *client = i.getNode()->getValue();
3203 assert(client->peer_id == i.getNode()->getKey());
3204 if(client->serialization_version == SER_FMT_VER_INVALID)
3207 Player *player = m_env.getPlayer(client->peer_id);
3208 // Get name of player
3209 std::wstring name = L"unknown";
3211 name = narrow_to_wide(player->getName());
3212 // Add name to information string
3216 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3217 os<<" WARNING: Map saving is disabled."<<std::endl;
3222 void setCreativeInventory(Player *player)
3224 player->resetInventory();
3226 // Give some good picks
3228 InventoryItem *item = new ToolItem("STPick", 0);
3229 void* r = player->inventory.addItem("main", item);
3233 InventoryItem *item = new ToolItem("MesePick", 0);
3234 void* r = player->inventory.addItem("main", item);
3241 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3244 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3245 player->inventory.addItem("main", item);
3248 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3250 // Skip some materials
3251 if(i == CONTENT_WATER || i == CONTENT_TORCH
3252 || i == CONTENT_COALSTONE)
3255 InventoryItem *item = new MaterialItem(i, 1);
3256 player->inventory.addItem("main", item);
3260 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3261 void* r = player->inventory.addItem("main", item);
3266 Player *Server::emergePlayer(const char *name, const char *password,
3270 Try to get an existing player
3272 Player *player = m_env.getPlayer(name);
3275 // If player is already connected, cancel
3276 if(player->peer_id != 0)
3278 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3283 player->peer_id = peer_id;
3285 // Reset inventory to creative if in creative mode
3286 if(g_settings.getBool("creative_mode"))
3288 setCreativeInventory(player);
3295 If player with the wanted peer_id already exists, cancel.
3297 if(m_env.getPlayer(peer_id) != NULL)
3299 dstream<<"emergePlayer(): Player with wrong name but same"
3300 " peer_id already exists"<<std::endl;
3308 player = new ServerRemotePlayer();
3309 //player->peer_id = c.peer_id;
3310 //player->peer_id = PEER_ID_INEXISTENT;
3311 player->peer_id = peer_id;
3312 player->updateName(name);
3318 dstream<<"Server: Finding spawn place for player \""
3319 <<player->getName()<<"\""<<std::endl;
3323 player->setPosition(intToFloat(v3s16(
3330 f32 groundheight = 0;
3332 // Try to find a good place a few times
3333 for(s32 i=0; i<500; i++)
3336 // We're going to try to throw the player to this position
3337 nodepos = v2s16(-range + (myrand()%(range*2)),
3338 -range + (myrand()%(range*2)));
3339 v2s16 sectorpos = getNodeSectorPos(nodepos);
3341 m_env.getMap().emergeSector(sectorpos);
3342 // Get ground height at point
3343 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3344 // The sector should have been generated -> groundheight exists
3345 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3346 // Don't go underwater
3347 if(groundheight < WATER_LEVEL)
3349 //dstream<<"-> Underwater"<<std::endl;
3352 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3353 // Get block at point
3355 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3356 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3357 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3358 // Don't go inside ground
3360 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3361 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3362 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3363 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3364 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3365 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3367 dstream<<"-> Inside ground"<<std::endl;
3371 }catch(InvalidPositionException &e)
3373 dstream<<"-> Invalid position"<<std::endl;
3374 // Ignore invalid position
3378 // Found a good place
3379 dstream<<"Searched through "<<i<<" places."<<std::endl;
3384 // If no suitable place was not found, go above water at least.
3385 if(groundheight < WATER_LEVEL)
3386 groundheight = WATER_LEVEL;
3388 player->setPosition(intToFloat(v3s16(
3397 Add player to environment
3400 m_env.addPlayer(player);
3403 Add stuff to inventory
3406 if(g_settings.getBool("creative_mode"))
3408 setCreativeInventory(player);
3413 InventoryItem *item = new ToolItem("WPick", 32000);
3414 void* r = player->inventory.addItem("main", item);
3418 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3419 void* r = player->inventory.addItem("main", item);
3423 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3424 void* r = player->inventory.addItem("main", item);
3428 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3429 void* r = player->inventory.addItem("main", item);
3433 InventoryItem *item = new CraftItem("Stick", 4);
3434 void* r = player->inventory.addItem("main", item);
3438 InventoryItem *item = new ToolItem("WPick", 32000);
3439 void* r = player->inventory.addItem("main", item);
3443 InventoryItem *item = new ToolItem("STPick", 32000);
3444 void* r = player->inventory.addItem("main", item);
3447 /*// Give some lights
3449 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3450 bool r = player->inventory.addItem("main", item);
3454 for(u16 i=0; i<4; i++)
3456 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3457 bool r = player->inventory.addItem("main", item);
3460 /*// Give some other stuff
3462 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3463 bool r = player->inventory.addItem("main", item);
3470 } // create new player
3474 void Server::UpdateBlockWaterPressure(MapBlock *block,
3475 core::map<v3s16, MapBlock*> &modified_blocks)
3477 MapVoxelManipulator v(&m_env.getMap());
3478 v.m_disable_water_climb =
3479 g_settings.getBool("disable_water_climb");
3481 VoxelArea area(block->getPosRelative(),
3482 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3486 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3488 catch(ProcessingLimitException &e)
3490 dstream<<"Processing limit reached (1)"<<std::endl;
3493 v.blitBack(modified_blocks);
3497 void Server::handlePeerChange(PeerChange &c)
3499 JMutexAutoLock envlock(m_env_mutex);
3500 JMutexAutoLock conlock(m_con_mutex);
3502 if(c.type == PEER_ADDED)
3509 core::map<u16, RemoteClient*>::Node *n;
3510 n = m_clients.find(c.peer_id);
3511 // The client shouldn't already exist
3515 RemoteClient *client = new RemoteClient();
3516 client->peer_id = c.peer_id;
3517 m_clients.insert(client->peer_id, client);
3520 else if(c.type == PEER_REMOVED)
3527 core::map<u16, RemoteClient*>::Node *n;
3528 n = m_clients.find(c.peer_id);
3529 // The client should exist
3532 // Collect information about leaving in chat
3533 std::wstring message;
3535 std::wstring name = L"unknown";
3536 Player *player = m_env.getPlayer(c.peer_id);
3538 name = narrow_to_wide(player->getName());
3542 message += L" left game";
3544 message += L" (timed out)";
3549 m_env.removePlayer(c.peer_id);
3552 // Set player client disconnected
3554 Player *player = m_env.getPlayer(c.peer_id);
3556 player->peer_id = 0;
3560 delete m_clients[c.peer_id];
3561 m_clients.remove(c.peer_id);
3563 // Send player info to all remaining clients
3566 // Send leave chat message to all remaining clients
3567 BroadcastChatMessage(message);
3576 void Server::handlePeerChanges()
3578 while(m_peer_change_queue.size() > 0)
3580 PeerChange c = m_peer_change_queue.pop_front();
3582 dout_server<<"Server: Handling peer change: "
3583 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3586 handlePeerChange(c);
3590 void dedicated_server_loop(Server &server, bool &kill)
3592 DSTACK(__FUNCTION_NAME);
3594 std::cout<<DTIME<<std::endl;
3595 std::cout<<"========================"<<std::endl;
3596 std::cout<<"Running dedicated server"<<std::endl;
3597 std::cout<<"========================"<<std::endl;
3598 std::cout<<std::endl;
3602 // This is kind of a hack but can be done like this
3603 // because server.step() is very light
3607 if(server.getShutdownRequested() || kill)
3609 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3613 static int counter = 0;
3619 core::list<PlayerInfo> list = server.getPlayerInfo();
3620 core::list<PlayerInfo>::Iterator i;
3621 static u32 sum_old = 0;
3622 u32 sum = PIChecksum(list);
3625 std::cout<<DTIME<<"Player info:"<<std::endl;
3626 for(i=list.begin(); i!=list.end(); i++)
3628 i->PrintLine(&std::cout);