3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
34 void * ServerThread::Thread()
38 DSTACK(__FUNCTION_NAME);
40 BEGIN_DEBUG_EXCEPTION_HANDLER
45 //TimeTaker timer("AsyncRunStep() + Receive()");
48 //TimeTaker timer("AsyncRunStep()");
49 m_server->AsyncRunStep();
52 //dout_server<<"Running m_server->Receive()"<<std::endl;
55 catch(con::NoIncomingDataException &e)
58 catch(con::PeerNotFoundException &e)
60 dout_server<<"Server: PeerNotFoundException"<<std::endl;
64 END_DEBUG_EXCEPTION_HANDLER
69 void * EmergeThread::Thread()
73 DSTACK(__FUNCTION_NAME);
77 BEGIN_DEBUG_EXCEPTION_HANDLER
80 Get block info from queue, emerge them and send them
83 After queue is empty, exit.
87 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
91 SharedPtr<QueuedBlockEmerge> q(qptr);
95 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
97 //TimeTaker timer("block emerge");
100 Try to emerge it from somewhere.
102 If it is only wanted as optional, only loading from disk
107 Check if any peer wants it as non-optional. In that case it
110 Also decrement the emerge queue count in clients.
113 bool optional = true;
116 core::map<u16, u8>::Iterator i;
117 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
119 //u16 peer_id = i.getNode()->getKey();
122 u8 flags = i.getNode()->getValue();
123 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
129 /*dstream<<"EmergeThread: p="
130 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
131 <<"optional="<<optional<<std::endl;*/
133 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
135 core::map<v3s16, MapBlock*> changed_blocks;
136 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
138 MapBlock *block = NULL;
139 bool got_block = true;
140 core::map<v3s16, MapBlock*> modified_blocks;
144 //TimeTaker envlockwaittimer("block emerge envlock wait time");
147 JMutexAutoLock envlock(m_server->m_env_mutex);
149 //envlockwaittimer.stop();
151 //TimeTaker timer("block emerge (while env locked)");
154 bool only_from_disk = false;
157 only_from_disk = true;
159 // First check if the block already exists
160 //block = map.getBlockNoCreate(p);
164 //dstream<<"Calling emergeBlock"<<std::endl;
165 block = map.emergeBlock(
169 lighting_invalidated_blocks);
172 // If it is a dummy, block was not found on disk
175 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
178 if(only_from_disk == false)
180 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
185 catch(InvalidPositionException &e)
188 // This happens when position is over limit.
194 if(debug && changed_blocks.size() > 0)
196 dout_server<<DTIME<<"Got changed_blocks: ";
197 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
198 i.atEnd() == false; i++)
200 MapBlock *block = i.getNode()->getValue();
201 v3s16 p = block->getPos();
202 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
204 dout_server<<std::endl;
208 Collect a list of blocks that have been modified in
209 addition to the fetched one.
212 // Add all the "changed blocks" to modified_blocks
213 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
214 i.atEnd() == false; i++)
216 MapBlock *block = i.getNode()->getValue();
217 modified_blocks.insert(block->getPos(), block);
220 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
221 <<" blocks"<<std::endl;*/
223 //TimeTaker timer("** updateLighting");
225 // Update lighting without locking the environment mutex,
226 // add modified blocks to changed blocks
227 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
229 // If we got no block, there should be no invalidated blocks
232 assert(lighting_invalidated_blocks.size() == 0);
238 Set sent status of modified blocks on clients
241 // NOTE: Server's clients are also behind the connection mutex
242 JMutexAutoLock lock(m_server->m_con_mutex);
245 Add the originally fetched block to the modified list
249 modified_blocks.insert(p, block);
253 Set the modified blocks unsent for all the clients
256 for(core::map<u16, RemoteClient*>::Iterator
257 i = m_server->m_clients.getIterator();
258 i.atEnd() == false; i++)
260 RemoteClient *client = i.getNode()->getValue();
262 if(modified_blocks.size() > 0)
264 // Remove block from sent history
265 client->SetBlocksNotSent(modified_blocks);
271 END_DEBUG_EXCEPTION_HANDLER
276 void RemoteClient::GetNextBlocks(Server *server, float dtime,
277 core::array<PrioritySortedBlockTransfer> &dest)
279 DSTACK(__FUNCTION_NAME);
282 m_nearest_unsent_reset_timer += dtime;
284 // Won't send anything if already sending
285 if(m_blocks_sending.size() >= g_settings.getU16
286 ("max_simultaneous_block_sends_per_client"))
288 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
292 Player *player = server->m_env.getPlayer(peer_id);
294 assert(player != NULL);
296 v3f playerpos = player->getPosition();
297 v3f playerspeed = player->getSpeed();
299 v3s16 center_nodepos = floatToInt(playerpos, BS);
301 v3s16 center = getNodeBlockPos(center_nodepos);
303 // Camera position and direction
305 playerpos + v3f(0, BS+BS/2, 0);
306 v3f camera_dir = v3f(0,0,1);
307 camera_dir.rotateYZBy(player->getPitch());
308 camera_dir.rotateXZBy(player->getYaw());
311 Get the starting value of the block finder radius.
313 s16 last_nearest_unsent_d;
316 if(m_last_center != center)
318 m_nearest_unsent_d = 0;
319 m_last_center = center;
322 /*dstream<<"m_nearest_unsent_reset_timer="
323 <<m_nearest_unsent_reset_timer<<std::endl;*/
324 if(m_nearest_unsent_reset_timer > 5.0)
326 m_nearest_unsent_reset_timer = 0;
327 m_nearest_unsent_d = 0;
328 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
331 last_nearest_unsent_d = m_nearest_unsent_d;
333 d_start = m_nearest_unsent_d;
335 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
336 ("max_simultaneous_block_sends_per_client");
337 u16 maximum_simultaneous_block_sends =
338 maximum_simultaneous_block_sends_setting;
341 Check the time from last addNode/removeNode.
343 Decrease send rate if player is building stuff.
345 m_time_from_building += dtime;
346 if(m_time_from_building < g_settings.getFloat(
347 "full_block_send_enable_min_time_from_building"))
349 maximum_simultaneous_block_sends
350 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
353 u32 num_blocks_selected = m_blocks_sending.size();
356 next time d will be continued from the d from which the nearest
357 unsent block was found this time.
359 This is because not necessarily any of the blocks found this
360 time are actually sent.
362 s32 new_nearest_unsent_d = -1;
364 s16 d_max = g_settings.getS16("max_block_send_distance");
365 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
367 //dstream<<"Starting from "<<d_start<<std::endl;
369 for(s16 d = d_start; d <= d_max; d++)
371 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
374 If m_nearest_unsent_d was changed by the EmergeThread
375 (it can change it to 0 through SetBlockNotSent),
377 Else update m_nearest_unsent_d
379 if(m_nearest_unsent_d != last_nearest_unsent_d)
381 d = m_nearest_unsent_d;
382 last_nearest_unsent_d = m_nearest_unsent_d;
386 Get the border/face dot coordinates of a "d-radiused"
389 core::list<v3s16> list;
390 getFacePositions(list, d);
392 core::list<v3s16>::Iterator li;
393 for(li=list.begin(); li!=list.end(); li++)
395 v3s16 p = *li + center;
399 - Don't allow too many simultaneous transfers
400 - EXCEPT when the blocks are very close
402 Also, don't send blocks that are already flying.
405 u16 maximum_simultaneous_block_sends_now =
406 maximum_simultaneous_block_sends;
408 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
410 maximum_simultaneous_block_sends_now =
411 maximum_simultaneous_block_sends_setting;
414 // Limit is dynamically lowered when building
415 if(num_blocks_selected
416 >= maximum_simultaneous_block_sends_now)
418 /*dstream<<"Not sending more blocks. Queue full. "
419 <<m_blocks_sending.size()
424 if(m_blocks_sending.find(p) != NULL)
430 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
431 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
432 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
433 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
434 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
435 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
438 // If this is true, inexistent block will be made from scratch
439 bool generate = d <= d_max_gen;
442 /*// Limit the generating area vertically to 2/3
443 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
446 // Limit the send area vertically to 2/3
447 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
453 If block is far away, don't generate it unless it is
456 NOTE: We can't know the ground level this way with the
462 MapSector *sector = NULL;
465 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
467 catch(InvalidPositionException &e)
473 // Get center ground height in nodes
474 f32 gh = sector->getGroundHeight(
475 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
476 // Block center y in nodes
477 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
478 // If differs a lot, don't generate
479 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
486 Don't generate or send if not in sight
489 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
495 Don't send already sent blocks
498 if(m_blocks_sent.find(p) != NULL)
503 Check if map has this block
505 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
507 bool surely_not_found_on_disk = false;
508 bool block_is_invalid = false;
513 surely_not_found_on_disk = true;
516 if(block->isValid() == false)
518 block_is_invalid = true;
522 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
523 v2s16 chunkpos = map->sector_to_chunk(p2d);
524 if(map->chunkNonVolatile(chunkpos) == false)
525 block_is_invalid = true;
529 If block has been marked to not exist on disk (dummy)
530 and generating new ones is not wanted, skip block.
532 if(generate == false && surely_not_found_on_disk == true)
539 Record the lowest d from which a a block has been
540 found being not sent and possibly to exist
542 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
544 new_nearest_unsent_d = d;
548 Add inexistent block to emerge queue.
550 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
552 //TODO: Get value from somewhere
553 // Allow only one block in emerge queue
554 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
556 //dstream<<"Adding block to emerge queue"<<std::endl;
558 // Add it to the emerge queue and trigger the thread
561 if(generate == false)
562 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
564 server->m_emerge_queue.addBlock(peer_id, p, flags);
565 server->m_emergethread.trigger();
573 Add block to send queue
576 PrioritySortedBlockTransfer q((float)d, p, peer_id);
580 num_blocks_selected += 1;
585 if(new_nearest_unsent_d != -1)
587 m_nearest_unsent_d = new_nearest_unsent_d;
591 void RemoteClient::SendObjectData(
594 core::map<v3s16, bool> &stepped_blocks
597 DSTACK(__FUNCTION_NAME);
599 // Can't send anything without knowing version
600 if(serialization_version == SER_FMT_VER_INVALID)
602 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
608 Send a TOCLIENT_OBJECTDATA packet.
612 u16 number of player positions
623 std::ostringstream os(std::ios_base::binary);
627 writeU16(buf, TOCLIENT_OBJECTDATA);
628 os.write((char*)buf, 2);
631 Get and write player data
634 // Get connected players
635 core::list<Player*> players = server->m_env.getPlayers(true);
637 // Write player count
638 u16 playercount = players.size();
639 writeU16(buf, playercount);
640 os.write((char*)buf, 2);
642 core::list<Player*>::Iterator i;
643 for(i = players.begin();
644 i != players.end(); i++)
648 v3f pf = player->getPosition();
649 v3f sf = player->getSpeed();
651 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
652 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
653 s32 pitch_i (player->getPitch() * 100);
654 s32 yaw_i (player->getYaw() * 100);
656 writeU16(buf, player->peer_id);
657 os.write((char*)buf, 2);
658 writeV3S32(buf, position_i);
659 os.write((char*)buf, 12);
660 writeV3S32(buf, speed_i);
661 os.write((char*)buf, 12);
662 writeS32(buf, pitch_i);
663 os.write((char*)buf, 4);
664 writeS32(buf, yaw_i);
665 os.write((char*)buf, 4);
669 Get and write object data
675 For making players to be able to build to their nearby
676 environment (building is not possible on blocks that are not
679 - Add blocks to emerge queue if they are not found
681 SUGGESTION: These could be ignored from the backside of the player
684 Player *player = server->m_env.getPlayer(peer_id);
688 v3f playerpos = player->getPosition();
689 v3f playerspeed = player->getSpeed();
691 v3s16 center_nodepos = floatToInt(playerpos, BS);
692 v3s16 center = getNodeBlockPos(center_nodepos);
694 s16 d_max = g_settings.getS16("active_object_range");
696 // Number of blocks whose objects were written to bos
699 std::ostringstream bos(std::ios_base::binary);
701 for(s16 d = 0; d <= d_max; d++)
703 core::list<v3s16> list;
704 getFacePositions(list, d);
706 core::list<v3s16>::Iterator li;
707 for(li=list.begin(); li!=list.end(); li++)
709 v3s16 p = *li + center;
712 Ignore blocks that haven't been sent to the client
715 if(m_blocks_sent.find(p) == NULL)
719 // Try stepping block and add it to a send queue
724 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
727 Step block if not in stepped_blocks and add to stepped_blocks.
729 if(stepped_blocks.find(p) == NULL)
731 block->stepObjects(dtime, true, server->getDayNightRatio());
732 stepped_blocks.insert(p, true);
733 block->setChangedFlag();
736 // Skip block if there are no objects
737 if(block->getObjectCount() == 0)
746 bos.write((char*)buf, 6);
749 block->serializeObjects(bos, serialization_version);
754 Stop collecting objects if data is already too big
756 // Sum of player and object data sizes
757 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
758 // break out if data too big
759 if(sum > MAX_OBJECTDATA_SIZE)
761 goto skip_subsequent;
765 catch(InvalidPositionException &e)
768 // Add it to the emerge queue and trigger the thread.
769 // Fetch the block only if it is on disk.
771 // Grab and increment counter
772 /*SharedPtr<JMutexAutoLock> lock
773 (m_num_blocks_in_emerge_queue.getLock());
774 m_num_blocks_in_emerge_queue.m_value++;*/
776 // Add to queue as an anonymous fetch from disk
777 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
778 server->m_emerge_queue.addBlock(0, p, flags);
779 server->m_emergethread.trigger();
787 writeU16(buf, blockcount);
788 os.write((char*)buf, 2);
790 // Write block objects
797 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
800 std::string s = os.str();
801 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
802 // Send as unreliable
803 server->m_con.Send(peer_id, 0, data, false);
806 void RemoteClient::GotBlock(v3s16 p)
808 if(m_blocks_sending.find(p) != NULL)
809 m_blocks_sending.remove(p);
812 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
813 " m_blocks_sending"<<std::endl;*/
814 m_excess_gotblocks++;
816 m_blocks_sent.insert(p, true);
819 void RemoteClient::SentBlock(v3s16 p)
821 if(m_blocks_sending.find(p) == NULL)
822 m_blocks_sending.insert(p, 0.0);
824 dstream<<"RemoteClient::SentBlock(): Sent block"
825 " already in m_blocks_sending"<<std::endl;
828 void RemoteClient::SetBlockNotSent(v3s16 p)
830 m_nearest_unsent_d = 0;
832 if(m_blocks_sending.find(p) != NULL)
833 m_blocks_sending.remove(p);
834 if(m_blocks_sent.find(p) != NULL)
835 m_blocks_sent.remove(p);
838 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
840 m_nearest_unsent_d = 0;
842 for(core::map<v3s16, MapBlock*>::Iterator
843 i = blocks.getIterator();
844 i.atEnd()==false; i++)
846 v3s16 p = i.getNode()->getKey();
848 if(m_blocks_sending.find(p) != NULL)
849 m_blocks_sending.remove(p);
850 if(m_blocks_sent.find(p) != NULL)
851 m_blocks_sent.remove(p);
859 PlayerInfo::PlayerInfo()
864 void PlayerInfo::PrintLine(std::ostream *s)
867 (*s)<<"\""<<name<<"\" ("
868 <<(position.X/10)<<","<<(position.Y/10)
869 <<","<<(position.Z/10)<<") ";
871 (*s)<<" avg_rtt="<<avg_rtt;
875 u32 PIChecksum(core::list<PlayerInfo> &l)
877 core::list<PlayerInfo>::Iterator i;
880 for(i=l.begin(); i!=l.end(); i++)
882 checksum += a * (i->id+1);
883 checksum ^= 0x435aafcd;
894 std::string mapsavedir
896 m_env(new ServerMap(mapsavedir)),
897 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
899 m_emergethread(this),
902 m_time_of_day_send_timer(0),
904 m_mapsavedir(mapsavedir),
905 m_shutdown_requested(false)
907 //m_flowwater_timer = 0.0;
908 m_liquid_transform_timer = 0.0;
909 m_print_info_timer = 0.0;
910 m_objectdata_timer = 0.0;
911 m_emergethread_trigger_timer = 0.0;
912 m_savemap_timer = 0.0;
916 m_step_dtime_mutex.Init();
920 m_env.deSerializePlayers(m_mapsavedir);
926 Send shutdown message
929 JMutexAutoLock conlock(m_con_mutex);
931 std::wstring line = L"*** Server shutting down";
934 Send the message to clients
936 for(core::map<u16, RemoteClient*>::Iterator
937 i = m_clients.getIterator();
938 i.atEnd() == false; i++)
940 // Get client and check that it is valid
941 RemoteClient *client = i.getNode()->getValue();
942 assert(client->peer_id == i.getNode()->getKey());
943 if(client->serialization_version == SER_FMT_VER_INVALID)
946 SendChatMessage(client->peer_id, line);
953 m_env.serializePlayers(m_mapsavedir);
964 JMutexAutoLock clientslock(m_con_mutex);
966 for(core::map<u16, RemoteClient*>::Iterator
967 i = m_clients.getIterator();
968 i.atEnd() == false; i++)
971 // NOTE: These are removed by env destructor
973 u16 peer_id = i.getNode()->getKey();
974 JMutexAutoLock envlock(m_env_mutex);
975 m_env.removePlayer(peer_id);
979 delete i.getNode()->getValue();
984 void Server::start(unsigned short port)
986 DSTACK(__FUNCTION_NAME);
987 // Stop thread if already running
990 // Initialize connection
991 m_con.setTimeoutMs(30);
995 m_thread.setRun(true);
998 dout_server<<"Server: Started on port "<<port<<std::endl;
1003 DSTACK(__FUNCTION_NAME);
1005 // Stop threads (set run=false first so both start stopping)
1006 m_thread.setRun(false);
1007 m_emergethread.setRun(false);
1009 m_emergethread.stop();
1011 dout_server<<"Server: Threads stopped"<<std::endl;
1013 dout_server<<"Server: Saving players"<<std::endl;
1015 // FIXME: Apparently this does not do anything here
1016 //m_env.serializePlayers(m_mapsavedir);
1019 void Server::step(float dtime)
1021 DSTACK(__FUNCTION_NAME);
1026 JMutexAutoLock lock(m_step_dtime_mutex);
1027 m_step_dtime += dtime;
1031 void Server::AsyncRunStep()
1033 DSTACK(__FUNCTION_NAME);
1037 JMutexAutoLock lock1(m_step_dtime_mutex);
1038 dtime = m_step_dtime;
1041 // Send blocks to clients
1047 //dstream<<"Server steps "<<dtime<<std::endl;
1048 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1051 JMutexAutoLock lock1(m_step_dtime_mutex);
1052 m_step_dtime -= dtime;
1059 m_uptime.set(m_uptime.get() + dtime);
1063 Update m_time_of_day
1066 m_time_counter += dtime;
1067 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1068 u32 units = (u32)(m_time_counter*speed);
1069 m_time_counter -= (f32)units / speed;
1070 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1072 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1075 Send to clients at constant intervals
1078 m_time_of_day_send_timer -= dtime;
1079 if(m_time_of_day_send_timer < 0.0)
1081 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1083 //JMutexAutoLock envlock(m_env_mutex);
1084 JMutexAutoLock conlock(m_con_mutex);
1086 for(core::map<u16, RemoteClient*>::Iterator
1087 i = m_clients.getIterator();
1088 i.atEnd() == false; i++)
1090 RemoteClient *client = i.getNode()->getValue();
1091 //Player *player = m_env.getPlayer(client->peer_id);
1093 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1094 m_time_of_day.get());
1096 m_con.Send(client->peer_id, 0, data, true);
1102 // Process connection's timeouts
1103 JMutexAutoLock lock2(m_con_mutex);
1104 m_con.RunTimeouts(dtime);
1108 // This has to be called so that the client list gets synced
1109 // with the peer list of the connection
1110 handlePeerChanges();
1115 // This also runs Map's timers
1116 JMutexAutoLock lock(m_env_mutex);
1127 m_liquid_transform_timer += dtime;
1128 if(m_liquid_transform_timer >= 1.00)
1130 m_liquid_transform_timer -= 1.00;
1132 JMutexAutoLock lock(m_env_mutex);
1134 core::map<v3s16, MapBlock*> modified_blocks;
1135 m_env.getMap().transformLiquids(modified_blocks);
1140 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1141 ServerMap &map = ((ServerMap&)m_env.getMap());
1142 map.updateLighting(modified_blocks, lighting_modified_blocks);
1144 // Add blocks modified by lighting to modified_blocks
1145 for(core::map<v3s16, MapBlock*>::Iterator
1146 i = lighting_modified_blocks.getIterator();
1147 i.atEnd() == false; i++)
1149 MapBlock *block = i.getNode()->getValue();
1150 modified_blocks.insert(block->getPos(), block);
1154 Set the modified blocks unsent for all the clients
1157 JMutexAutoLock lock2(m_con_mutex);
1159 for(core::map<u16, RemoteClient*>::Iterator
1160 i = m_clients.getIterator();
1161 i.atEnd() == false; i++)
1163 RemoteClient *client = i.getNode()->getValue();
1165 if(modified_blocks.size() > 0)
1167 // Remove block from sent history
1168 client->SetBlocksNotSent(modified_blocks);
1173 // Periodically print some info
1175 float &counter = m_print_info_timer;
1181 JMutexAutoLock lock2(m_con_mutex);
1183 for(core::map<u16, RemoteClient*>::Iterator
1184 i = m_clients.getIterator();
1185 i.atEnd() == false; i++)
1187 //u16 peer_id = i.getNode()->getKey();
1188 RemoteClient *client = i.getNode()->getValue();
1189 client->PrintInfo(std::cout);
1195 Check added and deleted active objects
1198 JMutexAutoLock envlock(m_env_mutex);
1199 JMutexAutoLock conlock(m_con_mutex);
1201 for(core::map<u16, RemoteClient*>::Iterator
1202 i = m_clients.getIterator();
1203 i.atEnd() == false; i++)
1205 RemoteClient *client = i.getNode()->getValue();
1206 Player *player = m_env.getPlayer(client->peer_id);
1207 v3s16 pos = floatToInt(player->getPosition(), BS);
1210 core::map<u16, bool> removed_objects;
1211 core::map<u16, bool> added_objects;
1212 m_env.getRemovedActiveObjects(pos, radius,
1213 client->m_known_objects, removed_objects);
1214 m_env.getAddedActiveObjects(pos, radius,
1215 client->m_known_objects, added_objects);
1217 // Ignore if nothing happened
1218 if(removed_objects.size() == 0 && added_objects.size() == 0)
1221 std::string data_buffer;
1225 // Handle removed objects
1226 writeU16((u8*)buf, removed_objects.size());
1227 data_buffer.append(buf, 2);
1228 for(core::map<u16, bool>::Iterator
1229 i = removed_objects.getIterator();
1230 i.atEnd()==false; i++)
1233 u16 id = i.getNode()->getKey();
1234 ServerActiveObject* obj = m_env.getActiveObject(id);
1236 // Add to data buffer for sending
1237 writeU16((u8*)buf, i.getNode()->getKey());
1238 data_buffer.append(buf, 2);
1240 // Remove from known objects
1241 client->m_known_objects.remove(i.getNode()->getKey());
1243 if(obj && obj->m_known_by_count > 0)
1244 obj->m_known_by_count--;
1247 // Handle added objects
1248 writeU16((u8*)buf, added_objects.size());
1249 data_buffer.append(buf, 2);
1250 for(core::map<u16, bool>::Iterator
1251 i = added_objects.getIterator();
1252 i.atEnd()==false; i++)
1255 u16 id = i.getNode()->getKey();
1256 ServerActiveObject* obj = m_env.getActiveObject(id);
1259 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1261 dstream<<"WARNING: "<<__FUNCTION_NAME
1262 <<": NULL object"<<std::endl;
1264 type = obj->getType();
1266 // Add to data buffer for sending
1267 writeU16((u8*)buf, id);
1268 data_buffer.append(buf, 2);
1269 writeU8((u8*)buf, type);
1270 data_buffer.append(buf, 1);
1272 data_buffer.append(serializeLongString(
1273 obj->getClientInitializationData()));
1275 // Add to known objects
1276 client->m_known_objects.insert(i.getNode()->getKey(), false);
1279 obj->m_known_by_count++;
1283 SharedBuffer<u8> reply(2 + data_buffer.size());
1284 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1285 memcpy((char*)&reply[2], data_buffer.c_str(),
1286 data_buffer.size());
1288 m_con.Send(client->peer_id, 0, reply, true);
1290 dstream<<"INFO: Server: Sent object remove/add: "
1291 <<removed_objects.size()<<" removed, "
1292 <<added_objects.size()<<" added, "
1293 <<"packet size is "<<reply.getSize()<<std::endl;
1298 Send object messages
1301 JMutexAutoLock envlock(m_env_mutex);
1302 JMutexAutoLock conlock(m_con_mutex);
1305 // Value = data sent by object
1306 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1308 // Get active object messages from environment
1311 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1315 core::list<ActiveObjectMessage>* message_list = NULL;
1316 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1317 n = buffered_messages.find(aom.id);
1320 message_list = new core::list<ActiveObjectMessage>;
1321 buffered_messages.insert(aom.id, message_list);
1325 message_list = n->getValue();
1327 message_list->push_back(aom);
1330 // Route data to every client
1331 for(core::map<u16, RemoteClient*>::Iterator
1332 i = m_clients.getIterator();
1333 i.atEnd()==false; i++)
1335 RemoteClient *client = i.getNode()->getValue();
1336 std::string reliable_data;
1337 std::string unreliable_data;
1338 // Go through all objects in message buffer
1339 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1340 j = buffered_messages.getIterator();
1341 j.atEnd()==false; j++)
1343 // If object is not known by client, skip it
1344 u16 id = j.getNode()->getKey();
1345 if(client->m_known_objects.find(id) == NULL)
1347 // Get message list of object
1348 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1349 // Go through every message
1350 for(core::list<ActiveObjectMessage>::Iterator
1351 k = list->begin(); k != list->end(); k++)
1353 // Compose the full new data with header
1354 ActiveObjectMessage aom = *k;
1355 std::string new_data;
1358 writeU16((u8*)&buf[0], aom.id);
1359 new_data.append(buf, 2);
1361 new_data += serializeString(aom.datastring);
1362 // Add data to buffer
1364 reliable_data += new_data;
1366 unreliable_data += new_data;
1370 reliable_data and unreliable_data are now ready.
1373 if(reliable_data.size() > 0)
1375 SharedBuffer<u8> reply(2 + reliable_data.size());
1376 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1377 memcpy((char*)&reply[2], reliable_data.c_str(),
1378 reliable_data.size());
1380 m_con.Send(client->peer_id, 0, reply, true);
1382 if(unreliable_data.size() > 0)
1384 SharedBuffer<u8> reply(2 + unreliable_data.size());
1385 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1386 memcpy((char*)&reply[2], unreliable_data.c_str(),
1387 unreliable_data.size());
1388 // Send as unreliable
1389 m_con.Send(client->peer_id, 0, reply, false);
1392 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1394 dstream<<"INFO: Server: Size of object message data: "
1395 <<"reliable: "<<reliable_data.size()
1396 <<", unreliable: "<<unreliable_data.size()
1401 // Clear buffered_messages
1402 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1403 i = buffered_messages.getIterator();
1404 i.atEnd()==false; i++)
1406 delete i.getNode()->getValue();
1411 Send object positions
1414 float &counter = m_objectdata_timer;
1416 if(counter >= g_settings.getFloat("objectdata_interval"))
1418 JMutexAutoLock lock1(m_env_mutex);
1419 JMutexAutoLock lock2(m_con_mutex);
1420 SendObjectData(counter);
1427 Trigger emergethread (it somehow gets to a non-triggered but
1428 bysy state sometimes)
1431 float &counter = m_emergethread_trigger_timer;
1437 m_emergethread.trigger();
1443 float &counter = m_savemap_timer;
1445 if(counter >= g_settings.getFloat("server_map_save_interval"))
1449 JMutexAutoLock lock(m_env_mutex);
1451 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1453 // Save only changed parts
1454 m_env.getMap().save(true);
1456 // Delete unused sectors
1457 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1458 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1459 if(deleted_count > 0)
1461 dout_server<<"Server: Unloaded "<<deleted_count
1462 <<" sectors from memory"<<std::endl;
1466 m_env.serializePlayers(m_mapsavedir);
1472 void Server::Receive()
1474 DSTACK(__FUNCTION_NAME);
1475 u32 data_maxsize = 10000;
1476 Buffer<u8> data(data_maxsize);
1481 JMutexAutoLock conlock(m_con_mutex);
1482 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1485 // This has to be called so that the client list gets synced
1486 // with the peer list of the connection
1487 handlePeerChanges();
1489 ProcessData(*data, datasize, peer_id);
1491 catch(con::InvalidIncomingDataException &e)
1493 derr_server<<"Server::Receive(): "
1494 "InvalidIncomingDataException: what()="
1495 <<e.what()<<std::endl;
1497 catch(con::PeerNotFoundException &e)
1499 //NOTE: This is not needed anymore
1501 // The peer has been disconnected.
1502 // Find the associated player and remove it.
1504 /*JMutexAutoLock envlock(m_env_mutex);
1506 dout_server<<"ServerThread: peer_id="<<peer_id
1507 <<" has apparently closed connection. "
1508 <<"Removing player."<<std::endl;
1510 m_env.removePlayer(peer_id);*/
1514 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1516 DSTACK(__FUNCTION_NAME);
1517 // Environment is locked first.
1518 JMutexAutoLock envlock(m_env_mutex);
1519 JMutexAutoLock conlock(m_con_mutex);
1523 peer = m_con.GetPeer(peer_id);
1525 catch(con::PeerNotFoundException &e)
1527 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1528 <<peer_id<<" not found"<<std::endl;
1532 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1540 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1542 if(command == TOSERVER_INIT)
1544 // [0] u16 TOSERVER_INIT
1545 // [2] u8 SER_FMT_VER_HIGHEST
1546 // [3] u8[20] player_name
1551 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1552 <<peer->id<<std::endl;
1554 // First byte after command is maximum supported
1555 // serialization version
1556 u8 client_max = data[2];
1557 u8 our_max = SER_FMT_VER_HIGHEST;
1558 // Use the highest version supported by both
1559 u8 deployed = core::min_(client_max, our_max);
1560 // If it's lower than the lowest supported, give up.
1561 if(deployed < SER_FMT_VER_LOWEST)
1562 deployed = SER_FMT_VER_INVALID;
1564 //peer->serialization_version = deployed;
1565 getClient(peer->id)->pending_serialization_version = deployed;
1567 if(deployed == SER_FMT_VER_INVALID)
1569 derr_server<<DTIME<<"Server: Cannot negotiate "
1570 "serialization version with peer "
1571 <<peer_id<<std::endl;
1580 const u32 playername_size = 20;
1581 char playername[playername_size];
1582 for(u32 i=0; i<playername_size-1; i++)
1584 playername[i] = data[3+i];
1586 playername[playername_size-1] = 0;
1589 Player *player = emergePlayer(playername, "", peer_id);
1590 //Player *player = m_env.getPlayer(peer_id);
1593 // DEBUG: Test serialization
1594 std::ostringstream test_os;
1595 player->serialize(test_os);
1596 dstream<<"Player serialization test: \""<<test_os.str()
1598 std::istringstream test_is(test_os.str());
1599 player->deSerialize(test_is);
1602 // If failed, cancel
1605 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1606 <<": failed to emerge player"<<std::endl;
1611 // If a client is already connected to the player, cancel
1612 if(player->peer_id != 0)
1614 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1615 <<" tried to connect to "
1616 "an already connected player (peer_id="
1617 <<player->peer_id<<")"<<std::endl;
1620 // Set client of player
1621 player->peer_id = peer_id;
1624 // Check if player doesn't exist
1626 throw con::InvalidIncomingDataException
1627 ("Server::ProcessData(): INIT: Player doesn't exist");
1629 /*// update name if it was supplied
1630 if(datasize >= 20+3)
1633 player->updateName((const char*)&data[3]);
1636 // Now answer with a TOCLIENT_INIT
1638 SharedBuffer<u8> reply(2+1+6);
1639 writeU16(&reply[0], TOCLIENT_INIT);
1640 writeU8(&reply[2], deployed);
1641 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1643 m_con.Send(peer_id, 0, reply, true);
1647 if(command == TOSERVER_INIT2)
1649 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1650 <<peer->id<<std::endl;
1653 getClient(peer->id)->serialization_version
1654 = getClient(peer->id)->pending_serialization_version;
1657 Send some initialization data
1660 // Send player info to all players
1663 // Send inventory to player
1664 SendInventory(peer->id);
1668 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1669 m_time_of_day.get());
1670 m_con.Send(peer->id, 0, data, true);
1673 // Send information about server to player in chat
1674 SendChatMessage(peer_id, getStatusString());
1676 // Send information about joining in chat
1678 std::wstring name = L"unknown";
1679 Player *player = m_env.getPlayer(peer_id);
1681 name = narrow_to_wide(player->getName());
1683 std::wstring message;
1686 message += L" joined game";
1687 BroadcastChatMessage(message);
1693 if(peer_ser_ver == SER_FMT_VER_INVALID)
1695 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1696 " serialization format invalid or not initialized."
1697 " Skipping incoming command="<<command<<std::endl;
1701 Player *player = m_env.getPlayer(peer_id);
1704 derr_server<<"Server::ProcessData(): Cancelling: "
1705 "No player for peer_id="<<peer_id
1709 if(command == TOSERVER_PLAYERPOS)
1711 if(datasize < 2+12+12+4+4)
1715 v3s32 ps = readV3S32(&data[start+2]);
1716 v3s32 ss = readV3S32(&data[start+2+12]);
1717 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1718 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1719 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1720 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1721 pitch = wrapDegrees(pitch);
1722 yaw = wrapDegrees(yaw);
1723 player->setPosition(position);
1724 player->setSpeed(speed);
1725 player->setPitch(pitch);
1726 player->setYaw(yaw);
1728 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1729 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1730 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1732 else if(command == TOSERVER_GOTBLOCKS)
1745 u16 count = data[2];
1746 for(u16 i=0; i<count; i++)
1748 if((s16)datasize < 2+1+(i+1)*6)
1749 throw con::InvalidIncomingDataException
1750 ("GOTBLOCKS length is too short");
1751 v3s16 p = readV3S16(&data[2+1+i*6]);
1752 /*dstream<<"Server: GOTBLOCKS ("
1753 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1754 RemoteClient *client = getClient(peer_id);
1755 client->GotBlock(p);
1758 else if(command == TOSERVER_DELETEDBLOCKS)
1771 u16 count = data[2];
1772 for(u16 i=0; i<count; i++)
1774 if((s16)datasize < 2+1+(i+1)*6)
1775 throw con::InvalidIncomingDataException
1776 ("DELETEDBLOCKS length is too short");
1777 v3s16 p = readV3S16(&data[2+1+i*6]);
1778 /*dstream<<"Server: DELETEDBLOCKS ("
1779 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1780 RemoteClient *client = getClient(peer_id);
1781 client->SetBlockNotSent(p);
1784 else if(command == TOSERVER_CLICK_OBJECT)
1791 [2] u8 button (0=left, 1=right)
1796 u8 button = readU8(&data[2]);
1798 p.X = readS16(&data[3]);
1799 p.Y = readS16(&data[5]);
1800 p.Z = readS16(&data[7]);
1801 s16 id = readS16(&data[9]);
1802 //u16 item_i = readU16(&data[11]);
1804 MapBlock *block = NULL;
1807 block = m_env.getMap().getBlockNoCreate(p);
1809 catch(InvalidPositionException &e)
1811 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1815 MapBlockObject *obj = block->getObject(id);
1819 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1823 //TODO: Check that object is reasonably close
1828 InventoryList *ilist = player->inventory.getList("main");
1829 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1832 // Skip if inventory has no free space
1833 if(ilist->getUsedSlots() == ilist->getSize())
1835 dout_server<<"Player inventory has no free space"<<std::endl;
1840 Create the inventory item
1842 InventoryItem *item = NULL;
1843 // If it is an item-object, take the item from it
1844 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1846 item = ((ItemObject*)obj)->createInventoryItem();
1848 // Else create an item of the object
1851 item = new MapBlockObjectItem
1852 (obj->getInventoryString());
1855 // Add to inventory and send inventory
1856 ilist->addItem(item);
1857 SendInventory(player->peer_id);
1860 // Remove from block
1861 block->removeObject(id);
1864 else if(command == TOSERVER_GROUND_ACTION)
1872 [3] v3s16 nodepos_undersurface
1873 [9] v3s16 nodepos_abovesurface
1878 2: stop digging (all parameters ignored)
1879 3: digging completed
1881 u8 action = readU8(&data[2]);
1883 p_under.X = readS16(&data[3]);
1884 p_under.Y = readS16(&data[5]);
1885 p_under.Z = readS16(&data[7]);
1887 p_over.X = readS16(&data[9]);
1888 p_over.Y = readS16(&data[11]);
1889 p_over.Z = readS16(&data[13]);
1890 u16 item_i = readU16(&data[15]);
1892 //TODO: Check that target is reasonably close
1900 NOTE: This can be used in the future to check if
1901 somebody is cheating, by checking the timing.
1908 else if(action == 2)
1911 RemoteClient *client = getClient(peer->id);
1912 JMutexAutoLock digmutex(client->m_dig_mutex);
1913 client->m_dig_tool_item = -1;
1918 3: Digging completed
1920 else if(action == 3)
1922 // Mandatory parameter; actually used for nothing
1923 core::map<v3s16, MapBlock*> modified_blocks;
1926 u8 mineral = MINERAL_NONE;
1930 MapNode n = m_env.getMap().getNode(p_under);
1931 // Get material at position
1933 // If it's not diggable, do nothing
1934 if(content_diggable(material) == false)
1936 derr_server<<"Server: Not finishing digging: Node not diggable"
1939 // Client probably has wrong data.
1940 // Set block not sent, so that client will get
1942 dstream<<"Client "<<peer_id<<" tried to dig "
1943 <<"node from invalid position; setting"
1944 <<" MapBlock not sent."<<std::endl;
1945 RemoteClient *client = getClient(peer_id);
1946 v3s16 blockpos = getNodeBlockPos(p_under);
1947 client->SetBlockNotSent(blockpos);
1952 mineral = n.getMineral();
1954 catch(InvalidPositionException &e)
1956 derr_server<<"Server: Not finishing digging: Node not found."
1957 <<" Adding block to emerge queue."
1959 m_emerge_queue.addBlock(peer_id,
1960 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1965 Send the removal to all other clients
1970 SharedBuffer<u8> reply(replysize);
1971 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1972 writeS16(&reply[2], p_under.X);
1973 writeS16(&reply[4], p_under.Y);
1974 writeS16(&reply[6], p_under.Z);
1976 for(core::map<u16, RemoteClient*>::Iterator
1977 i = m_clients.getIterator();
1978 i.atEnd() == false; i++)
1980 // Get client and check that it is valid
1981 RemoteClient *client = i.getNode()->getValue();
1982 assert(client->peer_id == i.getNode()->getKey());
1983 if(client->serialization_version == SER_FMT_VER_INVALID)
1986 // Don't send if it's the same one
1987 if(peer_id == client->peer_id)
1991 m_con.Send(client->peer_id, 0, reply, true);
1995 Update and send inventory
1998 if(g_settings.getBool("creative_mode") == false)
2003 InventoryList *mlist = player->inventory.getList("main");
2006 InventoryItem *item = mlist->getItem(item_i);
2007 if(item && (std::string)item->getName() == "ToolItem")
2009 ToolItem *titem = (ToolItem*)item;
2010 std::string toolname = titem->getToolName();
2012 // Get digging properties for material and tool
2013 DiggingProperties prop =
2014 getDiggingProperties(material, toolname);
2016 if(prop.diggable == false)
2018 derr_server<<"Server: WARNING: Player digged"
2019 <<" with impossible material + tool"
2020 <<" combination"<<std::endl;
2023 bool weared_out = titem->addWear(prop.wear);
2027 mlist->deleteItem(item_i);
2033 Add dug item to inventory
2036 InventoryItem *item = NULL;
2038 if(mineral != MINERAL_NONE)
2039 item = getDiggedMineralItem(mineral);
2044 std::string &dug_s = content_features(material).dug_item;
2047 std::istringstream is(dug_s, std::ios::binary);
2048 item = InventoryItem::deSerialize(is);
2054 // Add a item to inventory
2055 player->inventory.addItem("main", item);
2058 SendInventory(player->peer_id);
2064 (this takes some time so it is done after the quick stuff)
2066 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2073 // Update water pressure around modification
2074 // This also adds it to m_flow_active_nodes if appropriate
2076 MapVoxelManipulator v(&m_env.getMap());
2077 v.m_disable_water_climb =
2078 g_settings.getBool("disable_water_climb");
2080 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2084 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2086 catch(ProcessingLimitException &e)
2088 dstream<<"Processing limit reached (1)"<<std::endl;
2091 v.blitBack(modified_blocks);
2098 else if(action == 1)
2101 InventoryList *ilist = player->inventory.getList("main");
2106 InventoryItem *item = ilist->getItem(item_i);
2108 // If there is no item, it is not possible to add it anywhere
2113 Handle material items
2115 if(std::string("MaterialItem") == item->getName())
2118 // Don't add a node if this is not a free space
2119 MapNode n2 = m_env.getMap().getNode(p_over);
2120 if(content_buildable_to(n2.d) == false)
2122 // Client probably has wrong data.
2123 // Set block not sent, so that client will get
2125 dstream<<"Client "<<peer_id<<" tried to place"
2126 <<" node in invalid position; setting"
2127 <<" MapBlock not sent."<<std::endl;
2128 RemoteClient *client = getClient(peer_id);
2129 v3s16 blockpos = getNodeBlockPos(p_over);
2130 client->SetBlockNotSent(blockpos);
2134 catch(InvalidPositionException &e)
2136 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2137 <<" Adding block to emerge queue."
2139 m_emerge_queue.addBlock(peer_id,
2140 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2144 // Reset build time counter
2145 getClient(peer->id)->m_time_from_building = 0.0;
2148 MaterialItem *mitem = (MaterialItem*)item;
2150 n.d = mitem->getMaterial();
2151 if(content_features(n.d).wall_mounted)
2152 n.dir = packDir(p_under - p_over);
2155 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2156 SharedBuffer<u8> reply(replysize);
2157 writeU16(&reply[0], TOCLIENT_ADDNODE);
2158 writeS16(&reply[2], p_over.X);
2159 writeS16(&reply[4], p_over.Y);
2160 writeS16(&reply[6], p_over.Z);
2161 n.serialize(&reply[8], peer_ser_ver);
2163 m_con.SendToAll(0, reply, true);
2168 InventoryList *ilist = player->inventory.getList("main");
2169 if(g_settings.getBool("creative_mode") == false && ilist)
2171 // Remove from inventory and send inventory
2172 if(mitem->getCount() == 1)
2173 ilist->deleteItem(item_i);
2177 SendInventory(peer_id);
2183 This takes some time so it is done after the quick stuff
2185 core::map<v3s16, MapBlock*> modified_blocks;
2186 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2189 Calculate special events
2192 /*if(n.d == CONTENT_MESE)
2195 for(s16 z=-1; z<=1; z++)
2196 for(s16 y=-1; y<=1; y++)
2197 for(s16 x=-1; x<=1; x++)
2208 v3s16 blockpos = getNodeBlockPos(p_over);
2210 MapBlock *block = NULL;
2213 block = m_env.getMap().getBlockNoCreate(blockpos);
2215 catch(InvalidPositionException &e)
2217 derr_server<<"Error while placing object: "
2218 "block not found"<<std::endl;
2222 v3s16 block_pos_i_on_map = block->getPosRelative();
2223 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2225 v3f pos = intToFloat(p_over, BS);
2226 pos -= block_pos_f_on_map;
2228 /*dout_server<<"pos="
2229 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2232 MapBlockObject *obj = NULL;
2235 Handle block object items
2237 if(std::string("MBOItem") == item->getName())
2239 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2241 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2242 "inventorystring=\""
2243 <<oitem->getInventoryString()
2244 <<"\""<<std::endl;*/
2246 obj = oitem->createObject
2247 (pos, player->getYaw(), player->getPitch());
2254 dout_server<<"Placing a miscellaneous item on map"
2257 Create an ItemObject that contains the item.
2259 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2260 std::ostringstream os(std::ios_base::binary);
2261 item->serialize(os);
2262 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2263 iobj->setItemString(os.str());
2269 derr_server<<"WARNING: item resulted in NULL object, "
2270 <<"not placing onto map"
2275 block->addObject(obj);
2277 dout_server<<"Placed object"<<std::endl;
2279 InventoryList *ilist = player->inventory.getList("main");
2280 if(g_settings.getBool("creative_mode") == false && ilist)
2282 // Remove from inventory and send inventory
2283 ilist->deleteItem(item_i);
2285 SendInventory(peer_id);
2293 Catch invalid actions
2297 derr_server<<"WARNING: Server: Invalid action "
2298 <<action<<std::endl;
2302 else if(command == TOSERVER_RELEASE)
2311 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2314 else if(command == TOSERVER_SIGNTEXT)
2323 std::string datastring((char*)&data[2], datasize-2);
2324 std::istringstream is(datastring, std::ios_base::binary);
2327 is.read((char*)buf, 6);
2328 v3s16 blockpos = readV3S16(buf);
2329 is.read((char*)buf, 2);
2330 s16 id = readS16(buf);
2331 is.read((char*)buf, 2);
2332 u16 textlen = readU16(buf);
2334 for(u16 i=0; i<textlen; i++)
2336 is.read((char*)buf, 1);
2337 text += (char)buf[0];
2340 MapBlock *block = NULL;
2343 block = m_env.getMap().getBlockNoCreate(blockpos);
2345 catch(InvalidPositionException &e)
2347 derr_server<<"Error while setting sign text: "
2348 "block not found"<<std::endl;
2352 MapBlockObject *obj = block->getObject(id);
2355 derr_server<<"Error while setting sign text: "
2356 "object not found"<<std::endl;
2360 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2362 derr_server<<"Error while setting sign text: "
2363 "object is not a sign"<<std::endl;
2367 ((SignObject*)obj)->setText(text);
2369 obj->getBlock()->setChangedFlag();
2371 else if(command == TOSERVER_INVENTORY_ACTION)
2373 /*// Ignore inventory changes if in creative mode
2374 if(g_settings.getBool("creative_mode") == true)
2376 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2380 // Strip command and create a stream
2381 std::string datastring((char*)&data[2], datasize-2);
2382 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2383 std::istringstream is(datastring, std::ios_base::binary);
2385 InventoryAction *a = InventoryAction::deSerialize(is);
2389 Handle craftresult specially if not in creative mode
2391 bool disable_action = false;
2392 if(a->getType() == IACTION_MOVE
2393 && g_settings.getBool("creative_mode") == false)
2395 IMoveAction *ma = (IMoveAction*)a;
2396 // Don't allow moving anything to craftresult
2397 if(ma->to_name == "craftresult")
2400 disable_action = true;
2402 // When something is removed from craftresult
2403 if(ma->from_name == "craftresult")
2405 disable_action = true;
2406 // Remove stuff from craft
2407 InventoryList *clist = player->inventory.getList("craft");
2410 u16 count = ma->count;
2413 clist->decrementMaterials(count);
2416 // Feed action to player inventory
2417 a->apply(&player->inventory);
2420 // If something appeared in craftresult, throw it
2422 InventoryList *rlist = player->inventory.getList("craftresult");
2423 InventoryList *mlist = player->inventory.getList("main");
2424 if(rlist && mlist && rlist->getUsedSlots() == 1)
2426 InventoryItem *item1 = rlist->changeItem(0, NULL);
2427 mlist->addItem(item1);
2431 if(disable_action == false)
2433 // Feed action to player inventory
2434 a->apply(&player->inventory);
2439 SendInventory(player->peer_id);
2443 dstream<<"TOSERVER_INVENTORY_ACTION: "
2444 <<"InventoryAction::deSerialize() returned NULL"
2448 else if(command == TOSERVER_CHAT_MESSAGE)
2456 std::string datastring((char*)&data[2], datasize-2);
2457 std::istringstream is(datastring, std::ios_base::binary);
2460 is.read((char*)buf, 2);
2461 u16 len = readU16(buf);
2463 std::wstring message;
2464 for(u16 i=0; i<len; i++)
2466 is.read((char*)buf, 2);
2467 message += (wchar_t)readU16(buf);
2470 // Get player name of this client
2471 std::wstring name = narrow_to_wide(player->getName());
2473 // Line to send to players
2475 // Whether to send to the player that sent the line
2476 bool send_to_sender = false;
2477 // Whether to send to other players
2478 bool send_to_others = false;
2481 std::wstring commandprefix = L"/#";
2482 if(message.substr(0, commandprefix.size()) == commandprefix)
2484 line += L"Server: ";
2486 message = message.substr(commandprefix.size());
2487 // Get player name as narrow string
2488 std::string name_s = player->getName();
2489 // Convert message to narrow string
2490 std::string message_s = wide_to_narrow(message);
2491 // Operator is the single name defined in config.
2492 std::string operator_name = g_settings.get("name");
2493 bool is_operator = (operator_name != "" &&
2494 wide_to_narrow(name) == operator_name);
2495 bool valid_command = false;
2496 if(message_s == "help")
2498 line += L"-!- Available commands: ";
2502 line += L"shutdown setting ";
2507 send_to_sender = true;
2508 valid_command = true;
2510 else if(message_s == "status")
2512 line = getStatusString();
2513 send_to_sender = true;
2514 valid_command = true;
2516 else if(is_operator)
2518 if(message_s == "shutdown")
2520 dstream<<DTIME<<" Server: Operator requested shutdown."
2522 m_shutdown_requested.set(true);
2524 line += L"*** Server shutting down (operator request)";
2525 send_to_sender = true;
2526 valid_command = true;
2528 else if(message_s.substr(0,8) == "setting ")
2530 std::string confline = message_s.substr(8);
2531 g_settings.parseConfigLine(confline);
2532 line += L"-!- Setting changed.";
2533 send_to_sender = true;
2534 valid_command = true;
2538 if(valid_command == false)
2540 line += L"-!- Invalid command: " + message;
2541 send_to_sender = true;
2552 send_to_others = true;
2557 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2560 Send the message to clients
2562 for(core::map<u16, RemoteClient*>::Iterator
2563 i = m_clients.getIterator();
2564 i.atEnd() == false; i++)
2566 // Get client and check that it is valid
2567 RemoteClient *client = i.getNode()->getValue();
2568 assert(client->peer_id == i.getNode()->getKey());
2569 if(client->serialization_version == SER_FMT_VER_INVALID)
2573 bool sender_selected = (peer_id == client->peer_id);
2574 if(sender_selected == true && send_to_sender == false)
2576 if(sender_selected == false && send_to_others == false)
2579 SendChatMessage(client->peer_id, line);
2585 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2586 "unknown command "<<command<<std::endl;
2590 catch(SendFailedException &e)
2592 derr_server<<"Server::ProcessData(): SendFailedException: "
2598 /*void Server::Send(u16 peer_id, u16 channelnum,
2599 SharedBuffer<u8> data, bool reliable)
2601 JMutexAutoLock lock(m_con_mutex);
2602 m_con.Send(peer_id, channelnum, data, reliable);
2605 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2607 DSTACK(__FUNCTION_NAME);
2609 Create a packet with the block in the right format
2612 std::ostringstream os(std::ios_base::binary);
2613 block->serialize(os, ver);
2614 std::string s = os.str();
2615 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2617 u32 replysize = 8 + blockdata.getSize();
2618 SharedBuffer<u8> reply(replysize);
2619 v3s16 p = block->getPos();
2620 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2621 writeS16(&reply[2], p.X);
2622 writeS16(&reply[4], p.Y);
2623 writeS16(&reply[6], p.Z);
2624 memcpy(&reply[8], *blockdata, blockdata.getSize());
2626 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2627 <<": \tpacket size: "<<replysize<<std::endl;*/
2632 m_con.Send(peer_id, 1, reply, true);
2635 core::list<PlayerInfo> Server::getPlayerInfo()
2637 DSTACK(__FUNCTION_NAME);
2638 JMutexAutoLock envlock(m_env_mutex);
2639 JMutexAutoLock conlock(m_con_mutex);
2641 core::list<PlayerInfo> list;
2643 core::list<Player*> players = m_env.getPlayers();
2645 core::list<Player*>::Iterator i;
2646 for(i = players.begin();
2647 i != players.end(); i++)
2651 Player *player = *i;
2654 con::Peer *peer = m_con.GetPeer(player->peer_id);
2655 // Copy info from peer to info struct
2657 info.address = peer->address;
2658 info.avg_rtt = peer->avg_rtt;
2660 catch(con::PeerNotFoundException &e)
2662 // Set dummy peer info
2664 info.address = Address(0,0,0,0,0);
2668 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2669 info.position = player->getPosition();
2671 list.push_back(info);
2678 void Server::peerAdded(con::Peer *peer)
2680 DSTACK(__FUNCTION_NAME);
2681 dout_server<<"Server::peerAdded(): peer->id="
2682 <<peer->id<<std::endl;
2685 c.type = PEER_ADDED;
2686 c.peer_id = peer->id;
2688 m_peer_change_queue.push_back(c);
2691 void Server::deletingPeer(con::Peer *peer, bool timeout)
2693 DSTACK(__FUNCTION_NAME);
2694 dout_server<<"Server::deletingPeer(): peer->id="
2695 <<peer->id<<", timeout="<<timeout<<std::endl;
2698 c.type = PEER_REMOVED;
2699 c.peer_id = peer->id;
2700 c.timeout = timeout;
2701 m_peer_change_queue.push_back(c);
2704 void Server::SendObjectData(float dtime)
2706 DSTACK(__FUNCTION_NAME);
2708 core::map<v3s16, bool> stepped_blocks;
2710 for(core::map<u16, RemoteClient*>::Iterator
2711 i = m_clients.getIterator();
2712 i.atEnd() == false; i++)
2714 u16 peer_id = i.getNode()->getKey();
2715 RemoteClient *client = i.getNode()->getValue();
2716 assert(client->peer_id == peer_id);
2718 if(client->serialization_version == SER_FMT_VER_INVALID)
2721 client->SendObjectData(this, dtime, stepped_blocks);
2725 void Server::SendPlayerInfos()
2727 DSTACK(__FUNCTION_NAME);
2729 //JMutexAutoLock envlock(m_env_mutex);
2731 // Get connected players
2732 core::list<Player*> players = m_env.getPlayers(true);
2734 u32 player_count = players.getSize();
2735 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2737 SharedBuffer<u8> data(datasize);
2738 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2741 core::list<Player*>::Iterator i;
2742 for(i = players.begin();
2743 i != players.end(); i++)
2745 Player *player = *i;
2747 /*dstream<<"Server sending player info for player with "
2748 "peer_id="<<player->peer_id<<std::endl;*/
2750 writeU16(&data[start], player->peer_id);
2751 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2752 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2753 start += 2+PLAYERNAME_SIZE;
2756 //JMutexAutoLock conlock(m_con_mutex);
2759 m_con.SendToAll(0, data, true);
2777 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2783 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2789 enum ItemSpecType type;
2790 // Only other one of these is used
2796 items: a pointer to an array of 9 pointers to items
2797 specs: a pointer to an array of 9 ItemSpecs
2799 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2801 u16 items_min_x = 100;
2802 u16 items_max_x = 100;
2803 u16 items_min_y = 100;
2804 u16 items_max_y = 100;
2805 for(u16 y=0; y<3; y++)
2806 for(u16 x=0; x<3; x++)
2808 if(items[y*3 + x] == NULL)
2810 if(items_min_x == 100 || x < items_min_x)
2812 if(items_min_y == 100 || y < items_min_y)
2814 if(items_max_x == 100 || x > items_max_x)
2816 if(items_max_y == 100 || y > items_max_y)
2819 // No items at all, just return false
2820 if(items_min_x == 100)
2823 u16 items_w = items_max_x - items_min_x + 1;
2824 u16 items_h = items_max_y - items_min_y + 1;
2826 u16 specs_min_x = 100;
2827 u16 specs_max_x = 100;
2828 u16 specs_min_y = 100;
2829 u16 specs_max_y = 100;
2830 for(u16 y=0; y<3; y++)
2831 for(u16 x=0; x<3; x++)
2833 if(specs[y*3 + x].type == ITEM_NONE)
2835 if(specs_min_x == 100 || x < specs_min_x)
2837 if(specs_min_y == 100 || y < specs_min_y)
2839 if(specs_max_x == 100 || x > specs_max_x)
2841 if(specs_max_y == 100 || y > specs_max_y)
2844 // No specs at all, just return false
2845 if(specs_min_x == 100)
2848 u16 specs_w = specs_max_x - specs_min_x + 1;
2849 u16 specs_h = specs_max_y - specs_min_y + 1;
2852 if(items_w != specs_w || items_h != specs_h)
2855 for(u16 y=0; y<specs_h; y++)
2856 for(u16 x=0; x<specs_w; x++)
2858 u16 items_x = items_min_x + x;
2859 u16 items_y = items_min_y + y;
2860 u16 specs_x = specs_min_x + x;
2861 u16 specs_y = specs_min_y + y;
2862 InventoryItem *item = items[items_y * 3 + items_x];
2863 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2865 if(spec.type == ITEM_NONE)
2867 // Has to be no item
2873 // There should be an item
2877 std::string itemname = item->getName();
2879 if(spec.type == ITEM_MATERIAL)
2881 if(itemname != "MaterialItem")
2883 MaterialItem *mitem = (MaterialItem*)item;
2884 if(mitem->getMaterial() != spec.num)
2887 else if(spec.type == ITEM_CRAFT)
2889 if(itemname != "CraftItem")
2891 CraftItem *mitem = (CraftItem*)item;
2892 if(mitem->getSubName() != spec.name)
2895 else if(spec.type == ITEM_TOOL)
2897 // Not supported yet
2900 else if(spec.type == ITEM_MBO)
2902 // Not supported yet
2907 // Not supported yet
2915 void Server::SendInventory(u16 peer_id)
2917 DSTACK(__FUNCTION_NAME);
2919 Player* player = m_env.getPlayer(peer_id);
2922 Calculate crafting stuff
2924 if(g_settings.getBool("creative_mode") == false)
2926 InventoryList *clist = player->inventory.getList("craft");
2927 InventoryList *rlist = player->inventory.getList("craftresult");
2930 rlist->clearItems();
2934 InventoryItem *items[9];
2935 for(u16 i=0; i<9; i++)
2937 items[i] = clist->getItem(i);
2946 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2947 if(checkItemCombination(items, specs))
2949 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2958 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2959 if(checkItemCombination(items, specs))
2961 rlist->addItem(new CraftItem("Stick", 4));
2970 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2971 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2972 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2973 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2974 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2975 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2976 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2977 if(checkItemCombination(items, specs))
2979 rlist->addItem(new MapBlockObjectItem("Sign"));
2988 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2989 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2990 if(checkItemCombination(items, specs))
2992 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3001 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3002 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3003 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3004 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3005 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3006 if(checkItemCombination(items, specs))
3008 rlist->addItem(new ToolItem("WPick", 0));
3017 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3018 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3019 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3020 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3021 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3022 if(checkItemCombination(items, specs))
3024 rlist->addItem(new ToolItem("STPick", 0));
3033 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3034 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3035 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3036 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3037 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3038 if(checkItemCombination(items, specs))
3040 rlist->addItem(new ToolItem("MesePick", 0));
3045 } // if creative_mode == false
3051 std::ostringstream os;
3052 //os.imbue(std::locale("C"));
3054 player->inventory.serialize(os);
3056 std::string s = os.str();
3058 SharedBuffer<u8> data(s.size()+2);
3059 writeU16(&data[0], TOCLIENT_INVENTORY);
3060 memcpy(&data[2], s.c_str(), s.size());
3063 m_con.Send(peer_id, 0, data, true);
3066 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3068 DSTACK(__FUNCTION_NAME);
3070 std::ostringstream os(std::ios_base::binary);
3074 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3075 os.write((char*)buf, 2);
3078 writeU16(buf, message.size());
3079 os.write((char*)buf, 2);
3082 for(u32 i=0; i<message.size(); i++)
3086 os.write((char*)buf, 2);
3090 std::string s = os.str();
3091 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3093 m_con.Send(peer_id, 0, data, true);
3096 void Server::BroadcastChatMessage(const std::wstring &message)
3098 for(core::map<u16, RemoteClient*>::Iterator
3099 i = m_clients.getIterator();
3100 i.atEnd() == false; i++)
3102 // Get client and check that it is valid
3103 RemoteClient *client = i.getNode()->getValue();
3104 assert(client->peer_id == i.getNode()->getKey());
3105 if(client->serialization_version == SER_FMT_VER_INVALID)
3108 SendChatMessage(client->peer_id, message);
3112 void Server::SendBlocks(float dtime)
3114 DSTACK(__FUNCTION_NAME);
3116 JMutexAutoLock envlock(m_env_mutex);
3117 JMutexAutoLock conlock(m_con_mutex);
3119 //TimeTaker timer("Server::SendBlocks");
3121 core::array<PrioritySortedBlockTransfer> queue;
3123 s32 total_sending = 0;
3125 for(core::map<u16, RemoteClient*>::Iterator
3126 i = m_clients.getIterator();
3127 i.atEnd() == false; i++)
3129 RemoteClient *client = i.getNode()->getValue();
3130 assert(client->peer_id == i.getNode()->getKey());
3132 total_sending += client->SendingCount();
3134 if(client->serialization_version == SER_FMT_VER_INVALID)
3137 client->GetNextBlocks(this, dtime, queue);
3141 // Lowest priority number comes first.
3142 // Lowest is most important.
3145 for(u32 i=0; i<queue.size(); i++)
3147 //TODO: Calculate limit dynamically
3148 if(total_sending >= g_settings.getS32
3149 ("max_simultaneous_block_sends_server_total"))
3152 PrioritySortedBlockTransfer q = queue[i];
3154 MapBlock *block = NULL;
3157 block = m_env.getMap().getBlockNoCreate(q.pos);
3159 catch(InvalidPositionException &e)
3164 RemoteClient *client = getClient(q.peer_id);
3166 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3168 client->SentBlock(q.pos);
3175 RemoteClient* Server::getClient(u16 peer_id)
3177 DSTACK(__FUNCTION_NAME);
3178 //JMutexAutoLock lock(m_con_mutex);
3179 core::map<u16, RemoteClient*>::Node *n;
3180 n = m_clients.find(peer_id);
3181 // A client should exist for all peers
3183 return n->getValue();
3186 std::wstring Server::getStatusString()
3188 std::wostringstream os(std::ios_base::binary);
3191 os<<L"uptime="<<m_uptime.get();
3192 // Information about clients
3194 for(core::map<u16, RemoteClient*>::Iterator
3195 i = m_clients.getIterator();
3196 i.atEnd() == false; i++)
3198 // Get client and check that it is valid
3199 RemoteClient *client = i.getNode()->getValue();
3200 assert(client->peer_id == i.getNode()->getKey());
3201 if(client->serialization_version == SER_FMT_VER_INVALID)
3204 Player *player = m_env.getPlayer(client->peer_id);
3205 // Get name of player
3206 std::wstring name = L"unknown";
3208 name = narrow_to_wide(player->getName());
3209 // Add name to information string
3213 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3214 os<<" WARNING: Map saving is disabled."<<std::endl;
3219 void setCreativeInventory(Player *player)
3221 player->resetInventory();
3223 // Give some good picks
3225 InventoryItem *item = new ToolItem("STPick", 0);
3226 void* r = player->inventory.addItem("main", item);
3230 InventoryItem *item = new ToolItem("MesePick", 0);
3231 void* r = player->inventory.addItem("main", item);
3238 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3241 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3242 player->inventory.addItem("main", item);
3245 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3247 // Skip some materials
3248 if(i == CONTENT_WATER || i == CONTENT_TORCH
3249 || i == CONTENT_COALSTONE)
3252 InventoryItem *item = new MaterialItem(i, 1);
3253 player->inventory.addItem("main", item);
3257 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3258 void* r = player->inventory.addItem("main", item);
3263 Player *Server::emergePlayer(const char *name, const char *password,
3267 Try to get an existing player
3269 Player *player = m_env.getPlayer(name);
3272 // If player is already connected, cancel
3273 if(player->peer_id != 0)
3275 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3280 player->peer_id = peer_id;
3282 // Reset inventory to creative if in creative mode
3283 if(g_settings.getBool("creative_mode"))
3285 setCreativeInventory(player);
3292 If player with the wanted peer_id already exists, cancel.
3294 if(m_env.getPlayer(peer_id) != NULL)
3296 dstream<<"emergePlayer(): Player with wrong name but same"
3297 " peer_id already exists"<<std::endl;
3305 player = new ServerRemotePlayer();
3306 //player->peer_id = c.peer_id;
3307 //player->peer_id = PEER_ID_INEXISTENT;
3308 player->peer_id = peer_id;
3309 player->updateName(name);
3315 dstream<<"Server: Finding spawn place for player \""
3316 <<player->getName()<<"\""<<std::endl;
3320 player->setPosition(intToFloat(v3s16(
3327 f32 groundheight = 0;
3329 // Try to find a good place a few times
3330 for(s32 i=0; i<500; i++)
3333 // We're going to try to throw the player to this position
3334 nodepos = v2s16(-range + (myrand()%(range*2)),
3335 -range + (myrand()%(range*2)));
3336 v2s16 sectorpos = getNodeSectorPos(nodepos);
3338 m_env.getMap().emergeSector(sectorpos);
3339 // Get ground height at point
3340 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3341 // The sector should have been generated -> groundheight exists
3342 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3343 // Don't go underwater
3344 if(groundheight < WATER_LEVEL)
3346 //dstream<<"-> Underwater"<<std::endl;
3349 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3350 // Get block at point
3352 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3353 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3354 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3355 // Don't go inside ground
3357 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3358 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3359 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3360 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3361 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3362 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3364 dstream<<"-> Inside ground"<<std::endl;
3368 }catch(InvalidPositionException &e)
3370 dstream<<"-> Invalid position"<<std::endl;
3371 // Ignore invalid position
3375 // Found a good place
3376 dstream<<"Searched through "<<i<<" places."<<std::endl;
3381 // If no suitable place was not found, go above water at least.
3382 if(groundheight < WATER_LEVEL)
3383 groundheight = WATER_LEVEL;
3385 player->setPosition(intToFloat(v3s16(
3394 Add player to environment
3397 m_env.addPlayer(player);
3400 Add stuff to inventory
3403 if(g_settings.getBool("creative_mode"))
3405 setCreativeInventory(player);
3410 InventoryItem *item = new ToolItem("WPick", 32000);
3411 void* r = player->inventory.addItem("main", item);
3415 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3416 void* r = player->inventory.addItem("main", item);
3420 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3421 void* r = player->inventory.addItem("main", item);
3425 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3426 void* r = player->inventory.addItem("main", item);
3430 InventoryItem *item = new CraftItem("Stick", 4);
3431 void* r = player->inventory.addItem("main", item);
3435 InventoryItem *item = new ToolItem("WPick", 32000);
3436 void* r = player->inventory.addItem("main", item);
3440 InventoryItem *item = new ToolItem("STPick", 32000);
3441 void* r = player->inventory.addItem("main", item);
3444 /*// Give some lights
3446 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3447 bool r = player->inventory.addItem("main", item);
3451 for(u16 i=0; i<4; i++)
3453 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3454 bool r = player->inventory.addItem("main", item);
3457 /*// Give some other stuff
3459 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3460 bool r = player->inventory.addItem("main", item);
3467 } // create new player
3471 void Server::UpdateBlockWaterPressure(MapBlock *block,
3472 core::map<v3s16, MapBlock*> &modified_blocks)
3474 MapVoxelManipulator v(&m_env.getMap());
3475 v.m_disable_water_climb =
3476 g_settings.getBool("disable_water_climb");
3478 VoxelArea area(block->getPosRelative(),
3479 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3483 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3485 catch(ProcessingLimitException &e)
3487 dstream<<"Processing limit reached (1)"<<std::endl;
3490 v.blitBack(modified_blocks);
3494 void Server::handlePeerChange(PeerChange &c)
3496 JMutexAutoLock envlock(m_env_mutex);
3497 JMutexAutoLock conlock(m_con_mutex);
3499 if(c.type == PEER_ADDED)
3506 core::map<u16, RemoteClient*>::Node *n;
3507 n = m_clients.find(c.peer_id);
3508 // The client shouldn't already exist
3512 RemoteClient *client = new RemoteClient();
3513 client->peer_id = c.peer_id;
3514 m_clients.insert(client->peer_id, client);
3517 else if(c.type == PEER_REMOVED)
3524 core::map<u16, RemoteClient*>::Node *n;
3525 n = m_clients.find(c.peer_id);
3526 // The client should exist
3529 // Collect information about leaving in chat
3530 std::wstring message;
3532 std::wstring name = L"unknown";
3533 Player *player = m_env.getPlayer(c.peer_id);
3535 name = narrow_to_wide(player->getName());
3539 message += L" left game";
3541 message += L" (timed out)";
3546 m_env.removePlayer(c.peer_id);
3549 // Set player client disconnected
3551 Player *player = m_env.getPlayer(c.peer_id);
3553 player->peer_id = 0;
3557 delete m_clients[c.peer_id];
3558 m_clients.remove(c.peer_id);
3560 // Send player info to all remaining clients
3563 // Send leave chat message to all remaining clients
3564 BroadcastChatMessage(message);
3573 void Server::handlePeerChanges()
3575 while(m_peer_change_queue.size() > 0)
3577 PeerChange c = m_peer_change_queue.pop_front();
3579 dout_server<<"Server: Handling peer change: "
3580 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3583 handlePeerChange(c);
3587 void dedicated_server_loop(Server &server, bool &kill)
3589 DSTACK(__FUNCTION_NAME);
3591 std::cout<<DTIME<<std::endl;
3592 std::cout<<"========================"<<std::endl;
3593 std::cout<<"Running dedicated server"<<std::endl;
3594 std::cout<<"========================"<<std::endl;
3595 std::cout<<std::endl;
3599 // This is kind of a hack but can be done like this
3600 // because server.step() is very light
3604 if(server.getShutdownRequested() || kill)
3606 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3610 static int counter = 0;
3616 core::list<PlayerInfo> list = server.getPlayerInfo();
3617 core::list<PlayerInfo>::Iterator i;
3618 static u32 sum_old = 0;
3619 u32 sum = PIChecksum(list);
3622 std::cout<<DTIME<<"Player info:"<<std::endl;
3623 for(i=list.begin(); i!=list.end(); i++)
3625 i->PrintLine(&std::cout);