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"
34 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36 void * ServerThread::Thread()
40 DSTACK(__FUNCTION_NAME);
45 m_server->AsyncRunStep();
47 //dout_server<<"Running m_server->Receive()"<<std::endl;
50 catch(con::NoIncomingDataException &e)
53 #if CATCH_UNHANDLED_EXCEPTIONS
55 This is what has to be done in threads to get suitable debug info
57 catch(std::exception &e)
59 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
60 <<e.what()<<std::endl;
70 void * EmergeThread::Thread()
74 DSTACK(__FUNCTION_NAME);
77 #if CATCH_UNHANDLED_EXCEPTIONS
83 Get block info from queue, emerge them and send them
86 After queue is empty, exit.
90 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
94 SharedPtr<QueuedBlockEmerge> q(qptr);
98 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
100 //TimeTaker timer("block emerge", g_device);
103 Try to emerge it from somewhere.
105 If it is only wanted as optional, only loading from disk
110 Check if any peer wants it as non-optional. In that case it
113 Also decrement the emerge queue count in clients.
116 bool optional = true;
119 core::map<u16, u8>::Iterator i;
120 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
122 //u16 peer_id = i.getNode()->getKey();
125 u8 flags = i.getNode()->getValue();
126 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
132 /*dstream<<"EmergeThread: p="
133 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
134 <<"optional="<<optional<<std::endl;*/
136 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
138 core::map<v3s16, MapBlock*> changed_blocks;
139 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
141 MapBlock *block = NULL;
142 bool got_block = true;
143 core::map<v3s16, MapBlock*> modified_blocks;
147 JMutexAutoLock envlock(m_server->m_env_mutex);
149 //TimeTaker timer("block emerge envlock", g_device);
152 bool only_from_disk = false;
155 only_from_disk = true;
157 block = map.emergeBlock(
161 lighting_invalidated_blocks);
163 // If it is a dummy, block was not found on disk
166 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
170 catch(InvalidPositionException &e)
173 // This happens when position is over limit.
179 if(debug && changed_blocks.size() > 0)
181 dout_server<<DTIME<<"Got changed_blocks: ";
182 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
183 i.atEnd() == false; i++)
185 MapBlock *block = i.getNode()->getValue();
186 v3s16 p = block->getPos();
187 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
189 dout_server<<std::endl;
193 Update water pressure
196 m_server->UpdateBlockWaterPressure(block, modified_blocks);
198 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
199 i.atEnd() == false; i++)
201 MapBlock *block = i.getNode()->getValue();
202 m_server->UpdateBlockWaterPressure(block, modified_blocks);
203 //v3s16 p = i.getNode()->getKey();
204 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
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;
222 TimeTaker timer("** updateLighting", g_device);*/
224 // Update lighting without locking the environment mutex,
225 // add modified blocks to changed blocks
226 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
228 // If we got no block, there should be no invalidated blocks
231 assert(lighting_invalidated_blocks.size() == 0);
237 Set sent status of modified blocks on clients
240 // NOTE: Server's clients are also behind the connection mutex
241 JMutexAutoLock lock(m_server->m_con_mutex);
244 Add the originally fetched block to the modified list
248 modified_blocks.insert(p, block);
252 Set the modified blocks unsent for all the clients
255 for(core::map<u16, RemoteClient*>::Iterator
256 i = m_server->m_clients.getIterator();
257 i.atEnd() == false; i++)
259 RemoteClient *client = i.getNode()->getValue();
261 if(modified_blocks.size() > 0)
263 // Remove block from sent history
264 client->SetBlocksNotSent(modified_blocks);
269 #if CATCH_UNHANDLED_EXCEPTIONS
272 This is what has to be done in threads to get suitable debug info
274 catch(std::exception &e)
276 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
277 <<e.what()<<std::endl;
285 void RemoteClient::GetNextBlocks(Server *server, float dtime,
286 core::array<PrioritySortedBlockTransfer> &dest)
288 DSTACK(__FUNCTION_NAME);
292 JMutexAutoLock lock(m_blocks_sent_mutex);
293 m_nearest_unsent_reset_timer += dtime;
296 // Won't send anything if already sending
298 JMutexAutoLock lock(m_blocks_sending_mutex);
300 if(m_blocks_sending.size() >= g_settings.getU16
301 ("max_simultaneous_block_sends_per_client"))
303 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
308 Player *player = server->m_env.getPlayer(peer_id);
310 v3f playerpos = player->getPosition();
311 v3f playerspeed = player->getSpeed();
313 v3s16 center_nodepos = floatToInt(playerpos);
315 v3s16 center = getNodeBlockPos(center_nodepos);
318 Get the starting value of the block finder radius.
320 s16 last_nearest_unsent_d;
323 JMutexAutoLock lock(m_blocks_sent_mutex);
325 if(m_last_center != center)
327 m_nearest_unsent_d = 0;
328 m_last_center = center;
331 /*dstream<<"m_nearest_unsent_reset_timer="
332 <<m_nearest_unsent_reset_timer<<std::endl;*/
333 if(m_nearest_unsent_reset_timer > 5.0)
335 m_nearest_unsent_reset_timer = 0;
336 m_nearest_unsent_d = 0;
337 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
340 last_nearest_unsent_d = m_nearest_unsent_d;
342 d_start = m_nearest_unsent_d;
345 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
346 ("max_simultaneous_block_sends_per_client");
347 u16 maximum_simultaneous_block_sends =
348 maximum_simultaneous_block_sends_setting;
351 Check the time from last addNode/removeNode.
353 Decrease send rate if player is building stuff.
356 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
357 m_time_from_building.m_value += dtime;
358 if(m_time_from_building.m_value
359 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)
361 maximum_simultaneous_block_sends
362 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
366 u32 num_blocks_selected;
368 JMutexAutoLock lock(m_blocks_sending_mutex);
369 num_blocks_selected = m_blocks_sending.size();
373 next time d will be continued from the d from which the nearest
374 unsent block was found this time.
376 This is because not necessarily any of the blocks found this
377 time are actually sent.
379 s32 new_nearest_unsent_d = -1;
381 // Serialization version used
382 //u8 ser_version = serialization_version;
384 //bool has_incomplete_blocks = false;
386 s16 d_max = g_settings.getS16("max_block_send_distance");
387 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
389 //dstream<<"Starting from "<<d_start<<std::endl;
391 for(s16 d = d_start; d <= d_max; d++)
393 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
395 //if(has_incomplete_blocks == false)
397 JMutexAutoLock lock(m_blocks_sent_mutex);
399 If m_nearest_unsent_d was changed by the EmergeThread
400 (it can change it to 0 through SetBlockNotSent),
402 Else update m_nearest_unsent_d
404 if(m_nearest_unsent_d != last_nearest_unsent_d)
406 d = m_nearest_unsent_d;
407 last_nearest_unsent_d = m_nearest_unsent_d;
412 Get the border/face dot coordinates of a "d-radiused"
415 core::list<v3s16> list;
416 getFacePositions(list, d);
418 core::list<v3s16>::Iterator li;
419 for(li=list.begin(); li!=list.end(); li++)
421 v3s16 p = *li + center;
425 - Don't allow too many simultaneous transfers
426 - EXCEPT when the blocks are very close
428 Also, don't send blocks that are already flying.
431 u16 maximum_simultaneous_block_sends_now =
432 maximum_simultaneous_block_sends;
434 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
436 maximum_simultaneous_block_sends_now =
437 maximum_simultaneous_block_sends_setting;
441 JMutexAutoLock lock(m_blocks_sending_mutex);
443 // Limit is dynamically lowered when building
444 if(num_blocks_selected
445 >= maximum_simultaneous_block_sends_now)
447 /*dstream<<"Not sending more blocks. Queue full. "
448 <<m_blocks_sending.size()
453 if(m_blocks_sending.find(p) != NULL)
460 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
461 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
462 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
468 bool generate = d <= d_max_gen;
470 // Limit the generating area vertically to half
471 if(abs(p.Y - center.Y) > d_max_gen / 2)
475 Don't send already sent blocks
478 JMutexAutoLock lock(m_blocks_sent_mutex);
480 if(m_blocks_sent.find(p) != NULL)
485 Check if map has this block
487 MapBlock *block = NULL;
490 block = server->m_env.getMap().getBlockNoCreate(p);
492 catch(InvalidPositionException &e)
496 bool surely_not_found_on_disk = false;
499 /*if(block->isIncomplete())
501 has_incomplete_blocks = true;
507 surely_not_found_on_disk = true;
512 If block has been marked to not exist on disk (dummy)
513 and generating new ones is not wanted, skip block.
515 if(generate == false && surely_not_found_on_disk == true)
522 Record the lowest d from which a a block has been
523 found being not sent and possibly to exist
525 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
527 new_nearest_unsent_d = d;
531 Add inexistent block to emerge queue.
533 if(block == NULL || surely_not_found_on_disk)
535 /*SharedPtr<JMutexAutoLock> lock
536 (m_num_blocks_in_emerge_queue.getLock());*/
538 //TODO: Get value from somewhere
539 // Allow only one block in emerge queue
540 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
542 // Add it to the emerge queue and trigger the thread
545 if(generate == false)
546 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
548 server->m_emerge_queue.addBlock(peer_id, p, flags);
549 server->m_emergethread.trigger();
560 PrioritySortedBlockTransfer q((float)d, p, peer_id);
564 num_blocks_selected += 1;
569 if(new_nearest_unsent_d != -1)
571 JMutexAutoLock lock(m_blocks_sent_mutex);
572 m_nearest_unsent_d = new_nearest_unsent_d;
576 void RemoteClient::SendObjectData(
579 core::map<v3s16, bool> &stepped_blocks
582 DSTACK(__FUNCTION_NAME);
584 // Can't send anything without knowing version
585 if(serialization_version == SER_FMT_VER_INVALID)
587 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
593 Send a TOCLIENT_OBJECTDATA packet.
597 u16 number of player positions
608 std::ostringstream os(std::ios_base::binary);
612 writeU16(buf, TOCLIENT_OBJECTDATA);
613 os.write((char*)buf, 2);
616 Get and write player data
619 core::list<Player*> players = server->m_env.getPlayers();
621 // Write player count
622 u16 playercount = players.size();
623 writeU16(buf, playercount);
624 os.write((char*)buf, 2);
626 core::list<Player*>::Iterator i;
627 for(i = players.begin();
628 i != players.end(); i++)
632 v3f pf = player->getPosition();
633 v3f sf = player->getSpeed();
635 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
636 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
637 s32 pitch_i (player->getPitch() * 100);
638 s32 yaw_i (player->getYaw() * 100);
640 writeU16(buf, player->peer_id);
641 os.write((char*)buf, 2);
642 writeV3S32(buf, position_i);
643 os.write((char*)buf, 12);
644 writeV3S32(buf, speed_i);
645 os.write((char*)buf, 12);
646 writeS32(buf, pitch_i);
647 os.write((char*)buf, 4);
648 writeS32(buf, yaw_i);
649 os.write((char*)buf, 4);
653 Get and write object data
659 For making players to be able to build to their nearby
660 environment (building is not possible on blocks that are not
663 - Add blocks to emerge queue if they are not found
665 SUGGESTION: These could be ignored from the backside of the player
668 Player *player = server->m_env.getPlayer(peer_id);
670 v3f playerpos = player->getPosition();
671 v3f playerspeed = player->getSpeed();
673 v3s16 center_nodepos = floatToInt(playerpos);
674 v3s16 center = getNodeBlockPos(center_nodepos);
676 s16 d_max = g_settings.getS16("active_object_range");
678 // Number of blocks whose objects were written to bos
681 std::ostringstream bos(std::ios_base::binary);
683 for(s16 d = 0; d <= d_max; d++)
685 core::list<v3s16> list;
686 getFacePositions(list, d);
688 core::list<v3s16>::Iterator li;
689 for(li=list.begin(); li!=list.end(); li++)
691 v3s16 p = *li + center;
694 Ignore blocks that haven't been sent to the client
697 JMutexAutoLock sentlock(m_blocks_sent_mutex);
698 if(m_blocks_sent.find(p) == NULL)
702 // Try stepping block and add it to a send queue
707 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
710 Step block if not in stepped_blocks and add to stepped_blocks.
712 if(stepped_blocks.find(p) == NULL)
714 block->stepObjects(dtime, true, server->getDayNightRatio());
715 stepped_blocks.insert(p, true);
716 block->setChangedFlag();
719 // Skip block if there are no objects
720 if(block->getObjectCount() == 0)
729 bos.write((char*)buf, 6);
732 block->serializeObjects(bos, serialization_version);
737 Stop collecting objects if data is already too big
739 // Sum of player and object data sizes
740 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
741 // break out if data too big
742 if(sum > MAX_OBJECTDATA_SIZE)
744 goto skip_subsequent;
748 catch(InvalidPositionException &e)
751 // Add it to the emerge queue and trigger the thread.
752 // Fetch the block only if it is on disk.
754 // Grab and increment counter
755 /*SharedPtr<JMutexAutoLock> lock
756 (m_num_blocks_in_emerge_queue.getLock());
757 m_num_blocks_in_emerge_queue.m_value++;*/
759 // Add to queue as an anonymous fetch from disk
760 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
761 server->m_emerge_queue.addBlock(0, p, flags);
762 server->m_emergethread.trigger();
770 writeU16(buf, blockcount);
771 os.write((char*)buf, 2);
773 // Write block objects
780 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
783 std::string s = os.str();
784 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
785 // Send as unreliable
786 server->m_con.Send(peer_id, 0, data, false);
789 void RemoteClient::GotBlock(v3s16 p)
791 JMutexAutoLock lock(m_blocks_sending_mutex);
792 JMutexAutoLock lock2(m_blocks_sent_mutex);
793 if(m_blocks_sending.find(p) != NULL)
794 m_blocks_sending.remove(p);
796 dstream<<"RemoteClient::GotBlock(): Didn't find in"
797 " m_blocks_sending"<<std::endl;
798 m_blocks_sent.insert(p, true);
801 void RemoteClient::SentBlock(v3s16 p)
803 JMutexAutoLock lock(m_blocks_sending_mutex);
804 if(m_blocks_sending.size() > 15)
806 dstream<<"RemoteClient::SentBlock(): "
807 <<"m_blocks_sending.size()="
808 <<m_blocks_sending.size()<<std::endl;
810 if(m_blocks_sending.find(p) == NULL)
811 m_blocks_sending.insert(p, 0.0);
813 dstream<<"RemoteClient::SentBlock(): Sent block"
814 " already in m_blocks_sending"<<std::endl;
817 void RemoteClient::SetBlockNotSent(v3s16 p)
819 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
820 JMutexAutoLock sentlock(m_blocks_sent_mutex);
822 m_nearest_unsent_d = 0;
824 if(m_blocks_sending.find(p) != NULL)
825 m_blocks_sending.remove(p);
826 if(m_blocks_sent.find(p) != NULL)
827 m_blocks_sent.remove(p);
830 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
832 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
833 JMutexAutoLock sentlock(m_blocks_sent_mutex);
835 m_nearest_unsent_d = 0;
837 for(core::map<v3s16, MapBlock*>::Iterator
838 i = blocks.getIterator();
839 i.atEnd()==false; i++)
841 v3s16 p = i.getNode()->getKey();
843 if(m_blocks_sending.find(p) != NULL)
844 m_blocks_sending.remove(p);
845 if(m_blocks_sent.find(p) != NULL)
846 m_blocks_sent.remove(p);
850 /*void RemoteClient::BlockEmerged()
852 SharedPtr<JMutexAutoLock> lock(m_num_blocks_in_emerge_queue.getLock());
853 assert(m_num_blocks_in_emerge_queue.m_value > 0);
854 m_num_blocks_in_emerge_queue.m_value--;
857 /*void RemoteClient::RunSendingTimeouts(float dtime, float timeout)
859 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
861 core::list<v3s16> remove_queue;
862 for(core::map<v3s16, float>::Iterator
863 i = m_blocks_sending.getIterator();
864 i.atEnd()==false; i++)
866 v3s16 p = i.getNode()->getKey();
867 float t = i.getNode()->getValue();
869 i.getNode()->setValue(t);
873 remove_queue.push_back(p);
876 for(core::list<v3s16>::Iterator
877 i = remove_queue.begin();
878 i != remove_queue.end(); i++)
880 m_blocks_sending.remove(*i);
888 PlayerInfo::PlayerInfo()
893 void PlayerInfo::PrintLine(std::ostream *s)
895 (*s)<<id<<": \""<<name<<"\" ("
896 <<position.X<<","<<position.Y
897 <<","<<position.Z<<") ";
899 (*s)<<" avg_rtt="<<avg_rtt;
903 u32 PIChecksum(core::list<PlayerInfo> &l)
905 core::list<PlayerInfo>::Iterator i;
908 for(i=l.begin(); i!=l.end(); i++)
910 checksum += a * (i->id+1);
911 checksum ^= 0x435aafcd;
922 std::string mapsavedir,
926 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
927 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
929 m_emergethread(this),
932 m_time_of_day_send_timer(0)
934 m_flowwater_timer = 0.0;
935 m_print_info_timer = 0.0;
936 m_objectdata_timer = 0.0;
937 m_emergethread_trigger_timer = 0.0;
938 m_savemap_timer = 0.0;
942 m_step_dtime_mutex.Init();
951 JMutexAutoLock clientslock(m_con_mutex);
953 for(core::map<u16, RemoteClient*>::Iterator
954 i = m_clients.getIterator();
955 i.atEnd() == false; i++)
957 u16 peer_id = i.getNode()->getKey();
961 JMutexAutoLock envlock(m_env_mutex);
962 m_env.removePlayer(peer_id);
966 delete i.getNode()->getValue();
970 void Server::start(unsigned short port)
972 DSTACK(__FUNCTION_NAME);
973 // Stop thread if already running
976 // Initialize connection
977 m_con.setTimeoutMs(30);
981 m_thread.setRun(true);
984 dout_server<<"Server started on port "<<port<<std::endl;
989 DSTACK(__FUNCTION_NAME);
990 // Stop threads (set run=false first so both start stopping)
991 m_thread.setRun(false);
992 m_emergethread.setRun(false);
994 m_emergethread.stop();
996 dout_server<<"Server threads stopped"<<std::endl;
999 void Server::step(float dtime)
1001 DSTACK(__FUNCTION_NAME);
1006 JMutexAutoLock lock(m_step_dtime_mutex);
1007 m_step_dtime += dtime;
1011 void Server::AsyncRunStep()
1013 DSTACK(__FUNCTION_NAME);
1017 JMutexAutoLock lock1(m_step_dtime_mutex);
1018 dtime = m_step_dtime;
1021 // Send blocks to clients
1028 JMutexAutoLock lock1(m_step_dtime_mutex);
1029 m_step_dtime -= dtime;
1033 Update m_time_of_day
1036 m_time_counter += dtime;
1037 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1038 u32 units = (u32)(m_time_counter*speed);
1039 m_time_counter -= (f32)units / speed;
1040 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1042 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1045 Send to clients at constant intervals
1048 m_time_of_day_send_timer -= dtime;
1049 if(m_time_of_day_send_timer < 0.0)
1051 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1053 //JMutexAutoLock envlock(m_env_mutex);
1054 JMutexAutoLock conlock(m_con_mutex);
1056 for(core::map<u16, RemoteClient*>::Iterator
1057 i = m_clients.getIterator();
1058 i.atEnd() == false; i++)
1060 RemoteClient *client = i.getNode()->getValue();
1061 //Player *player = m_env.getPlayer(client->peer_id);
1063 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1064 m_time_of_day.get());
1066 m_con.Send(client->peer_id, 0, data, true);
1071 //dstream<<"Server steps "<<dtime<<std::endl;
1073 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1075 // Has to be locked for peerAdded/Removed
1076 JMutexAutoLock lock1(m_env_mutex);
1077 // Process connection's timeouts
1078 JMutexAutoLock lock2(m_con_mutex);
1079 m_con.RunTimeouts(dtime);
1083 // This also runs Map's timers
1084 JMutexAutoLock lock(m_env_mutex);
1098 if(g_settings.getBool("endless_water") == false)
1103 float &counter = m_flowwater_timer;
1105 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1110 core::map<v3s16, MapBlock*> modified_blocks;
1114 JMutexAutoLock envlock(m_env_mutex);
1116 MapVoxelManipulator v(&m_env.getMap());
1117 v.m_disable_water_climb =
1118 g_settings.getBool("disable_water_climb");
1120 if(g_settings.getBool("endless_water") == false)
1121 v.flowWater(m_flow_active_nodes, 0, false, 250);
1123 v.flowWater(m_flow_active_nodes, 0, false, 50);
1125 v.blitBack(modified_blocks);
1127 ServerMap &map = ((ServerMap&)m_env.getMap());
1130 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1131 map.updateLighting(modified_blocks, lighting_modified_blocks);
1133 // Add blocks modified by lighting to modified_blocks
1134 for(core::map<v3s16, MapBlock*>::Iterator
1135 i = lighting_modified_blocks.getIterator();
1136 i.atEnd() == false; i++)
1138 MapBlock *block = i.getNode()->getValue();
1139 modified_blocks.insert(block->getPos(), block);
1144 Set the modified blocks unsent for all the clients
1147 JMutexAutoLock lock2(m_con_mutex);
1149 for(core::map<u16, RemoteClient*>::Iterator
1150 i = m_clients.getIterator();
1151 i.atEnd() == false; i++)
1153 RemoteClient *client = i.getNode()->getValue();
1155 if(modified_blocks.size() > 0)
1157 // Remove block from sent history
1158 client->SetBlocksNotSent(modified_blocks);
1162 } // interval counter
1165 // Periodically print some info
1167 float &counter = m_print_info_timer;
1173 JMutexAutoLock lock2(m_con_mutex);
1175 for(core::map<u16, RemoteClient*>::Iterator
1176 i = m_clients.getIterator();
1177 i.atEnd() == false; i++)
1179 //u16 peer_id = i.getNode()->getKey();
1180 RemoteClient *client = i.getNode()->getValue();
1181 client->PrintInfo(std::cout);
1189 NOTE: Some of this could be moved to RemoteClient
1193 JMutexAutoLock envlock(m_env_mutex);
1194 JMutexAutoLock conlock(m_con_mutex);
1196 for(core::map<u16, RemoteClient*>::Iterator
1197 i = m_clients.getIterator();
1198 i.atEnd() == false; i++)
1200 RemoteClient *client = i.getNode()->getValue();
1201 Player *player = m_env.getPlayer(client->peer_id);
1203 JMutexAutoLock digmutex(client->m_dig_mutex);
1205 if(client->m_dig_tool_item == -1)
1208 client->m_dig_time_remaining -= dtime;
1210 if(client->m_dig_time_remaining > 0)
1213 v3s16 p_under = client->m_dig_position;
1215 // Mandatory parameter; actually used for nothing
1216 core::map<v3s16, MapBlock*> modified_blocks;
1222 // Get material at position
1223 material = m_env.getMap().getNode(p_under).d;
1224 // If it's not diggable, do nothing
1225 if(content_diggable(material) == false)
1227 derr_server<<"Server: Not finishing digging: Node not diggable"
1229 client->m_dig_tool_item = -1;
1233 catch(InvalidPositionException &e)
1235 derr_server<<"Server: Not finishing digging: Node not found"
1237 client->m_dig_tool_item = -1;
1243 SharedBuffer<u8> reply(replysize);
1244 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1245 writeS16(&reply[2], p_under.X);
1246 writeS16(&reply[4], p_under.Y);
1247 writeS16(&reply[6], p_under.Z);
1249 m_con.SendToAll(0, reply, true);
1251 if(g_settings.getBool("creative_mode") == false)
1253 // Add to inventory and send inventory
1254 InventoryItem *item = new MaterialItem(material, 1);
1255 player->inventory.addItem("main", item);
1256 SendInventory(player->peer_id);
1261 (this takes some time so it is done after the quick stuff)
1263 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1269 // Update water pressure around modification
1270 // This also adds it to m_flow_active_nodes if appropriate
1272 MapVoxelManipulator v(&m_env.getMap());
1273 v.m_disable_water_climb =
1274 g_settings.getBool("disable_water_climb");
1276 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1280 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1282 catch(ProcessingLimitException &e)
1284 dstream<<"Processing limit reached (1)"<<std::endl;
1287 v.blitBack(modified_blocks);
1291 // Send object positions
1293 float &counter = m_objectdata_timer;
1295 if(counter >= g_settings.getFloat("objectdata_interval"))
1297 JMutexAutoLock lock1(m_env_mutex);
1298 JMutexAutoLock lock2(m_con_mutex);
1299 SendObjectData(counter);
1305 // Trigger emergethread (it gets somehow gets to a
1306 // non-triggered but bysy state sometimes)
1308 float &counter = m_emergethread_trigger_timer;
1314 m_emergethread.trigger();
1320 float &counter = m_savemap_timer;
1322 if(counter >= g_settings.getFloat("server_map_save_interval"))
1326 JMutexAutoLock lock(m_env_mutex);
1328 // Save only changed parts
1329 m_env.getMap().save(true);
1331 // Delete unused sectors
1332 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1333 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1334 if(deleted_count > 0)
1336 dout_server<<"Server: Unloaded "<<deleted_count
1337 <<" sectors from memory"<<std::endl;
1343 void Server::Receive()
1345 DSTACK(__FUNCTION_NAME);
1346 u32 data_maxsize = 10000;
1347 Buffer<u8> data(data_maxsize);
1352 JMutexAutoLock lock(m_con_mutex);
1353 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1355 ProcessData(*data, datasize, peer_id);
1357 catch(con::InvalidIncomingDataException &e)
1359 derr_server<<"Server::Receive(): "
1360 "InvalidIncomingDataException: what()="
1361 <<e.what()<<std::endl;
1363 catch(con::PeerNotFoundException &e)
1365 //NOTE: This is not needed anymore
1367 // The peer has been disconnected.
1368 // Find the associated player and remove it.
1370 /*JMutexAutoLock envlock(m_env_mutex);
1372 dout_server<<"ServerThread: peer_id="<<peer_id
1373 <<" has apparently closed connection. "
1374 <<"Removing player."<<std::endl;
1376 m_env.removePlayer(peer_id);*/
1380 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1382 DSTACK(__FUNCTION_NAME);
1383 // Environment is locked first.
1384 JMutexAutoLock envlock(m_env_mutex);
1385 JMutexAutoLock conlock(m_con_mutex);
1389 peer = m_con.GetPeer(peer_id);
1391 catch(con::PeerNotFoundException &e)
1393 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1394 <<peer_id<<" not found"<<std::endl;
1398 //u8 peer_ser_ver = peer->serialization_version;
1399 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1407 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1409 if(command == TOSERVER_INIT)
1411 // [0] u16 TOSERVER_INIT
1412 // [2] u8 SER_FMT_VER_HIGHEST
1413 // [3] u8[20] player_name
1418 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1419 <<peer->id<<std::endl;
1421 // First byte after command is maximum supported
1422 // serialization version
1423 u8 client_max = data[2];
1424 u8 our_max = SER_FMT_VER_HIGHEST;
1425 // Use the highest version supported by both
1426 u8 deployed = core::min_(client_max, our_max);
1427 // If it's lower than the lowest supported, give up.
1428 if(deployed < SER_FMT_VER_LOWEST)
1429 deployed = SER_FMT_VER_INVALID;
1431 //peer->serialization_version = deployed;
1432 getClient(peer->id)->pending_serialization_version = deployed;
1434 if(deployed == SER_FMT_VER_INVALID)
1436 derr_server<<DTIME<<"Server: Cannot negotiate "
1437 "serialization version with peer "
1438 <<peer_id<<std::endl;
1446 Player *player = m_env.getPlayer(peer_id);
1448 // Check if player doesn't exist
1450 throw con::InvalidIncomingDataException
1451 ("Server::ProcessData(): INIT: Player doesn't exist");
1453 // update name if it was supplied
1454 if(datasize >= 20+3)
1457 player->updateName((const char*)&data[3]);
1460 // Now answer with a TOCLIENT_INIT
1462 SharedBuffer<u8> reply(2+1+6);
1463 writeU16(&reply[0], TOCLIENT_INIT);
1464 writeU8(&reply[2], deployed);
1465 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1467 m_con.Send(peer_id, 0, reply, true);
1471 if(command == TOSERVER_INIT2)
1473 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1474 <<peer->id<<std::endl;
1477 getClient(peer->id)->serialization_version
1478 = getClient(peer->id)->pending_serialization_version;
1481 Send some initialization data
1484 // Send player info to all players
1487 // Send inventory to player
1488 SendInventory(peer->id);
1492 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1493 m_time_of_day.get());
1494 m_con.Send(peer->id, 0, data, true);
1500 if(peer_ser_ver == SER_FMT_VER_INVALID)
1502 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1503 " serialization format invalid or not initialized."
1504 " Skipping incoming command="<<command<<std::endl;
1508 Player *player = m_env.getPlayer(peer_id);
1511 derr_server<<"Server::ProcessData(): Cancelling: "
1512 "No player for peer_id="<<peer_id
1516 if(command == TOSERVER_PLAYERPOS)
1518 if(datasize < 2+12+12+4+4)
1522 v3s32 ps = readV3S32(&data[start+2]);
1523 v3s32 ss = readV3S32(&data[start+2+12]);
1524 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1525 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1526 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1527 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1528 pitch = wrapDegrees(pitch);
1529 yaw = wrapDegrees(yaw);
1530 player->setPosition(position);
1531 player->setSpeed(speed);
1532 player->setPitch(pitch);
1533 player->setYaw(yaw);
1535 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1536 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1537 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1539 else if(command == TOSERVER_GOTBLOCKS)
1552 u16 count = data[2];
1553 for(u16 i=0; i<count; i++)
1555 if((s16)datasize < 2+1+(i+1)*6)
1556 throw con::InvalidIncomingDataException
1557 ("GOTBLOCKS length is too short");
1558 v3s16 p = readV3S16(&data[2+1+i*6]);
1559 /*dstream<<"Server: GOTBLOCKS ("
1560 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1561 RemoteClient *client = getClient(peer_id);
1562 client->GotBlock(p);
1565 else if(command == TOSERVER_DELETEDBLOCKS)
1578 u16 count = data[2];
1579 for(u16 i=0; i<count; i++)
1581 if((s16)datasize < 2+1+(i+1)*6)
1582 throw con::InvalidIncomingDataException
1583 ("DELETEDBLOCKS length is too short");
1584 v3s16 p = readV3S16(&data[2+1+i*6]);
1585 /*dstream<<"Server: DELETEDBLOCKS ("
1586 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1587 RemoteClient *client = getClient(peer_id);
1588 client->SetBlockNotSent(p);
1591 else if(command == TOSERVER_CLICK_OBJECT)
1598 [2] u8 button (0=left, 1=right)
1603 u8 button = readU8(&data[2]);
1605 p.X = readS16(&data[3]);
1606 p.Y = readS16(&data[5]);
1607 p.Z = readS16(&data[7]);
1608 s16 id = readS16(&data[9]);
1609 //u16 item_i = readU16(&data[11]);
1611 MapBlock *block = NULL;
1614 block = m_env.getMap().getBlockNoCreate(p);
1616 catch(InvalidPositionException &e)
1618 derr_server<<"PICK_OBJECT block not found"<<std::endl;
1622 MapBlockObject *obj = block->getObject(id);
1626 derr_server<<"PICK_OBJECT object not found"<<std::endl;
1630 //TODO: Check that object is reasonably close
1635 InventoryList *ilist = player->inventory.getList("main");
1636 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1639 // Skip if inventory has no free space
1640 if(ilist->getUsedSlots() == ilist->getSize())
1642 dout_server<<"Player inventory has no free space"<<std::endl;
1646 // Add to inventory and send inventory
1647 InventoryItem *item = new MapBlockObjectItem
1648 (obj->getInventoryString());
1649 ilist->addItem(item);
1650 SendInventory(player->peer_id);
1653 // Remove from block
1654 block->removeObject(id);
1657 else if(command == TOSERVER_GROUND_ACTION)
1665 [3] v3s16 nodepos_undersurface
1666 [9] v3s16 nodepos_abovesurface
1671 2: stop digging (all parameters ignored)
1673 u8 action = readU8(&data[2]);
1675 p_under.X = readS16(&data[3]);
1676 p_under.Y = readS16(&data[5]);
1677 p_under.Z = readS16(&data[7]);
1679 p_over.X = readS16(&data[9]);
1680 p_over.Y = readS16(&data[11]);
1681 p_over.Z = readS16(&data[13]);
1682 u16 item_i = readU16(&data[15]);
1684 //TODO: Check that target is reasonably close
1696 // Get content at position
1697 content = m_env.getMap().getNode(p_under).d;
1698 // If it's not diggable, do nothing
1699 if(content_diggable(content) == false)
1704 catch(InvalidPositionException &e)
1706 derr_server<<"Server: Not starting digging: Node not found"
1712 Set stuff in RemoteClient
1714 RemoteClient *client = getClient(peer->id);
1715 JMutexAutoLock(client->m_dig_mutex);
1716 client->m_dig_tool_item = 0;
1717 client->m_dig_position = p_under;
1718 float dig_time = 0.5;
1719 if(content == CONTENT_STONE)
1723 else if(content == CONTENT_TORCH)
1727 client->m_dig_time_remaining = dig_time;
1729 // Reset build time counter
1730 getClient(peer->id)->m_time_from_building.set(0.0);
1737 else if(action == 2)
1739 RemoteClient *client = getClient(peer->id);
1740 JMutexAutoLock digmutex(client->m_dig_mutex);
1741 client->m_dig_tool_item = -1;
1747 else if(action == 1)
1750 InventoryList *ilist = player->inventory.getList("main");
1755 InventoryItem *item = ilist->getItem(item_i);
1757 // If there is no item, it is not possible to add it anywhere
1762 Handle material items
1764 if(std::string("MaterialItem") == item->getName())
1767 // Don't add a node if this is not a free space
1768 MapNode n2 = m_env.getMap().getNode(p_over);
1769 if(content_buildable_to(n2.d) == false)
1772 catch(InvalidPositionException &e)
1774 derr_server<<"Server: Ignoring ADDNODE: Node not found"
1779 // Reset build time counter
1780 getClient(peer->id)->m_time_from_building.set(0.0);
1783 MaterialItem *mitem = (MaterialItem*)item;
1785 n.d = mitem->getMaterial();
1786 if(content_directional(n.d))
1787 n.dir = packDir(p_under - p_over);
1791 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
1792 SharedBuffer<u8> reply(replysize);
1793 writeU16(&reply[0], TOCLIENT_ADDNODE);
1794 writeS16(&reply[2], p_over.X);
1795 writeS16(&reply[4], p_over.Y);
1796 writeS16(&reply[6], p_over.Z);
1797 n.serialize(&reply[8], peer_ser_ver);
1799 m_con.SendToAll(0, reply, true);
1804 InventoryList *ilist = player->inventory.getList("main");
1805 if(g_settings.getBool("creative_mode") == false && ilist)
1807 // Remove from inventory and send inventory
1808 if(mitem->getCount() == 1)
1809 ilist->deleteItem(item_i);
1813 SendInventory(peer_id);
1819 This takes some time so it is done after the quick stuff
1821 core::map<v3s16, MapBlock*> modified_blocks;
1822 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
1828 InventoryList *ilist = player->inventory.getList("main");
1829 if(g_settings.getBool("creative_mode") == false && ilist)
1831 // Remove from inventory and send inventory
1832 if(mitem->getCount() == 1)
1833 ilist->deleteItem(item_i);
1837 SendInventory(peer_id);
1843 This takes some time so it is done after the quick stuff
1845 core::map<v3s16, MapBlock*> modified_blocks;
1846 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
1849 Set the modified blocks unsent for all the clients
1852 //JMutexAutoLock lock2(m_con_mutex);
1854 for(core::map<u16, RemoteClient*>::Iterator
1855 i = m_clients.getIterator();
1856 i.atEnd() == false; i++)
1858 RemoteClient *client = i.getNode()->getValue();
1860 if(modified_blocks.size() > 0)
1862 // Remove block from sent history
1863 client->SetBlocksNotSent(modified_blocks);
1872 // Update water pressure around modification
1873 // This also adds it to m_flow_active_nodes if appropriate
1875 MapVoxelManipulator v(&m_env.getMap());
1876 v.m_disable_water_climb =
1877 g_settings.getBool("disable_water_climb");
1879 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
1883 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1885 catch(ProcessingLimitException &e)
1887 dstream<<"Processing limit reached (1)"<<std::endl;
1890 v.blitBack(modified_blocks);
1893 Handle block object items
1895 else if(std::string("MBOItem") == item->getName())
1897 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
1899 /*dout_server<<"Trying to place a MapBlockObjectItem: "
1900 "inventorystring=\""
1901 <<oitem->getInventoryString()
1902 <<"\""<<std::endl;*/
1904 v3s16 blockpos = getNodeBlockPos(p_over);
1906 MapBlock *block = NULL;
1909 block = m_env.getMap().getBlockNoCreate(blockpos);
1911 catch(InvalidPositionException &e)
1913 derr_server<<"Error while placing object: "
1914 "block not found"<<std::endl;
1918 v3s16 block_pos_i_on_map = block->getPosRelative();
1919 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1921 v3f pos = intToFloat(p_over);
1922 pos -= block_pos_f_on_map;
1924 /*dout_server<<"pos="
1925 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1929 MapBlockObject *obj = oitem->createObject
1930 (pos, player->getYaw(), player->getPitch());
1933 derr_server<<"WARNING: oitem created NULL object"
1936 block->addObject(obj);
1938 //dout_server<<"Placed object"<<std::endl;
1940 InventoryList *ilist = player->inventory.getList("main");
1941 if(g_settings.getBool("creative_mode") == false && ilist)
1943 // Remove from inventory and send inventory
1944 ilist->deleteItem(item_i);
1946 SendInventory(peer_id);
1952 Catch invalid actions
1956 derr_server<<"WARNING: Server: Invalid action "
1957 <<action<<std::endl;
1961 else if(command == TOSERVER_RELEASE)
1970 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
1973 else if(command == TOSERVER_SIGNTEXT)
1982 std::string datastring((char*)&data[2], datasize-2);
1983 std::istringstream is(datastring, std::ios_base::binary);
1986 is.read((char*)buf, 6);
1987 v3s16 blockpos = readV3S16(buf);
1988 is.read((char*)buf, 2);
1989 s16 id = readS16(buf);
1990 is.read((char*)buf, 2);
1991 u16 textlen = readU16(buf);
1993 for(u16 i=0; i<textlen; i++)
1995 is.read((char*)buf, 1);
1996 text += (char)buf[0];
1999 MapBlock *block = NULL;
2002 block = m_env.getMap().getBlockNoCreate(blockpos);
2004 catch(InvalidPositionException &e)
2006 derr_server<<"Error while setting sign text: "
2007 "block not found"<<std::endl;
2011 MapBlockObject *obj = block->getObject(id);
2014 derr_server<<"Error while setting sign text: "
2015 "object not found"<<std::endl;
2019 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2021 derr_server<<"Error while setting sign text: "
2022 "object is not a sign"<<std::endl;
2026 ((SignObject*)obj)->setText(text);
2028 obj->getBlock()->setChangedFlag();
2030 else if(command == TOSERVER_INVENTORY_ACTION)
2032 // Ignore inventory changes if in creative mode
2033 if(g_settings.getBool("creative_mode") == true)
2035 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2039 // Strip command and create a stream
2040 std::string datastring((char*)&data[2], datasize-2);
2041 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2042 std::istringstream is(datastring, std::ios_base::binary);
2044 InventoryAction *a = InventoryAction::deSerialize(is);
2048 Handle craftresult specially
2050 bool disable_action = false;
2051 if(a->getType() == IACTION_MOVE)
2053 IMoveAction *ma = (IMoveAction*)a;
2054 // Don't allow moving anything to craftresult
2055 if(ma->to_name == "craftresult")
2058 disable_action = true;
2060 // When something is removed from craftresult
2061 if(ma->from_name == "craftresult")
2063 disable_action = true;
2064 // Remove stuff from craft
2065 InventoryList *clist = player->inventory.getList("craft");
2068 clist->decrementMaterials(ma->count);
2071 // Feed action to player inventory
2072 a->apply(&player->inventory);
2075 // If something appeared in craftresult, throw it
2077 InventoryList *rlist = player->inventory.getList("craftresult");
2078 InventoryList *mlist = player->inventory.getList("main");
2079 if(rlist && mlist && rlist->getUsedSlots() == 1)
2081 InventoryItem *item1 = rlist->changeItem(0, NULL);
2082 mlist->addItem(item1);
2086 if(disable_action == false)
2088 // Feed action to player inventory
2089 a->apply(&player->inventory);
2094 SendInventory(player->peer_id);
2098 dstream<<"TOSERVER_INVENTORY_ACTION: "
2099 <<"InventoryAction::deSerialize() returned NULL"
2105 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2106 "unknown command "<<command<<std::endl;
2110 catch(SendFailedException &e)
2112 derr_server<<"Server::ProcessData(): SendFailedException: "
2118 /*void Server::Send(u16 peer_id, u16 channelnum,
2119 SharedBuffer<u8> data, bool reliable)
2121 JMutexAutoLock lock(m_con_mutex);
2122 m_con.Send(peer_id, channelnum, data, reliable);
2125 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2127 DSTACK(__FUNCTION_NAME);
2129 Create a packet with the block in the right format
2132 std::ostringstream os(std::ios_base::binary);
2133 block->serialize(os, ver);
2134 std::string s = os.str();
2135 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2137 u32 replysize = 8 + blockdata.getSize();
2138 SharedBuffer<u8> reply(replysize);
2139 v3s16 p = block->getPos();
2140 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2141 writeS16(&reply[2], p.X);
2142 writeS16(&reply[4], p.Y);
2143 writeS16(&reply[6], p.Z);
2144 memcpy(&reply[8], *blockdata, blockdata.getSize());
2146 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2147 <<": \tpacket size: "<<replysize<<std::endl;*/
2152 m_con.Send(peer_id, 1, reply, true);
2155 core::list<PlayerInfo> Server::getPlayerInfo()
2157 DSTACK(__FUNCTION_NAME);
2158 JMutexAutoLock envlock(m_env_mutex);
2159 JMutexAutoLock conlock(m_con_mutex);
2161 core::list<PlayerInfo> list;
2163 core::list<Player*> players = m_env.getPlayers();
2165 core::list<Player*>::Iterator i;
2166 for(i = players.begin();
2167 i != players.end(); i++)
2171 Player *player = *i;
2173 con::Peer *peer = m_con.GetPeer(player->peer_id);
2175 info.address = peer->address;
2176 info.avg_rtt = peer->avg_rtt;
2178 catch(con::PeerNotFoundException &e)
2180 // Outdated peer info
2182 info.address = Address(0,0,0,0,0);
2186 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2187 info.position = player->getPosition();
2189 list.push_back(info);
2195 void Server::peerAdded(con::Peer *peer)
2197 DSTACK(__FUNCTION_NAME);
2198 dout_server<<"Server::peerAdded(): peer->id="
2199 <<peer->id<<std::endl;
2201 // Connection is already locked when this is called.
2202 //JMutexAutoLock lock(m_con_mutex);
2205 core::map<u16, RemoteClient*>::Node *n;
2206 n = m_clients.find(peer->id);
2207 // The client shouldn't already exist
2211 RemoteClient *client = new RemoteClient();
2212 client->peer_id = peer->id;
2213 m_clients.insert(client->peer_id, client);
2217 // Already locked when called
2218 //JMutexAutoLock envlock(m_env_mutex);
2220 Player *player = m_env.getPlayer(peer->id);
2222 // The player shouldn't already exist
2223 assert(player == NULL);
2225 player = new ServerRemotePlayer();
2226 player->peer_id = peer->id;
2232 // We're going to throw the player to this position
2233 //v2s16 nodepos(29990,29990);
2234 //v2s16 nodepos(9990,9990);
2236 v2s16 sectorpos = getNodeSectorPos(nodepos);
2237 // Get zero sector (it could have been unloaded to disk)
2238 m_env.getMap().emergeSector(sectorpos);
2239 // Get ground height at origin
2240 f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
2241 // The sector should have been generated -> groundheight exists
2242 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
2243 // Don't go underwater
2244 if(groundheight < WATER_LEVEL)
2245 groundheight = WATER_LEVEL;
2247 player->setPosition(intToFloat(v3s16(
2254 Add player to environment
2257 m_env.addPlayer(player);
2260 Add stuff to inventory
2263 if(g_settings.getBool("creative_mode"))
2265 // Give all materials
2266 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
2267 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
2269 // Skip some materials
2270 if(i == CONTENT_OCEAN)
2273 InventoryItem *item = new MaterialItem(i, 1);
2274 player->inventory.addItem("main", item);
2278 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
2279 bool r = player->inventory.addItem("main", item);
2284 InventoryItem *item = new MapBlockObjectItem("Rat");
2285 bool r = player->inventory.addItem("main", item);
2291 /*// Give some lights
2293 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
2294 bool r = player->inventory.addItem("main", item);
2298 for(u16 i=0; i<4; i++)
2300 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
2301 bool r = player->inventory.addItem("main", item);
2304 /*// Give some other stuff
2306 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
2307 bool r = player->inventory.addItem("main", item);
2314 void Server::deletingPeer(con::Peer *peer, bool timeout)
2316 DSTACK(__FUNCTION_NAME);
2317 dout_server<<"Server::deletingPeer(): peer->id="
2318 <<peer->id<<", timeout="<<timeout<<std::endl;
2320 // Connection is already locked when this is called.
2321 //JMutexAutoLock lock(m_con_mutex);
2324 core::map<u16, RemoteClient*>::Node *n;
2325 n = m_clients.find(peer->id);
2326 // The client should exist
2331 // Already locked when called
2332 //JMutexAutoLock envlock(m_env_mutex);
2333 m_env.removePlayer(peer->id);
2337 delete m_clients[peer->id];
2338 m_clients.remove(peer->id);
2340 // Send player info to all clients
2344 void Server::SendObjectData(float dtime)
2346 DSTACK(__FUNCTION_NAME);
2348 core::map<v3s16, bool> stepped_blocks;
2350 for(core::map<u16, RemoteClient*>::Iterator
2351 i = m_clients.getIterator();
2352 i.atEnd() == false; i++)
2354 u16 peer_id = i.getNode()->getKey();
2355 RemoteClient *client = i.getNode()->getValue();
2356 assert(client->peer_id == peer_id);
2358 if(client->serialization_version == SER_FMT_VER_INVALID)
2361 client->SendObjectData(this, dtime, stepped_blocks);
2365 void Server::SendPlayerInfos()
2367 DSTACK(__FUNCTION_NAME);
2369 //JMutexAutoLock envlock(m_env_mutex);
2371 core::list<Player*> players = m_env.getPlayers();
2373 u32 player_count = players.getSize();
2374 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2376 SharedBuffer<u8> data(datasize);
2377 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2380 core::list<Player*>::Iterator i;
2381 for(i = players.begin();
2382 i != players.end(); i++)
2384 Player *player = *i;
2386 /*dstream<<"Server sending player info for player with "
2387 "peer_id="<<player->peer_id<<std::endl;*/
2389 writeU16(&data[start], player->peer_id);
2390 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2391 start += 2+PLAYERNAME_SIZE;
2394 //JMutexAutoLock conlock(m_con_mutex);
2397 m_con.SendToAll(0, data, true);
2400 void Server::SendInventory(u16 peer_id)
2402 DSTACK(__FUNCTION_NAME);
2404 //JMutexAutoLock envlock(m_env_mutex);
2406 Player* player = m_env.getPlayer(peer_id);
2409 Calculate crafting stuff
2412 InventoryList *clist = player->inventory.getList("craft");
2413 InventoryList *rlist = player->inventory.getList("craftresult");
2416 //rlist->clearItems();
2420 InventoryItem *items[9];
2421 for(u16 i=0; i<9; i++)
2423 items[i] = clist->getItem(i);
2426 if(clist->getUsedSlots() == 1 && items[0])
2428 if((std::string)items[0]->getName() == "MaterialItem")
2430 MaterialItem *mitem = (MaterialItem*)items[0];
2431 if(mitem->getMaterial() == CONTENT_TREE)
2433 rlist->addItem(new MapBlockObjectItem("Sign"));
2438 if(clist->getUsedSlots() == 2 && items[0] && items[3])
2441 (std::string)items[0]->getName() == "MaterialItem"
2442 && ((MaterialItem*)items[0])->getMaterial() == CONTENT_COALSTONE
2443 && (std::string)items[3]->getName() == "MaterialItem"
2444 && ((MaterialItem*)items[3])->getMaterial() == CONTENT_TREE
2447 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2456 std::ostringstream os;
2457 //os.imbue(std::locale("C"));
2459 player->inventory.serialize(os);
2461 std::string s = os.str();
2463 SharedBuffer<u8> data(s.size()+2);
2464 writeU16(&data[0], TOCLIENT_INVENTORY);
2465 memcpy(&data[2], s.c_str(), s.size());
2467 //JMutexAutoLock conlock(m_con_mutex);
2470 m_con.Send(peer_id, 0, data, true);
2473 void Server::SendBlocks(float dtime)
2475 DSTACK(__FUNCTION_NAME);
2477 JMutexAutoLock envlock(m_env_mutex);
2479 core::array<PrioritySortedBlockTransfer> queue;
2481 s32 total_sending = 0;
2483 for(core::map<u16, RemoteClient*>::Iterator
2484 i = m_clients.getIterator();
2485 i.atEnd() == false; i++)
2487 RemoteClient *client = i.getNode()->getValue();
2488 assert(client->peer_id == i.getNode()->getKey());
2490 total_sending += client->SendingCount();
2492 if(client->serialization_version == SER_FMT_VER_INVALID)
2495 client->GetNextBlocks(this, dtime, queue);
2499 // Lowest priority number comes first.
2500 // Lowest is most important.
2503 JMutexAutoLock conlock(m_con_mutex);
2505 for(u32 i=0; i<queue.size(); i++)
2507 //TODO: Calculate limit dynamically
2508 if(total_sending >= g_settings.getS32
2509 ("max_simultaneous_block_sends_server_total"))
2512 PrioritySortedBlockTransfer q = queue[i];
2514 MapBlock *block = NULL;
2517 block = m_env.getMap().getBlockNoCreate(q.pos);
2519 catch(InvalidPositionException &e)
2524 RemoteClient *client = getClient(q.peer_id);
2526 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2528 client->SentBlock(q.pos);
2535 RemoteClient* Server::getClient(u16 peer_id)
2537 DSTACK(__FUNCTION_NAME);
2538 //JMutexAutoLock lock(m_con_mutex);
2539 core::map<u16, RemoteClient*>::Node *n;
2540 n = m_clients.find(peer_id);
2541 // A client should exist for all peers
2543 return n->getValue();
2546 void Server::UpdateBlockWaterPressure(MapBlock *block,
2547 core::map<v3s16, MapBlock*> &modified_blocks)
2549 MapVoxelManipulator v(&m_env.getMap());
2550 v.m_disable_water_climb =
2551 g_settings.getBool("disable_water_climb");
2553 VoxelArea area(block->getPosRelative(),
2554 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
2558 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2560 catch(ProcessingLimitException &e)
2562 dstream<<"Processing limit reached (1)"<<std::endl;
2565 v.blitBack(modified_blocks);