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);
2101 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2102 SharedBuffer<u8> reply(replysize);
2103 writeU16(&reply[0], TOCLIENT_ADDNODE);
2104 writeS16(&reply[2], p_over.X);
2105 writeS16(&reply[4], p_over.Y);
2106 writeS16(&reply[6], p_over.Z);
2107 n.serialize(&reply[8], peer_ser_ver);
2109 m_con.SendToAll(0, reply, true);
2114 InventoryList *ilist = player->inventory.getList("main");
2115 if(g_settings.getBool("creative_mode") == false && ilist)
2117 // Remove from inventory and send inventory
2118 if(mitem->getCount() == 1)
2119 ilist->deleteItem(item_i);
2123 SendInventory(peer_id);
2129 This takes some time so it is done after the quick stuff
2131 core::map<v3s16, MapBlock*> modified_blocks;
2132 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2135 Calculate special events
2138 /*if(n.d == CONTENT_MESE)
2141 for(s16 z=-1; z<=1; z++)
2142 for(s16 y=-1; y<=1; y++)
2143 for(s16 x=-1; x<=1; x++)
2154 v3s16 blockpos = getNodeBlockPos(p_over);
2156 MapBlock *block = NULL;
2159 block = m_env.getMap().getBlockNoCreate(blockpos);
2161 catch(InvalidPositionException &e)
2163 derr_server<<"Error while placing object: "
2164 "block not found"<<std::endl;
2168 v3s16 block_pos_i_on_map = block->getPosRelative();
2169 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2171 v3f pos = intToFloat(p_over);
2172 pos -= block_pos_f_on_map;
2174 /*dout_server<<"pos="
2175 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2178 MapBlockObject *obj = NULL;
2181 Handle block object items
2183 if(std::string("MBOItem") == item->getName())
2185 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2187 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2188 "inventorystring=\""
2189 <<oitem->getInventoryString()
2190 <<"\""<<std::endl;*/
2192 obj = oitem->createObject
2193 (pos, player->getYaw(), player->getPitch());
2200 dout_server<<"Placing a miscellaneous item on map"
2203 Create an ItemObject that contains the item.
2205 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2206 std::ostringstream os(std::ios_base::binary);
2207 item->serialize(os);
2208 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2209 iobj->setItemString(os.str());
2215 derr_server<<"WARNING: item resulted in NULL object, "
2216 <<"not placing onto map"
2221 block->addObject(obj);
2223 dout_server<<"Placed object"<<std::endl;
2225 InventoryList *ilist = player->inventory.getList("main");
2226 if(g_settings.getBool("creative_mode") == false && ilist)
2228 // Remove from inventory and send inventory
2229 ilist->deleteItem(item_i);
2231 SendInventory(peer_id);
2239 Catch invalid actions
2243 derr_server<<"WARNING: Server: Invalid action "
2244 <<action<<std::endl;
2248 else if(command == TOSERVER_RELEASE)
2257 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2260 else if(command == TOSERVER_SIGNTEXT)
2269 std::string datastring((char*)&data[2], datasize-2);
2270 std::istringstream is(datastring, std::ios_base::binary);
2273 is.read((char*)buf, 6);
2274 v3s16 blockpos = readV3S16(buf);
2275 is.read((char*)buf, 2);
2276 s16 id = readS16(buf);
2277 is.read((char*)buf, 2);
2278 u16 textlen = readU16(buf);
2280 for(u16 i=0; i<textlen; i++)
2282 is.read((char*)buf, 1);
2283 text += (char)buf[0];
2286 MapBlock *block = NULL;
2289 block = m_env.getMap().getBlockNoCreate(blockpos);
2291 catch(InvalidPositionException &e)
2293 derr_server<<"Error while setting sign text: "
2294 "block not found"<<std::endl;
2298 MapBlockObject *obj = block->getObject(id);
2301 derr_server<<"Error while setting sign text: "
2302 "object not found"<<std::endl;
2306 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2308 derr_server<<"Error while setting sign text: "
2309 "object is not a sign"<<std::endl;
2313 ((SignObject*)obj)->setText(text);
2315 obj->getBlock()->setChangedFlag();
2317 else if(command == TOSERVER_INVENTORY_ACTION)
2319 /*// Ignore inventory changes if in creative mode
2320 if(g_settings.getBool("creative_mode") == true)
2322 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2326 // Strip command and create a stream
2327 std::string datastring((char*)&data[2], datasize-2);
2328 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2329 std::istringstream is(datastring, std::ios_base::binary);
2331 InventoryAction *a = InventoryAction::deSerialize(is);
2335 Handle craftresult specially if not in creative mode
2337 bool disable_action = false;
2338 if(a->getType() == IACTION_MOVE
2339 && g_settings.getBool("creative_mode") == false)
2341 IMoveAction *ma = (IMoveAction*)a;
2342 // Don't allow moving anything to craftresult
2343 if(ma->to_name == "craftresult")
2346 disable_action = true;
2348 // When something is removed from craftresult
2349 if(ma->from_name == "craftresult")
2351 disable_action = true;
2352 // Remove stuff from craft
2353 InventoryList *clist = player->inventory.getList("craft");
2356 u16 count = ma->count;
2359 clist->decrementMaterials(count);
2362 // Feed action to player inventory
2363 a->apply(&player->inventory);
2366 // If something appeared in craftresult, throw it
2368 InventoryList *rlist = player->inventory.getList("craftresult");
2369 InventoryList *mlist = player->inventory.getList("main");
2370 if(rlist && mlist && rlist->getUsedSlots() == 1)
2372 InventoryItem *item1 = rlist->changeItem(0, NULL);
2373 mlist->addItem(item1);
2377 if(disable_action == false)
2379 // Feed action to player inventory
2380 a->apply(&player->inventory);
2385 SendInventory(player->peer_id);
2389 dstream<<"TOSERVER_INVENTORY_ACTION: "
2390 <<"InventoryAction::deSerialize() returned NULL"
2394 else if(command == TOSERVER_CHAT_MESSAGE)
2402 std::string datastring((char*)&data[2], datasize-2);
2403 std::istringstream is(datastring, std::ios_base::binary);
2406 is.read((char*)buf, 2);
2407 u16 len = readU16(buf);
2409 std::wstring message;
2410 for(u16 i=0; i<len; i++)
2412 is.read((char*)buf, 2);
2413 message += (wchar_t)readU16(buf);
2416 // Get player name of this client
2417 std::wstring name = narrow_to_wide(player->getName());
2419 // Line to send to players
2421 // Whether to send to the player that sent the line
2422 bool send_to_sender = false;
2423 // Whether to send to other players
2424 bool send_to_others = false;
2427 std::wstring commandprefix = L"/#";
2428 if(message.substr(0, commandprefix.size()) == commandprefix)
2430 line += L"Server: ";
2432 message = message.substr(commandprefix.size());
2433 // Get player name as narrow string
2434 std::string name_s = player->getName();
2435 // Convert message to narrow string
2436 std::string message_s = wide_to_narrow(message);
2437 // Operator is the single name defined in config.
2438 std::string operator_name = g_settings.get("name");
2439 bool is_operator = (operator_name != "" &&
2440 wide_to_narrow(name) == operator_name);
2441 bool valid_command = false;
2442 if(message_s == "help")
2444 line += L"-!- Available commands: ";
2448 line += L"shutdown setting ";
2453 send_to_sender = true;
2454 valid_command = true;
2456 else if(message_s == "status")
2458 line = getStatusString();
2459 send_to_sender = true;
2460 valid_command = true;
2462 else if(is_operator)
2464 if(message_s == "shutdown")
2466 dstream<<DTIME<<" Server: Operator requested shutdown."
2468 m_shutdown_requested.set(true);
2470 line += L"*** Server shutting down (operator request)";
2471 send_to_sender = true;
2472 valid_command = true;
2474 else if(message_s.substr(0,8) == "setting ")
2476 std::string confline = message_s.substr(8);
2477 g_settings.parseConfigLine(confline);
2478 line += L"-!- Setting changed.";
2479 send_to_sender = true;
2480 valid_command = true;
2484 if(valid_command == false)
2486 line += L"-!- Invalid command: " + message;
2487 send_to_sender = true;
2498 send_to_others = true;
2503 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2506 Send the message to clients
2508 for(core::map<u16, RemoteClient*>::Iterator
2509 i = m_clients.getIterator();
2510 i.atEnd() == false; i++)
2512 // Get client and check that it is valid
2513 RemoteClient *client = i.getNode()->getValue();
2514 assert(client->peer_id == i.getNode()->getKey());
2515 if(client->serialization_version == SER_FMT_VER_INVALID)
2519 bool sender_selected = (peer_id == client->peer_id);
2520 if(sender_selected == true && send_to_sender == false)
2522 if(sender_selected == false && send_to_others == false)
2525 SendChatMessage(client->peer_id, line);
2531 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2532 "unknown command "<<command<<std::endl;
2536 catch(SendFailedException &e)
2538 derr_server<<"Server::ProcessData(): SendFailedException: "
2544 /*void Server::Send(u16 peer_id, u16 channelnum,
2545 SharedBuffer<u8> data, bool reliable)
2547 JMutexAutoLock lock(m_con_mutex);
2548 m_con.Send(peer_id, channelnum, data, reliable);
2551 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2553 DSTACK(__FUNCTION_NAME);
2555 Create a packet with the block in the right format
2558 std::ostringstream os(std::ios_base::binary);
2559 block->serialize(os, ver);
2560 std::string s = os.str();
2561 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2563 u32 replysize = 8 + blockdata.getSize();
2564 SharedBuffer<u8> reply(replysize);
2565 v3s16 p = block->getPos();
2566 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2567 writeS16(&reply[2], p.X);
2568 writeS16(&reply[4], p.Y);
2569 writeS16(&reply[6], p.Z);
2570 memcpy(&reply[8], *blockdata, blockdata.getSize());
2572 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2573 <<": \tpacket size: "<<replysize<<std::endl;*/
2578 m_con.Send(peer_id, 1, reply, true);
2581 core::list<PlayerInfo> Server::getPlayerInfo()
2583 DSTACK(__FUNCTION_NAME);
2584 JMutexAutoLock envlock(m_env_mutex);
2585 JMutexAutoLock conlock(m_con_mutex);
2587 core::list<PlayerInfo> list;
2589 core::list<Player*> players = m_env.getPlayers();
2591 core::list<Player*>::Iterator i;
2592 for(i = players.begin();
2593 i != players.end(); i++)
2597 Player *player = *i;
2600 con::Peer *peer = m_con.GetPeer(player->peer_id);
2601 // Copy info from peer to info struct
2603 info.address = peer->address;
2604 info.avg_rtt = peer->avg_rtt;
2606 catch(con::PeerNotFoundException &e)
2608 // Set dummy peer info
2610 info.address = Address(0,0,0,0,0);
2614 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2615 info.position = player->getPosition();
2617 list.push_back(info);
2624 void Server::peerAdded(con::Peer *peer)
2626 DSTACK(__FUNCTION_NAME);
2627 dout_server<<"Server::peerAdded(): peer->id="
2628 <<peer->id<<std::endl;
2631 c.type = PEER_ADDED;
2632 c.peer_id = peer->id;
2634 m_peer_change_queue.push_back(c);
2637 void Server::deletingPeer(con::Peer *peer, bool timeout)
2639 DSTACK(__FUNCTION_NAME);
2640 dout_server<<"Server::deletingPeer(): peer->id="
2641 <<peer->id<<", timeout="<<timeout<<std::endl;
2644 c.type = PEER_REMOVED;
2645 c.peer_id = peer->id;
2646 c.timeout = timeout;
2647 m_peer_change_queue.push_back(c);
2650 void Server::SendObjectData(float dtime)
2652 DSTACK(__FUNCTION_NAME);
2654 core::map<v3s16, bool> stepped_blocks;
2656 for(core::map<u16, RemoteClient*>::Iterator
2657 i = m_clients.getIterator();
2658 i.atEnd() == false; i++)
2660 u16 peer_id = i.getNode()->getKey();
2661 RemoteClient *client = i.getNode()->getValue();
2662 assert(client->peer_id == peer_id);
2664 if(client->serialization_version == SER_FMT_VER_INVALID)
2667 client->SendObjectData(this, dtime, stepped_blocks);
2671 void Server::SendPlayerInfos()
2673 DSTACK(__FUNCTION_NAME);
2675 //JMutexAutoLock envlock(m_env_mutex);
2677 // Get connected players
2678 core::list<Player*> players = m_env.getPlayers(true);
2680 u32 player_count = players.getSize();
2681 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2683 SharedBuffer<u8> data(datasize);
2684 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2687 core::list<Player*>::Iterator i;
2688 for(i = players.begin();
2689 i != players.end(); i++)
2691 Player *player = *i;
2693 /*dstream<<"Server sending player info for player with "
2694 "peer_id="<<player->peer_id<<std::endl;*/
2696 writeU16(&data[start], player->peer_id);
2697 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2698 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2699 start += 2+PLAYERNAME_SIZE;
2702 //JMutexAutoLock conlock(m_con_mutex);
2705 m_con.SendToAll(0, data, true);
2723 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2729 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2735 enum ItemSpecType type;
2736 // Only other one of these is used
2742 items: a pointer to an array of 9 pointers to items
2743 specs: a pointer to an array of 9 ItemSpecs
2745 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2747 u16 items_min_x = 100;
2748 u16 items_max_x = 100;
2749 u16 items_min_y = 100;
2750 u16 items_max_y = 100;
2751 for(u16 y=0; y<3; y++)
2752 for(u16 x=0; x<3; x++)
2754 if(items[y*3 + x] == NULL)
2756 if(items_min_x == 100 || x < items_min_x)
2758 if(items_min_y == 100 || y < items_min_y)
2760 if(items_max_x == 100 || x > items_max_x)
2762 if(items_max_y == 100 || y > items_max_y)
2765 // No items at all, just return false
2766 if(items_min_x == 100)
2769 u16 items_w = items_max_x - items_min_x + 1;
2770 u16 items_h = items_max_y - items_min_y + 1;
2772 u16 specs_min_x = 100;
2773 u16 specs_max_x = 100;
2774 u16 specs_min_y = 100;
2775 u16 specs_max_y = 100;
2776 for(u16 y=0; y<3; y++)
2777 for(u16 x=0; x<3; x++)
2779 if(specs[y*3 + x].type == ITEM_NONE)
2781 if(specs_min_x == 100 || x < specs_min_x)
2783 if(specs_min_y == 100 || y < specs_min_y)
2785 if(specs_max_x == 100 || x > specs_max_x)
2787 if(specs_max_y == 100 || y > specs_max_y)
2790 // No specs at all, just return false
2791 if(specs_min_x == 100)
2794 u16 specs_w = specs_max_x - specs_min_x + 1;
2795 u16 specs_h = specs_max_y - specs_min_y + 1;
2798 if(items_w != specs_w || items_h != specs_h)
2801 for(u16 y=0; y<specs_h; y++)
2802 for(u16 x=0; x<specs_w; x++)
2804 u16 items_x = items_min_x + x;
2805 u16 items_y = items_min_y + y;
2806 u16 specs_x = specs_min_x + x;
2807 u16 specs_y = specs_min_y + y;
2808 InventoryItem *item = items[items_y * 3 + items_x];
2809 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2811 if(spec.type == ITEM_NONE)
2813 // Has to be no item
2819 // There should be an item
2823 std::string itemname = item->getName();
2825 if(spec.type == ITEM_MATERIAL)
2827 if(itemname != "MaterialItem")
2829 MaterialItem *mitem = (MaterialItem*)item;
2830 if(mitem->getMaterial() != spec.num)
2833 else if(spec.type == ITEM_CRAFT)
2835 if(itemname != "CraftItem")
2837 CraftItem *mitem = (CraftItem*)item;
2838 if(mitem->getSubName() != spec.name)
2841 else if(spec.type == ITEM_TOOL)
2843 // Not supported yet
2846 else if(spec.type == ITEM_MBO)
2848 // Not supported yet
2853 // Not supported yet
2861 void Server::SendInventory(u16 peer_id)
2863 DSTACK(__FUNCTION_NAME);
2865 Player* player = m_env.getPlayer(peer_id);
2868 Calculate crafting stuff
2870 if(g_settings.getBool("creative_mode") == false)
2872 InventoryList *clist = player->inventory.getList("craft");
2873 InventoryList *rlist = player->inventory.getList("craftresult");
2876 rlist->clearItems();
2880 InventoryItem *items[9];
2881 for(u16 i=0; i<9; i++)
2883 items[i] = clist->getItem(i);
2892 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2893 if(checkItemCombination(items, specs))
2895 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2904 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2905 if(checkItemCombination(items, specs))
2907 rlist->addItem(new CraftItem("Stick", 4));
2916 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2917 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2918 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2919 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2920 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2921 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2922 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2923 if(checkItemCombination(items, specs))
2925 rlist->addItem(new MapBlockObjectItem("Sign"));
2934 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2935 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2936 if(checkItemCombination(items, specs))
2938 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2947 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2948 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2949 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2950 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2951 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2952 if(checkItemCombination(items, specs))
2954 rlist->addItem(new ToolItem("WPick", 0));
2963 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2964 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2965 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2966 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2967 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2968 if(checkItemCombination(items, specs))
2970 rlist->addItem(new ToolItem("STPick", 0));
2979 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2980 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2981 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2982 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2983 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2984 if(checkItemCombination(items, specs))
2986 rlist->addItem(new ToolItem("MesePick", 0));
2991 } // if creative_mode == false
2997 std::ostringstream os;
2998 //os.imbue(std::locale("C"));
3000 player->inventory.serialize(os);
3002 std::string s = os.str();
3004 SharedBuffer<u8> data(s.size()+2);
3005 writeU16(&data[0], TOCLIENT_INVENTORY);
3006 memcpy(&data[2], s.c_str(), s.size());
3009 m_con.Send(peer_id, 0, data, true);
3012 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3014 DSTACK(__FUNCTION_NAME);
3016 std::ostringstream os(std::ios_base::binary);
3020 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3021 os.write((char*)buf, 2);
3024 writeU16(buf, message.size());
3025 os.write((char*)buf, 2);
3028 for(u32 i=0; i<message.size(); i++)
3032 os.write((char*)buf, 2);
3036 std::string s = os.str();
3037 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3039 m_con.Send(peer_id, 0, data, true);
3042 void Server::BroadcastChatMessage(const std::wstring &message)
3044 for(core::map<u16, RemoteClient*>::Iterator
3045 i = m_clients.getIterator();
3046 i.atEnd() == false; i++)
3048 // Get client and check that it is valid
3049 RemoteClient *client = i.getNode()->getValue();
3050 assert(client->peer_id == i.getNode()->getKey());
3051 if(client->serialization_version == SER_FMT_VER_INVALID)
3054 SendChatMessage(client->peer_id, message);
3058 void Server::SendBlocks(float dtime)
3060 DSTACK(__FUNCTION_NAME);
3062 JMutexAutoLock envlock(m_env_mutex);
3064 //TimeTaker timer("Server::SendBlocks");
3066 core::array<PrioritySortedBlockTransfer> queue;
3068 s32 total_sending = 0;
3070 for(core::map<u16, RemoteClient*>::Iterator
3071 i = m_clients.getIterator();
3072 i.atEnd() == false; i++)
3074 RemoteClient *client = i.getNode()->getValue();
3075 assert(client->peer_id == i.getNode()->getKey());
3077 total_sending += client->SendingCount();
3079 if(client->serialization_version == SER_FMT_VER_INVALID)
3082 client->GetNextBlocks(this, dtime, queue);
3086 // Lowest priority number comes first.
3087 // Lowest is most important.
3090 JMutexAutoLock conlock(m_con_mutex);
3092 for(u32 i=0; i<queue.size(); i++)
3094 //TODO: Calculate limit dynamically
3095 if(total_sending >= g_settings.getS32
3096 ("max_simultaneous_block_sends_server_total"))
3099 PrioritySortedBlockTransfer q = queue[i];
3101 MapBlock *block = NULL;
3104 block = m_env.getMap().getBlockNoCreate(q.pos);
3106 catch(InvalidPositionException &e)
3111 RemoteClient *client = getClient(q.peer_id);
3113 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3115 client->SentBlock(q.pos);
3122 RemoteClient* Server::getClient(u16 peer_id)
3124 DSTACK(__FUNCTION_NAME);
3125 //JMutexAutoLock lock(m_con_mutex);
3126 core::map<u16, RemoteClient*>::Node *n;
3127 n = m_clients.find(peer_id);
3128 // A client should exist for all peers
3130 return n->getValue();
3133 std::wstring Server::getStatusString()
3135 std::wostringstream os(std::ios_base::binary);
3138 os<<L"uptime="<<m_uptime.get();
3139 // Information about clients
3141 for(core::map<u16, RemoteClient*>::Iterator
3142 i = m_clients.getIterator();
3143 i.atEnd() == false; i++)
3145 // Get client and check that it is valid
3146 RemoteClient *client = i.getNode()->getValue();
3147 assert(client->peer_id == i.getNode()->getKey());
3148 if(client->serialization_version == SER_FMT_VER_INVALID)
3151 Player *player = m_env.getPlayer(client->peer_id);
3152 // Get name of player
3153 std::wstring name = L"unknown";
3155 name = narrow_to_wide(player->getName());
3156 // Add name to information string
3160 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3161 os<<" WARNING: Map saving is disabled."<<std::endl;
3166 void setCreativeInventory(Player *player)
3168 player->resetInventory();
3170 // Give some good picks
3172 InventoryItem *item = new ToolItem("STPick", 0);
3173 void* r = player->inventory.addItem("main", item);
3177 InventoryItem *item = new ToolItem("MesePick", 0);
3178 void* r = player->inventory.addItem("main", item);
3185 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3188 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3189 player->inventory.addItem("main", item);
3192 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3194 // Skip some materials
3195 if(i == CONTENT_WATER || i == CONTENT_TORCH
3196 || i == CONTENT_COALSTONE)
3199 InventoryItem *item = new MaterialItem(i, 1);
3200 player->inventory.addItem("main", item);
3204 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3205 void* r = player->inventory.addItem("main", item);
3210 Player *Server::emergePlayer(const char *name, const char *password,
3214 Try to get an existing player
3216 Player *player = m_env.getPlayer(name);
3219 // If player is already connected, cancel
3220 if(player->peer_id != 0)
3222 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3227 player->peer_id = peer_id;
3229 // Reset inventory to creative if in creative mode
3230 if(g_settings.getBool("creative_mode"))
3232 setCreativeInventory(player);
3239 If player with the wanted peer_id already exists, cancel.
3241 if(m_env.getPlayer(peer_id) != NULL)
3243 dstream<<"emergePlayer(): Player with wrong name but same"
3244 " peer_id already exists"<<std::endl;
3252 player = new ServerRemotePlayer();
3253 //player->peer_id = c.peer_id;
3254 //player->peer_id = PEER_ID_INEXISTENT;
3255 player->peer_id = peer_id;
3256 player->updateName(name);
3262 dstream<<"Server: Finding spawn place for player \""
3263 <<player->getName()<<"\""<<std::endl;
3267 player->setPosition(intToFloat(v3s16(
3274 f32 groundheight = 0;
3276 // Try to find a good place a few times
3277 for(s32 i=0; i<500; i++)
3280 // We're going to try to throw the player to this position
3281 nodepos = v2s16(-range + (myrand()%(range*2)),
3282 -range + (myrand()%(range*2)));
3283 v2s16 sectorpos = getNodeSectorPos(nodepos);
3285 m_env.getMap().emergeSector(sectorpos);
3286 // Get ground height at point
3287 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3288 // The sector should have been generated -> groundheight exists
3289 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3290 // Don't go underwater
3291 if(groundheight < WATER_LEVEL)
3293 //dstream<<"-> Underwater"<<std::endl;
3296 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3297 // Get block at point
3299 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3300 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3301 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3302 // Don't go inside ground
3304 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3305 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3306 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3307 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3308 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3309 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3311 dstream<<"-> Inside ground"<<std::endl;
3315 }catch(InvalidPositionException &e)
3317 dstream<<"-> Invalid position"<<std::endl;
3318 // Ignore invalid position
3322 // Found a good place
3323 dstream<<"Searched through "<<i<<" places."<<std::endl;
3328 // If no suitable place was not found, go above water at least.
3329 if(groundheight < WATER_LEVEL)
3330 groundheight = WATER_LEVEL;
3332 player->setPosition(intToFloat(v3s16(
3341 Add player to environment
3344 m_env.addPlayer(player);
3347 Add stuff to inventory
3350 if(g_settings.getBool("creative_mode"))
3352 setCreativeInventory(player);
3357 InventoryItem *item = new ToolItem("WPick", 32000);
3358 void* r = player->inventory.addItem("main", item);
3362 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3363 void* r = player->inventory.addItem("main", item);
3367 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3368 void* r = player->inventory.addItem("main", item);
3372 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3373 void* r = player->inventory.addItem("main", item);
3377 InventoryItem *item = new CraftItem("Stick", 4);
3378 void* r = player->inventory.addItem("main", item);
3382 InventoryItem *item = new ToolItem("WPick", 32000);
3383 void* r = player->inventory.addItem("main", item);
3387 InventoryItem *item = new ToolItem("STPick", 32000);
3388 void* r = player->inventory.addItem("main", item);
3391 /*// Give some lights
3393 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3394 bool r = player->inventory.addItem("main", item);
3398 for(u16 i=0; i<4; i++)
3400 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3401 bool r = player->inventory.addItem("main", item);
3404 /*// Give some other stuff
3406 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3407 bool r = player->inventory.addItem("main", item);
3414 } // create new player
3418 void Server::UpdateBlockWaterPressure(MapBlock *block,
3419 core::map<v3s16, MapBlock*> &modified_blocks)
3421 MapVoxelManipulator v(&m_env.getMap());
3422 v.m_disable_water_climb =
3423 g_settings.getBool("disable_water_climb");
3425 VoxelArea area(block->getPosRelative(),
3426 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3430 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3432 catch(ProcessingLimitException &e)
3434 dstream<<"Processing limit reached (1)"<<std::endl;
3437 v.blitBack(modified_blocks);
3441 void Server::handlePeerChange(PeerChange &c)
3443 JMutexAutoLock envlock(m_env_mutex);
3444 JMutexAutoLock conlock(m_con_mutex);
3446 if(c.type == PEER_ADDED)
3453 core::map<u16, RemoteClient*>::Node *n;
3454 n = m_clients.find(c.peer_id);
3455 // The client shouldn't already exist
3459 RemoteClient *client = new RemoteClient();
3460 client->peer_id = c.peer_id;
3461 m_clients.insert(client->peer_id, client);
3464 else if(c.type == PEER_REMOVED)
3471 core::map<u16, RemoteClient*>::Node *n;
3472 n = m_clients.find(c.peer_id);
3473 // The client should exist
3476 // Collect information about leaving in chat
3477 std::wstring message;
3479 std::wstring name = L"unknown";
3480 Player *player = m_env.getPlayer(c.peer_id);
3482 name = narrow_to_wide(player->getName());
3486 message += L" left game";
3488 message += L" (timed out)";
3493 m_env.removePlayer(c.peer_id);
3496 // Set player client disconnected
3498 Player *player = m_env.getPlayer(c.peer_id);
3500 player->peer_id = 0;
3504 delete m_clients[c.peer_id];
3505 m_clients.remove(c.peer_id);
3507 // Send player info to all remaining clients
3510 // Send leave chat message to all remaining clients
3511 BroadcastChatMessage(message);
3520 void Server::handlePeerChanges()
3522 while(m_peer_change_queue.size() > 0)
3524 PeerChange c = m_peer_change_queue.pop_front();
3526 dout_server<<"Server: Handling peer change: "
3527 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3530 handlePeerChange(c);
3534 void dedicated_server_loop(Server &server, bool &kill)
3536 DSTACK(__FUNCTION_NAME);
3538 std::cout<<DTIME<<std::endl;
3539 std::cout<<"========================"<<std::endl;
3540 std::cout<<"Running dedicated server"<<std::endl;
3541 std::cout<<"========================"<<std::endl;
3542 std::cout<<std::endl;
3546 // This is kind of a hack but can be done like this
3547 // because server.step() is very light
3551 if(server.getShutdownRequested() || kill)
3553 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3557 static int counter = 0;
3563 core::list<PlayerInfo> list = server.getPlayerInfo();
3564 core::list<PlayerInfo>::Iterator i;
3565 static u32 sum_old = 0;
3566 u32 sum = PIChecksum(list);
3569 std::cout<<DTIME<<"Player info:"<<std::endl;
3570 for(i=list.begin(); i!=list.end(); i++)
3572 i->PrintLine(&std::cout);