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)*/
360 if(m_time_from_building.m_value < g_settings.getFloat(
361 "full_block_send_enable_min_time_from_building"))
363 maximum_simultaneous_block_sends
364 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
368 u32 num_blocks_selected;
370 JMutexAutoLock lock(m_blocks_sending_mutex);
371 num_blocks_selected = m_blocks_sending.size();
375 next time d will be continued from the d from which the nearest
376 unsent block was found this time.
378 This is because not necessarily any of the blocks found this
379 time are actually sent.
381 s32 new_nearest_unsent_d = -1;
383 // Serialization version used
384 //u8 ser_version = serialization_version;
386 //bool has_incomplete_blocks = false;
388 s16 d_max = g_settings.getS16("max_block_send_distance");
389 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
391 //dstream<<"Starting from "<<d_start<<std::endl;
393 for(s16 d = d_start; d <= d_max; d++)
395 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
397 //if(has_incomplete_blocks == false)
399 JMutexAutoLock lock(m_blocks_sent_mutex);
401 If m_nearest_unsent_d was changed by the EmergeThread
402 (it can change it to 0 through SetBlockNotSent),
404 Else update m_nearest_unsent_d
406 if(m_nearest_unsent_d != last_nearest_unsent_d)
408 d = m_nearest_unsent_d;
409 last_nearest_unsent_d = m_nearest_unsent_d;
414 Get the border/face dot coordinates of a "d-radiused"
417 core::list<v3s16> list;
418 getFacePositions(list, d);
420 core::list<v3s16>::Iterator li;
421 for(li=list.begin(); li!=list.end(); li++)
423 v3s16 p = *li + center;
427 - Don't allow too many simultaneous transfers
428 - EXCEPT when the blocks are very close
430 Also, don't send blocks that are already flying.
433 u16 maximum_simultaneous_block_sends_now =
434 maximum_simultaneous_block_sends;
436 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
438 maximum_simultaneous_block_sends_now =
439 maximum_simultaneous_block_sends_setting;
443 JMutexAutoLock lock(m_blocks_sending_mutex);
445 // Limit is dynamically lowered when building
446 if(num_blocks_selected
447 >= maximum_simultaneous_block_sends_now)
449 /*dstream<<"Not sending more blocks. Queue full. "
450 <<m_blocks_sending.size()
455 if(m_blocks_sending.find(p) != NULL)
462 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
467 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
470 bool generate = d <= d_max_gen;
472 // Limit the generating area vertically to half
473 if(abs(p.Y - center.Y) > d_max_gen / 2)
477 Don't send already sent blocks
480 JMutexAutoLock lock(m_blocks_sent_mutex);
482 if(m_blocks_sent.find(p) != NULL)
487 Check if map has this block
489 MapBlock *block = NULL;
492 block = server->m_env.getMap().getBlockNoCreate(p);
494 catch(InvalidPositionException &e)
498 bool surely_not_found_on_disk = false;
501 /*if(block->isIncomplete())
503 has_incomplete_blocks = true;
509 surely_not_found_on_disk = true;
514 If block has been marked to not exist on disk (dummy)
515 and generating new ones is not wanted, skip block.
517 if(generate == false && surely_not_found_on_disk == true)
524 Record the lowest d from which a a block has been
525 found being not sent and possibly to exist
527 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
529 new_nearest_unsent_d = d;
533 Add inexistent block to emerge queue.
535 if(block == NULL || surely_not_found_on_disk)
537 /*SharedPtr<JMutexAutoLock> lock
538 (m_num_blocks_in_emerge_queue.getLock());*/
540 //TODO: Get value from somewhere
541 // Allow only one block in emerge queue
542 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
544 // Add it to the emerge queue and trigger the thread
547 if(generate == false)
548 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
550 server->m_emerge_queue.addBlock(peer_id, p, flags);
551 server->m_emergethread.trigger();
562 PrioritySortedBlockTransfer q((float)d, p, peer_id);
566 num_blocks_selected += 1;
571 if(new_nearest_unsent_d != -1)
573 JMutexAutoLock lock(m_blocks_sent_mutex);
574 m_nearest_unsent_d = new_nearest_unsent_d;
578 void RemoteClient::SendObjectData(
581 core::map<v3s16, bool> &stepped_blocks
584 DSTACK(__FUNCTION_NAME);
586 // Can't send anything without knowing version
587 if(serialization_version == SER_FMT_VER_INVALID)
589 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
595 Send a TOCLIENT_OBJECTDATA packet.
599 u16 number of player positions
610 std::ostringstream os(std::ios_base::binary);
614 writeU16(buf, TOCLIENT_OBJECTDATA);
615 os.write((char*)buf, 2);
618 Get and write player data
621 core::list<Player*> players = server->m_env.getPlayers();
623 // Write player count
624 u16 playercount = players.size();
625 writeU16(buf, playercount);
626 os.write((char*)buf, 2);
628 core::list<Player*>::Iterator i;
629 for(i = players.begin();
630 i != players.end(); i++)
634 v3f pf = player->getPosition();
635 v3f sf = player->getSpeed();
637 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
638 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
639 s32 pitch_i (player->getPitch() * 100);
640 s32 yaw_i (player->getYaw() * 100);
642 writeU16(buf, player->peer_id);
643 os.write((char*)buf, 2);
644 writeV3S32(buf, position_i);
645 os.write((char*)buf, 12);
646 writeV3S32(buf, speed_i);
647 os.write((char*)buf, 12);
648 writeS32(buf, pitch_i);
649 os.write((char*)buf, 4);
650 writeS32(buf, yaw_i);
651 os.write((char*)buf, 4);
655 Get and write object data
661 For making players to be able to build to their nearby
662 environment (building is not possible on blocks that are not
665 - Add blocks to emerge queue if they are not found
667 SUGGESTION: These could be ignored from the backside of the player
670 Player *player = server->m_env.getPlayer(peer_id);
672 v3f playerpos = player->getPosition();
673 v3f playerspeed = player->getSpeed();
675 v3s16 center_nodepos = floatToInt(playerpos);
676 v3s16 center = getNodeBlockPos(center_nodepos);
678 s16 d_max = g_settings.getS16("active_object_range");
680 // Number of blocks whose objects were written to bos
683 std::ostringstream bos(std::ios_base::binary);
685 for(s16 d = 0; d <= d_max; d++)
687 core::list<v3s16> list;
688 getFacePositions(list, d);
690 core::list<v3s16>::Iterator li;
691 for(li=list.begin(); li!=list.end(); li++)
693 v3s16 p = *li + center;
696 Ignore blocks that haven't been sent to the client
699 JMutexAutoLock sentlock(m_blocks_sent_mutex);
700 if(m_blocks_sent.find(p) == NULL)
704 // Try stepping block and add it to a send queue
709 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
712 Step block if not in stepped_blocks and add to stepped_blocks.
714 if(stepped_blocks.find(p) == NULL)
716 block->stepObjects(dtime, true, server->getDayNightRatio());
717 stepped_blocks.insert(p, true);
718 block->setChangedFlag();
721 // Skip block if there are no objects
722 if(block->getObjectCount() == 0)
731 bos.write((char*)buf, 6);
734 block->serializeObjects(bos, serialization_version);
739 Stop collecting objects if data is already too big
741 // Sum of player and object data sizes
742 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
743 // break out if data too big
744 if(sum > MAX_OBJECTDATA_SIZE)
746 goto skip_subsequent;
750 catch(InvalidPositionException &e)
753 // Add it to the emerge queue and trigger the thread.
754 // Fetch the block only if it is on disk.
756 // Grab and increment counter
757 /*SharedPtr<JMutexAutoLock> lock
758 (m_num_blocks_in_emerge_queue.getLock());
759 m_num_blocks_in_emerge_queue.m_value++;*/
761 // Add to queue as an anonymous fetch from disk
762 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
763 server->m_emerge_queue.addBlock(0, p, flags);
764 server->m_emergethread.trigger();
772 writeU16(buf, blockcount);
773 os.write((char*)buf, 2);
775 // Write block objects
782 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
785 std::string s = os.str();
786 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
787 // Send as unreliable
788 server->m_con.Send(peer_id, 0, data, false);
791 void RemoteClient::GotBlock(v3s16 p)
793 JMutexAutoLock lock(m_blocks_sending_mutex);
794 JMutexAutoLock lock2(m_blocks_sent_mutex);
795 if(m_blocks_sending.find(p) != NULL)
796 m_blocks_sending.remove(p);
798 dstream<<"RemoteClient::GotBlock(): Didn't find in"
799 " m_blocks_sending"<<std::endl;
800 m_blocks_sent.insert(p, true);
803 void RemoteClient::SentBlock(v3s16 p)
805 JMutexAutoLock lock(m_blocks_sending_mutex);
806 if(m_blocks_sending.size() > 15)
808 dstream<<"RemoteClient::SentBlock(): "
809 <<"m_blocks_sending.size()="
810 <<m_blocks_sending.size()<<std::endl;
812 if(m_blocks_sending.find(p) == NULL)
813 m_blocks_sending.insert(p, 0.0);
815 dstream<<"RemoteClient::SentBlock(): Sent block"
816 " already in m_blocks_sending"<<std::endl;
819 void RemoteClient::SetBlockNotSent(v3s16 p)
821 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
822 JMutexAutoLock sentlock(m_blocks_sent_mutex);
824 m_nearest_unsent_d = 0;
826 if(m_blocks_sending.find(p) != NULL)
827 m_blocks_sending.remove(p);
828 if(m_blocks_sent.find(p) != NULL)
829 m_blocks_sent.remove(p);
832 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
834 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
835 JMutexAutoLock sentlock(m_blocks_sent_mutex);
837 m_nearest_unsent_d = 0;
839 for(core::map<v3s16, MapBlock*>::Iterator
840 i = blocks.getIterator();
841 i.atEnd()==false; i++)
843 v3s16 p = i.getNode()->getKey();
845 if(m_blocks_sending.find(p) != NULL)
846 m_blocks_sending.remove(p);
847 if(m_blocks_sent.find(p) != NULL)
848 m_blocks_sent.remove(p);
852 /*void RemoteClient::BlockEmerged()
854 SharedPtr<JMutexAutoLock> lock(m_num_blocks_in_emerge_queue.getLock());
855 assert(m_num_blocks_in_emerge_queue.m_value > 0);
856 m_num_blocks_in_emerge_queue.m_value--;
859 /*void RemoteClient::RunSendingTimeouts(float dtime, float timeout)
861 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
863 core::list<v3s16> remove_queue;
864 for(core::map<v3s16, float>::Iterator
865 i = m_blocks_sending.getIterator();
866 i.atEnd()==false; i++)
868 v3s16 p = i.getNode()->getKey();
869 float t = i.getNode()->getValue();
871 i.getNode()->setValue(t);
875 remove_queue.push_back(p);
878 for(core::list<v3s16>::Iterator
879 i = remove_queue.begin();
880 i != remove_queue.end(); i++)
882 m_blocks_sending.remove(*i);
890 PlayerInfo::PlayerInfo()
895 void PlayerInfo::PrintLine(std::ostream *s)
897 (*s)<<id<<": \""<<name<<"\" ("
898 <<position.X<<","<<position.Y
899 <<","<<position.Z<<") ";
901 (*s)<<" avg_rtt="<<avg_rtt;
905 u32 PIChecksum(core::list<PlayerInfo> &l)
907 core::list<PlayerInfo>::Iterator i;
910 for(i=l.begin(); i!=l.end(); i++)
912 checksum += a * (i->id+1);
913 checksum ^= 0x435aafcd;
924 std::string mapsavedir,
928 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
929 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
931 m_emergethread(this),
934 m_time_of_day_send_timer(0)
936 m_flowwater_timer = 0.0;
937 m_print_info_timer = 0.0;
938 m_objectdata_timer = 0.0;
939 m_emergethread_trigger_timer = 0.0;
940 m_savemap_timer = 0.0;
944 m_step_dtime_mutex.Init();
953 JMutexAutoLock clientslock(m_con_mutex);
955 for(core::map<u16, RemoteClient*>::Iterator
956 i = m_clients.getIterator();
957 i.atEnd() == false; i++)
959 u16 peer_id = i.getNode()->getKey();
963 JMutexAutoLock envlock(m_env_mutex);
964 m_env.removePlayer(peer_id);
968 delete i.getNode()->getValue();
972 void Server::start(unsigned short port)
974 DSTACK(__FUNCTION_NAME);
975 // Stop thread if already running
978 // Initialize connection
979 m_con.setTimeoutMs(30);
983 m_thread.setRun(true);
986 dout_server<<"Server started on port "<<port<<std::endl;
991 DSTACK(__FUNCTION_NAME);
992 // Stop threads (set run=false first so both start stopping)
993 m_thread.setRun(false);
994 m_emergethread.setRun(false);
996 m_emergethread.stop();
998 dout_server<<"Server threads stopped"<<std::endl;
1001 void Server::step(float dtime)
1003 DSTACK(__FUNCTION_NAME);
1008 JMutexAutoLock lock(m_step_dtime_mutex);
1009 m_step_dtime += dtime;
1013 void Server::AsyncRunStep()
1015 DSTACK(__FUNCTION_NAME);
1019 JMutexAutoLock lock1(m_step_dtime_mutex);
1020 dtime = m_step_dtime;
1023 // Send blocks to clients
1030 JMutexAutoLock lock1(m_step_dtime_mutex);
1031 m_step_dtime -= dtime;
1035 Update m_time_of_day
1038 m_time_counter += dtime;
1039 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1040 u32 units = (u32)(m_time_counter*speed);
1041 m_time_counter -= (f32)units / speed;
1042 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1044 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1047 Send to clients at constant intervals
1050 m_time_of_day_send_timer -= dtime;
1051 if(m_time_of_day_send_timer < 0.0)
1053 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1055 //JMutexAutoLock envlock(m_env_mutex);
1056 JMutexAutoLock conlock(m_con_mutex);
1058 for(core::map<u16, RemoteClient*>::Iterator
1059 i = m_clients.getIterator();
1060 i.atEnd() == false; i++)
1062 RemoteClient *client = i.getNode()->getValue();
1063 //Player *player = m_env.getPlayer(client->peer_id);
1065 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1066 m_time_of_day.get());
1068 m_con.Send(client->peer_id, 0, data, true);
1073 //dstream<<"Server steps "<<dtime<<std::endl;
1075 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1077 // Has to be locked for peerAdded/Removed
1078 JMutexAutoLock lock1(m_env_mutex);
1079 // Process connection's timeouts
1080 JMutexAutoLock lock2(m_con_mutex);
1081 m_con.RunTimeouts(dtime);
1085 // This also runs Map's timers
1086 JMutexAutoLock lock(m_env_mutex);
1100 if(g_settings.getBool("endless_water") == false)
1105 float &counter = m_flowwater_timer;
1107 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1112 core::map<v3s16, MapBlock*> modified_blocks;
1116 JMutexAutoLock envlock(m_env_mutex);
1118 MapVoxelManipulator v(&m_env.getMap());
1119 v.m_disable_water_climb =
1120 g_settings.getBool("disable_water_climb");
1122 if(g_settings.getBool("endless_water") == false)
1123 v.flowWater(m_flow_active_nodes, 0, false, 250);
1125 v.flowWater(m_flow_active_nodes, 0, false, 50);
1127 v.blitBack(modified_blocks);
1129 ServerMap &map = ((ServerMap&)m_env.getMap());
1132 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1133 map.updateLighting(modified_blocks, lighting_modified_blocks);
1135 // Add blocks modified by lighting to modified_blocks
1136 for(core::map<v3s16, MapBlock*>::Iterator
1137 i = lighting_modified_blocks.getIterator();
1138 i.atEnd() == false; i++)
1140 MapBlock *block = i.getNode()->getValue();
1141 modified_blocks.insert(block->getPos(), block);
1146 Set the modified blocks unsent for all the clients
1149 JMutexAutoLock lock2(m_con_mutex);
1151 for(core::map<u16, RemoteClient*>::Iterator
1152 i = m_clients.getIterator();
1153 i.atEnd() == false; i++)
1155 RemoteClient *client = i.getNode()->getValue();
1157 if(modified_blocks.size() > 0)
1159 // Remove block from sent history
1160 client->SetBlocksNotSent(modified_blocks);
1164 } // interval counter
1167 // Periodically print some info
1169 float &counter = m_print_info_timer;
1175 JMutexAutoLock lock2(m_con_mutex);
1177 for(core::map<u16, RemoteClient*>::Iterator
1178 i = m_clients.getIterator();
1179 i.atEnd() == false; i++)
1181 //u16 peer_id = i.getNode()->getKey();
1182 RemoteClient *client = i.getNode()->getValue();
1183 client->PrintInfo(std::cout);
1191 NOTE: Some of this could be moved to RemoteClient
1195 JMutexAutoLock envlock(m_env_mutex);
1196 JMutexAutoLock conlock(m_con_mutex);
1198 for(core::map<u16, RemoteClient*>::Iterator
1199 i = m_clients.getIterator();
1200 i.atEnd() == false; i++)
1202 RemoteClient *client = i.getNode()->getValue();
1203 Player *player = m_env.getPlayer(client->peer_id);
1205 JMutexAutoLock digmutex(client->m_dig_mutex);
1207 if(client->m_dig_tool_item == -1)
1210 client->m_dig_time_remaining -= dtime;
1212 if(client->m_dig_time_remaining > 0)
1214 client->m_time_from_building.set(0.0);
1218 v3s16 p_under = client->m_dig_position;
1220 // Mandatory parameter; actually used for nothing
1221 core::map<v3s16, MapBlock*> modified_blocks;
1227 // Get material at position
1228 material = m_env.getMap().getNode(p_under).d;
1229 // If it's not diggable, do nothing
1230 if(content_diggable(material) == false)
1232 derr_server<<"Server: Not finishing digging: Node not diggable"
1234 client->m_dig_tool_item = -1;
1238 catch(InvalidPositionException &e)
1240 derr_server<<"Server: Not finishing digging: Node not found"
1242 client->m_dig_tool_item = -1;
1248 SharedBuffer<u8> reply(replysize);
1249 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1250 writeS16(&reply[2], p_under.X);
1251 writeS16(&reply[4], p_under.Y);
1252 writeS16(&reply[6], p_under.Z);
1254 m_con.SendToAll(0, reply, true);
1256 if(g_settings.getBool("creative_mode") == false)
1258 // Add to inventory and send inventory
1259 InventoryItem *item = new MaterialItem(material, 1);
1260 player->inventory.addItem("main", item);
1261 SendInventory(player->peer_id);
1266 (this takes some time so it is done after the quick stuff)
1268 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1274 // Update water pressure around modification
1275 // This also adds it to m_flow_active_nodes if appropriate
1277 MapVoxelManipulator v(&m_env.getMap());
1278 v.m_disable_water_climb =
1279 g_settings.getBool("disable_water_climb");
1281 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1285 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1287 catch(ProcessingLimitException &e)
1289 dstream<<"Processing limit reached (1)"<<std::endl;
1292 v.blitBack(modified_blocks);
1297 // Send object positions
1299 float &counter = m_objectdata_timer;
1301 if(counter >= g_settings.getFloat("objectdata_interval"))
1303 JMutexAutoLock lock1(m_env_mutex);
1304 JMutexAutoLock lock2(m_con_mutex);
1305 SendObjectData(counter);
1311 // Trigger emergethread (it gets somehow gets to a
1312 // non-triggered but bysy state sometimes)
1314 float &counter = m_emergethread_trigger_timer;
1320 m_emergethread.trigger();
1326 float &counter = m_savemap_timer;
1328 if(counter >= g_settings.getFloat("server_map_save_interval"))
1332 JMutexAutoLock lock(m_env_mutex);
1334 // Save only changed parts
1335 m_env.getMap().save(true);
1337 // Delete unused sectors
1338 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1339 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1340 if(deleted_count > 0)
1342 dout_server<<"Server: Unloaded "<<deleted_count
1343 <<" sectors from memory"<<std::endl;
1349 void Server::Receive()
1351 DSTACK(__FUNCTION_NAME);
1352 u32 data_maxsize = 10000;
1353 Buffer<u8> data(data_maxsize);
1358 JMutexAutoLock lock(m_con_mutex);
1359 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1361 ProcessData(*data, datasize, peer_id);
1363 catch(con::InvalidIncomingDataException &e)
1365 derr_server<<"Server::Receive(): "
1366 "InvalidIncomingDataException: what()="
1367 <<e.what()<<std::endl;
1369 catch(con::PeerNotFoundException &e)
1371 //NOTE: This is not needed anymore
1373 // The peer has been disconnected.
1374 // Find the associated player and remove it.
1376 /*JMutexAutoLock envlock(m_env_mutex);
1378 dout_server<<"ServerThread: peer_id="<<peer_id
1379 <<" has apparently closed connection. "
1380 <<"Removing player."<<std::endl;
1382 m_env.removePlayer(peer_id);*/
1386 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1388 DSTACK(__FUNCTION_NAME);
1389 // Environment is locked first.
1390 JMutexAutoLock envlock(m_env_mutex);
1391 JMutexAutoLock conlock(m_con_mutex);
1395 peer = m_con.GetPeer(peer_id);
1397 catch(con::PeerNotFoundException &e)
1399 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1400 <<peer_id<<" not found"<<std::endl;
1404 //u8 peer_ser_ver = peer->serialization_version;
1405 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1413 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1415 if(command == TOSERVER_INIT)
1417 // [0] u16 TOSERVER_INIT
1418 // [2] u8 SER_FMT_VER_HIGHEST
1419 // [3] u8[20] player_name
1424 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1425 <<peer->id<<std::endl;
1427 // First byte after command is maximum supported
1428 // serialization version
1429 u8 client_max = data[2];
1430 u8 our_max = SER_FMT_VER_HIGHEST;
1431 // Use the highest version supported by both
1432 u8 deployed = core::min_(client_max, our_max);
1433 // If it's lower than the lowest supported, give up.
1434 if(deployed < SER_FMT_VER_LOWEST)
1435 deployed = SER_FMT_VER_INVALID;
1437 //peer->serialization_version = deployed;
1438 getClient(peer->id)->pending_serialization_version = deployed;
1440 if(deployed == SER_FMT_VER_INVALID)
1442 derr_server<<DTIME<<"Server: Cannot negotiate "
1443 "serialization version with peer "
1444 <<peer_id<<std::endl;
1452 Player *player = m_env.getPlayer(peer_id);
1454 // Check if player doesn't exist
1456 throw con::InvalidIncomingDataException
1457 ("Server::ProcessData(): INIT: Player doesn't exist");
1459 // update name if it was supplied
1460 if(datasize >= 20+3)
1463 player->updateName((const char*)&data[3]);
1466 // Now answer with a TOCLIENT_INIT
1468 SharedBuffer<u8> reply(2+1+6);
1469 writeU16(&reply[0], TOCLIENT_INIT);
1470 writeU8(&reply[2], deployed);
1471 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1473 m_con.Send(peer_id, 0, reply, true);
1477 if(command == TOSERVER_INIT2)
1479 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1480 <<peer->id<<std::endl;
1483 getClient(peer->id)->serialization_version
1484 = getClient(peer->id)->pending_serialization_version;
1487 Send some initialization data
1490 // Send player info to all players
1493 // Send inventory to player
1494 SendInventory(peer->id);
1498 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1499 m_time_of_day.get());
1500 m_con.Send(peer->id, 0, data, true);
1503 // Send information about joining in chat
1505 std::wstring name = L"unknown";
1506 Player *player = m_env.getPlayer(peer_id);
1508 name = narrow_to_wide(player->getName());
1510 std::wstring message;
1513 message += L" joined game";
1514 BroadcastChatMessage(message);
1520 if(peer_ser_ver == SER_FMT_VER_INVALID)
1522 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1523 " serialization format invalid or not initialized."
1524 " Skipping incoming command="<<command<<std::endl;
1528 Player *player = m_env.getPlayer(peer_id);
1531 derr_server<<"Server::ProcessData(): Cancelling: "
1532 "No player for peer_id="<<peer_id
1536 if(command == TOSERVER_PLAYERPOS)
1538 if(datasize < 2+12+12+4+4)
1542 v3s32 ps = readV3S32(&data[start+2]);
1543 v3s32 ss = readV3S32(&data[start+2+12]);
1544 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1545 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1546 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1547 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1548 pitch = wrapDegrees(pitch);
1549 yaw = wrapDegrees(yaw);
1550 player->setPosition(position);
1551 player->setSpeed(speed);
1552 player->setPitch(pitch);
1553 player->setYaw(yaw);
1555 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1556 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1557 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1559 else if(command == TOSERVER_GOTBLOCKS)
1572 u16 count = data[2];
1573 for(u16 i=0; i<count; i++)
1575 if((s16)datasize < 2+1+(i+1)*6)
1576 throw con::InvalidIncomingDataException
1577 ("GOTBLOCKS length is too short");
1578 v3s16 p = readV3S16(&data[2+1+i*6]);
1579 /*dstream<<"Server: GOTBLOCKS ("
1580 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1581 RemoteClient *client = getClient(peer_id);
1582 client->GotBlock(p);
1585 else if(command == TOSERVER_DELETEDBLOCKS)
1598 u16 count = data[2];
1599 for(u16 i=0; i<count; i++)
1601 if((s16)datasize < 2+1+(i+1)*6)
1602 throw con::InvalidIncomingDataException
1603 ("DELETEDBLOCKS length is too short");
1604 v3s16 p = readV3S16(&data[2+1+i*6]);
1605 /*dstream<<"Server: DELETEDBLOCKS ("
1606 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1607 RemoteClient *client = getClient(peer_id);
1608 client->SetBlockNotSent(p);
1611 else if(command == TOSERVER_CLICK_OBJECT)
1618 [2] u8 button (0=left, 1=right)
1623 u8 button = readU8(&data[2]);
1625 p.X = readS16(&data[3]);
1626 p.Y = readS16(&data[5]);
1627 p.Z = readS16(&data[7]);
1628 s16 id = readS16(&data[9]);
1629 //u16 item_i = readU16(&data[11]);
1631 MapBlock *block = NULL;
1634 block = m_env.getMap().getBlockNoCreate(p);
1636 catch(InvalidPositionException &e)
1638 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1642 MapBlockObject *obj = block->getObject(id);
1646 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1650 //TODO: Check that object is reasonably close
1655 InventoryList *ilist = player->inventory.getList("main");
1656 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1659 // Skip if inventory has no free space
1660 if(ilist->getUsedSlots() == ilist->getSize())
1662 dout_server<<"Player inventory has no free space"<<std::endl;
1667 Create the inventory item
1669 InventoryItem *item = NULL;
1670 // If it is an item-object, take the item from it
1671 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1673 item = ((ItemObject*)obj)->createInventoryItem();
1675 // Else create an item of the object
1678 item = new MapBlockObjectItem
1679 (obj->getInventoryString());
1682 // Add to inventory and send inventory
1683 ilist->addItem(item);
1684 SendInventory(player->peer_id);
1687 // Remove from block
1688 block->removeObject(id);
1691 else if(command == TOSERVER_GROUND_ACTION)
1699 [3] v3s16 nodepos_undersurface
1700 [9] v3s16 nodepos_abovesurface
1705 2: stop digging (all parameters ignored)
1707 u8 action = readU8(&data[2]);
1709 p_under.X = readS16(&data[3]);
1710 p_under.Y = readS16(&data[5]);
1711 p_under.Z = readS16(&data[7]);
1713 p_over.X = readS16(&data[9]);
1714 p_over.Y = readS16(&data[11]);
1715 p_over.Z = readS16(&data[13]);
1716 u16 item_i = readU16(&data[15]);
1718 //TODO: Check that target is reasonably close
1726 NOTE: This can be used in the future to check if
1727 somebody is cheating, by checking the timing.
1735 // Get content at position
1736 content = m_env.getMap().getNode(p_under).d;
1737 // If it's not diggable, do nothing
1738 if(content_diggable(content) == false)
1743 catch(InvalidPositionException &e)
1745 derr_server<<"Server: Not starting digging: Node not found"
1751 Set stuff in RemoteClient
1753 RemoteClient *client = getClient(peer->id);
1754 JMutexAutoLock(client->m_dig_mutex);
1755 client->m_dig_tool_item = 0;
1756 client->m_dig_position = p_under;
1757 float dig_time = 0.5;
1758 if(content == CONTENT_STONE)
1762 else if(content == CONTENT_TORCH)
1766 client->m_dig_time_remaining = dig_time;
1768 // Reset build time counter
1769 getClient(peer->id)->m_time_from_building.set(0.0);
1776 else if(action == 2)
1779 RemoteClient *client = getClient(peer->id);
1780 JMutexAutoLock digmutex(client->m_dig_mutex);
1781 client->m_dig_tool_item = -1;
1786 3: Digging completed
1788 else if(action == 3)
1790 // Mandatory parameter; actually used for nothing
1791 core::map<v3s16, MapBlock*> modified_blocks;
1797 // Get material at position
1798 material = m_env.getMap().getNode(p_under).d;
1799 // If it's not diggable, do nothing
1800 if(content_diggable(material) == false)
1802 derr_server<<"Server: Not finishing digging: Node not diggable"
1807 catch(InvalidPositionException &e)
1809 derr_server<<"Server: Not finishing digging: Node not found"
1814 //TODO: Send to only other clients
1817 Send the removal to all other clients
1822 SharedBuffer<u8> reply(replysize);
1823 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1824 writeS16(&reply[2], p_under.X);
1825 writeS16(&reply[4], p_under.Y);
1826 writeS16(&reply[6], p_under.Z);
1828 for(core::map<u16, RemoteClient*>::Iterator
1829 i = m_clients.getIterator();
1830 i.atEnd() == false; i++)
1832 // Get client and check that it is valid
1833 RemoteClient *client = i.getNode()->getValue();
1834 assert(client->peer_id == i.getNode()->getKey());
1835 if(client->serialization_version == SER_FMT_VER_INVALID)
1838 // Don't send if it's the same one
1839 if(peer_id == client->peer_id)
1843 m_con.Send(client->peer_id, 0, reply, true);
1847 Update and send inventory
1850 if(g_settings.getBool("creative_mode") == false)
1852 // Add to inventory and send inventory
1853 InventoryItem *item = new MaterialItem(material, 1);
1854 player->inventory.addItem("main", item);
1855 SendInventory(player->peer_id);
1860 (this takes some time so it is done after the quick stuff)
1862 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1868 // Update water pressure around modification
1869 // This also adds it to m_flow_active_nodes if appropriate
1871 MapVoxelManipulator v(&m_env.getMap());
1872 v.m_disable_water_climb =
1873 g_settings.getBool("disable_water_climb");
1875 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1879 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1881 catch(ProcessingLimitException &e)
1883 dstream<<"Processing limit reached (1)"<<std::endl;
1886 v.blitBack(modified_blocks);
1892 else if(action == 1)
1895 InventoryList *ilist = player->inventory.getList("main");
1900 InventoryItem *item = ilist->getItem(item_i);
1902 // If there is no item, it is not possible to add it anywhere
1907 Handle material items
1909 if(std::string("MaterialItem") == item->getName())
1912 // Don't add a node if this is not a free space
1913 MapNode n2 = m_env.getMap().getNode(p_over);
1914 if(content_buildable_to(n2.d) == false)
1917 catch(InvalidPositionException &e)
1919 derr_server<<"Server: Ignoring ADDNODE: Node not found"
1924 // Reset build time counter
1925 getClient(peer->id)->m_time_from_building.set(0.0);
1928 MaterialItem *mitem = (MaterialItem*)item;
1930 n.d = mitem->getMaterial();
1931 if(content_directional(n.d))
1932 n.dir = packDir(p_under - p_over);
1936 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
1937 SharedBuffer<u8> reply(replysize);
1938 writeU16(&reply[0], TOCLIENT_ADDNODE);
1939 writeS16(&reply[2], p_over.X);
1940 writeS16(&reply[4], p_over.Y);
1941 writeS16(&reply[6], p_over.Z);
1942 n.serialize(&reply[8], peer_ser_ver);
1944 m_con.SendToAll(0, reply, true);
1949 InventoryList *ilist = player->inventory.getList("main");
1950 if(g_settings.getBool("creative_mode") == false && ilist)
1952 // Remove from inventory and send inventory
1953 if(mitem->getCount() == 1)
1954 ilist->deleteItem(item_i);
1958 SendInventory(peer_id);
1964 This takes some time so it is done after the quick stuff
1966 core::map<v3s16, MapBlock*> modified_blocks;
1967 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
1973 InventoryList *ilist = player->inventory.getList("main");
1974 if(g_settings.getBool("creative_mode") == false && ilist)
1976 // Remove from inventory and send inventory
1977 if(mitem->getCount() == 1)
1978 ilist->deleteItem(item_i);
1982 SendInventory(peer_id);
1988 This takes some time so it is done after the quick stuff
1990 core::map<v3s16, MapBlock*> modified_blocks;
1991 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
1994 Set the modified blocks unsent for all the clients
1997 //JMutexAutoLock lock2(m_con_mutex);
1999 for(core::map<u16, RemoteClient*>::Iterator
2000 i = m_clients.getIterator();
2001 i.atEnd() == false; i++)
2003 RemoteClient *client = i.getNode()->getValue();
2005 if(modified_blocks.size() > 0)
2007 // Remove block from sent history
2008 client->SetBlocksNotSent(modified_blocks);
2017 // Update water pressure around modification
2018 // This also adds it to m_flow_active_nodes if appropriate
2020 MapVoxelManipulator v(&m_env.getMap());
2021 v.m_disable_water_climb =
2022 g_settings.getBool("disable_water_climb");
2024 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2028 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2030 catch(ProcessingLimitException &e)
2032 dstream<<"Processing limit reached (1)"<<std::endl;
2035 v.blitBack(modified_blocks);
2042 v3s16 blockpos = getNodeBlockPos(p_over);
2044 MapBlock *block = NULL;
2047 block = m_env.getMap().getBlockNoCreate(blockpos);
2049 catch(InvalidPositionException &e)
2051 derr_server<<"Error while placing object: "
2052 "block not found"<<std::endl;
2056 v3s16 block_pos_i_on_map = block->getPosRelative();
2057 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2059 v3f pos = intToFloat(p_over);
2060 pos -= block_pos_f_on_map;
2062 /*dout_server<<"pos="
2063 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2066 MapBlockObject *obj = NULL;
2069 Handle block object items
2071 if(std::string("MBOItem") == item->getName())
2073 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2075 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2076 "inventorystring=\""
2077 <<oitem->getInventoryString()
2078 <<"\""<<std::endl;*/
2080 obj = oitem->createObject
2081 (pos, player->getYaw(), player->getPitch());
2088 dout_server<<"Placing a miscellaneous item on map"
2091 Create an ItemObject that contains the item.
2093 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2094 std::ostringstream os(std::ios_base::binary);
2095 item->serialize(os);
2096 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2097 iobj->setItemString(os.str());
2103 derr_server<<"WARNING: item resulted in NULL object, "
2104 <<"not placing onto map"
2109 block->addObject(obj);
2111 dout_server<<"Placed object"<<std::endl;
2113 InventoryList *ilist = player->inventory.getList("main");
2114 if(g_settings.getBool("creative_mode") == false && ilist)
2116 // Remove from inventory and send inventory
2117 ilist->deleteItem(item_i);
2119 SendInventory(peer_id);
2127 Catch invalid actions
2131 derr_server<<"WARNING: Server: Invalid action "
2132 <<action<<std::endl;
2136 else if(command == TOSERVER_RELEASE)
2145 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2148 else if(command == TOSERVER_SIGNTEXT)
2157 std::string datastring((char*)&data[2], datasize-2);
2158 std::istringstream is(datastring, std::ios_base::binary);
2161 is.read((char*)buf, 6);
2162 v3s16 blockpos = readV3S16(buf);
2163 is.read((char*)buf, 2);
2164 s16 id = readS16(buf);
2165 is.read((char*)buf, 2);
2166 u16 textlen = readU16(buf);
2168 for(u16 i=0; i<textlen; i++)
2170 is.read((char*)buf, 1);
2171 text += (char)buf[0];
2174 MapBlock *block = NULL;
2177 block = m_env.getMap().getBlockNoCreate(blockpos);
2179 catch(InvalidPositionException &e)
2181 derr_server<<"Error while setting sign text: "
2182 "block not found"<<std::endl;
2186 MapBlockObject *obj = block->getObject(id);
2189 derr_server<<"Error while setting sign text: "
2190 "object not found"<<std::endl;
2194 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2196 derr_server<<"Error while setting sign text: "
2197 "object is not a sign"<<std::endl;
2201 ((SignObject*)obj)->setText(text);
2203 obj->getBlock()->setChangedFlag();
2205 else if(command == TOSERVER_INVENTORY_ACTION)
2207 // Ignore inventory changes if in creative mode
2208 if(g_settings.getBool("creative_mode") == true)
2210 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2214 // Strip command and create a stream
2215 std::string datastring((char*)&data[2], datasize-2);
2216 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2217 std::istringstream is(datastring, std::ios_base::binary);
2219 InventoryAction *a = InventoryAction::deSerialize(is);
2223 Handle craftresult specially
2225 bool disable_action = false;
2226 if(a->getType() == IACTION_MOVE)
2228 IMoveAction *ma = (IMoveAction*)a;
2229 // Don't allow moving anything to craftresult
2230 if(ma->to_name == "craftresult")
2233 disable_action = true;
2235 // When something is removed from craftresult
2236 if(ma->from_name == "craftresult")
2238 disable_action = true;
2239 // Remove stuff from craft
2240 InventoryList *clist = player->inventory.getList("craft");
2243 clist->decrementMaterials(ma->count);
2246 // Feed action to player inventory
2247 a->apply(&player->inventory);
2250 // If something appeared in craftresult, throw it
2252 InventoryList *rlist = player->inventory.getList("craftresult");
2253 InventoryList *mlist = player->inventory.getList("main");
2254 if(rlist && mlist && rlist->getUsedSlots() == 1)
2256 InventoryItem *item1 = rlist->changeItem(0, NULL);
2257 mlist->addItem(item1);
2261 if(disable_action == false)
2263 // Feed action to player inventory
2264 a->apply(&player->inventory);
2269 SendInventory(player->peer_id);
2273 dstream<<"TOSERVER_INVENTORY_ACTION: "
2274 <<"InventoryAction::deSerialize() returned NULL"
2278 else if(command == TOSERVER_CHAT_MESSAGE)
2286 std::string datastring((char*)&data[2], datasize-2);
2287 std::istringstream is(datastring, std::ios_base::binary);
2290 is.read((char*)buf, 2);
2291 u16 len = readU16(buf);
2293 std::wstring message;
2294 for(u16 i=0; i<len; i++)
2296 is.read((char*)buf, 2);
2297 message += (wchar_t)readU16(buf);
2300 dstream<<"CHAT: "<<wide_to_narrow(message)<<std::endl;
2303 Send the message to all other clients
2305 for(core::map<u16, RemoteClient*>::Iterator
2306 i = m_clients.getIterator();
2307 i.atEnd() == false; i++)
2309 // Get client and check that it is valid
2310 RemoteClient *client = i.getNode()->getValue();
2311 assert(client->peer_id == i.getNode()->getKey());
2312 if(client->serialization_version == SER_FMT_VER_INVALID)
2315 // Don't send if it's the same one
2316 if(peer_id == client->peer_id)
2319 // Get player name of this client
2320 std::wstring name = L"unknown";
2321 Player *player = m_env.getPlayer(client->peer_id);
2323 name = narrow_to_wide(player->getName());
2325 SendChatMessage(client->peer_id,
2326 std::wstring(L"<")+name+L"> "+message);
2331 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2332 "unknown command "<<command<<std::endl;
2336 catch(SendFailedException &e)
2338 derr_server<<"Server::ProcessData(): SendFailedException: "
2344 /*void Server::Send(u16 peer_id, u16 channelnum,
2345 SharedBuffer<u8> data, bool reliable)
2347 JMutexAutoLock lock(m_con_mutex);
2348 m_con.Send(peer_id, channelnum, data, reliable);
2351 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2353 DSTACK(__FUNCTION_NAME);
2355 Create a packet with the block in the right format
2358 std::ostringstream os(std::ios_base::binary);
2359 block->serialize(os, ver);
2360 std::string s = os.str();
2361 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2363 u32 replysize = 8 + blockdata.getSize();
2364 SharedBuffer<u8> reply(replysize);
2365 v3s16 p = block->getPos();
2366 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2367 writeS16(&reply[2], p.X);
2368 writeS16(&reply[4], p.Y);
2369 writeS16(&reply[6], p.Z);
2370 memcpy(&reply[8], *blockdata, blockdata.getSize());
2372 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2373 <<": \tpacket size: "<<replysize<<std::endl;*/
2378 m_con.Send(peer_id, 1, reply, true);
2381 core::list<PlayerInfo> Server::getPlayerInfo()
2383 DSTACK(__FUNCTION_NAME);
2384 JMutexAutoLock envlock(m_env_mutex);
2385 JMutexAutoLock conlock(m_con_mutex);
2387 core::list<PlayerInfo> list;
2389 core::list<Player*> players = m_env.getPlayers();
2391 core::list<Player*>::Iterator i;
2392 for(i = players.begin();
2393 i != players.end(); i++)
2397 Player *player = *i;
2399 con::Peer *peer = m_con.GetPeer(player->peer_id);
2401 info.address = peer->address;
2402 info.avg_rtt = peer->avg_rtt;
2404 catch(con::PeerNotFoundException &e)
2406 // Outdated peer info
2408 info.address = Address(0,0,0,0,0);
2412 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2413 info.position = player->getPosition();
2415 list.push_back(info);
2421 void Server::peerAdded(con::Peer *peer)
2423 DSTACK(__FUNCTION_NAME);
2424 dout_server<<"Server::peerAdded(): peer->id="
2425 <<peer->id<<std::endl;
2427 // Connection is already locked when this is called.
2428 //JMutexAutoLock lock(m_con_mutex);
2431 core::map<u16, RemoteClient*>::Node *n;
2432 n = m_clients.find(peer->id);
2433 // The client shouldn't already exist
2437 RemoteClient *client = new RemoteClient();
2438 client->peer_id = peer->id;
2439 m_clients.insert(client->peer_id, client);
2443 // Already locked when called
2444 //JMutexAutoLock envlock(m_env_mutex);
2446 Player *player = m_env.getPlayer(peer->id);
2448 // The player shouldn't already exist
2449 assert(player == NULL);
2451 player = new ServerRemotePlayer();
2452 player->peer_id = peer->id;
2458 // We're going to throw the player to this position
2459 //v2s16 nodepos(29990,29990);
2460 //v2s16 nodepos(9990,9990);
2462 v2s16 sectorpos = getNodeSectorPos(nodepos);
2463 // Get zero sector (it could have been unloaded to disk)
2464 m_env.getMap().emergeSector(sectorpos);
2465 // Get ground height at origin
2466 f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
2467 // The sector should have been generated -> groundheight exists
2468 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
2469 // Don't go underwater
2470 if(groundheight < WATER_LEVEL)
2471 groundheight = WATER_LEVEL;
2473 player->setPosition(intToFloat(v3s16(
2480 Add player to environment
2483 m_env.addPlayer(player);
2486 Add stuff to inventory
2489 if(g_settings.getBool("creative_mode"))
2493 InventoryItem *item = new ToolItem("STPick", 32000);
2494 bool r = player->inventory.addItem("main", item);
2497 // Give all materials
2498 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
2499 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
2501 // Skip some materials
2502 if(i == CONTENT_OCEAN)
2505 InventoryItem *item = new MaterialItem(i, 1);
2506 player->inventory.addItem("main", item);
2510 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
2511 bool r = player->inventory.addItem("main", item);
2516 InventoryItem *item = new MapBlockObjectItem("Rat");
2517 bool r = player->inventory.addItem("main", item);
2524 InventoryItem *item = new ToolItem("WPick", 32000);
2525 bool r = player->inventory.addItem("main", item);
2529 InventoryItem *item = new ToolItem("STPick", 32000);
2530 bool r = player->inventory.addItem("main", item);
2533 /*// Give some lights
2535 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
2536 bool r = player->inventory.addItem("main", item);
2540 for(u16 i=0; i<4; i++)
2542 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
2543 bool r = player->inventory.addItem("main", item);
2546 /*// Give some other stuff
2548 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
2549 bool r = player->inventory.addItem("main", item);
2556 void Server::deletingPeer(con::Peer *peer, bool timeout)
2558 DSTACK(__FUNCTION_NAME);
2559 dout_server<<"Server::deletingPeer(): peer->id="
2560 <<peer->id<<", timeout="<<timeout<<std::endl;
2562 // Connection is already locked when this is called.
2563 //JMutexAutoLock lock(m_con_mutex);
2566 core::map<u16, RemoteClient*>::Node *n;
2567 n = m_clients.find(peer->id);
2568 // The client should exist
2573 // Already locked when called
2574 //JMutexAutoLock envlock(m_env_mutex);
2575 m_env.removePlayer(peer->id);
2579 delete m_clients[peer->id];
2580 m_clients.remove(peer->id);
2582 // Send player info to all clients
2586 void Server::SendObjectData(float dtime)
2588 DSTACK(__FUNCTION_NAME);
2590 core::map<v3s16, bool> stepped_blocks;
2592 for(core::map<u16, RemoteClient*>::Iterator
2593 i = m_clients.getIterator();
2594 i.atEnd() == false; i++)
2596 u16 peer_id = i.getNode()->getKey();
2597 RemoteClient *client = i.getNode()->getValue();
2598 assert(client->peer_id == peer_id);
2600 if(client->serialization_version == SER_FMT_VER_INVALID)
2603 client->SendObjectData(this, dtime, stepped_blocks);
2607 void Server::SendPlayerInfos()
2609 DSTACK(__FUNCTION_NAME);
2611 //JMutexAutoLock envlock(m_env_mutex);
2613 core::list<Player*> players = m_env.getPlayers();
2615 u32 player_count = players.getSize();
2616 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2618 SharedBuffer<u8> data(datasize);
2619 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2622 core::list<Player*>::Iterator i;
2623 for(i = players.begin();
2624 i != players.end(); i++)
2626 Player *player = *i;
2628 /*dstream<<"Server sending player info for player with "
2629 "peer_id="<<player->peer_id<<std::endl;*/
2631 writeU16(&data[start], player->peer_id);
2632 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2633 start += 2+PLAYERNAME_SIZE;
2636 //JMutexAutoLock conlock(m_con_mutex);
2639 m_con.SendToAll(0, data, true);
2642 void Server::SendInventory(u16 peer_id)
2644 DSTACK(__FUNCTION_NAME);
2646 Player* player = m_env.getPlayer(peer_id);
2649 Calculate crafting stuff
2652 InventoryList *clist = player->inventory.getList("craft");
2653 InventoryList *rlist = player->inventory.getList("craftresult");
2656 rlist->clearItems();
2660 InventoryItem *items[9];
2661 for(u16 i=0; i<9; i++)
2663 items[i] = clist->getItem(i);
2666 if(clist->getUsedSlots() == 1 && items[0])
2668 if((std::string)items[0]->getName() == "MaterialItem")
2670 MaterialItem *mitem = (MaterialItem*)items[0];
2671 if(mitem->getMaterial() == CONTENT_TREE)
2673 rlist->addItem(new MapBlockObjectItem("Sign"));
2678 if(clist->getUsedSlots() == 2 && items[0] && items[3])
2681 (std::string)items[0]->getName() == "MaterialItem"
2682 && ((MaterialItem*)items[0])->getMaterial() == CONTENT_COALSTONE
2683 && (std::string)items[3]->getName() == "MaterialItem"
2684 && ((MaterialItem*)items[3])->getMaterial() == CONTENT_TREE
2687 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2696 std::ostringstream os;
2697 //os.imbue(std::locale("C"));
2699 player->inventory.serialize(os);
2701 std::string s = os.str();
2703 SharedBuffer<u8> data(s.size()+2);
2704 writeU16(&data[0], TOCLIENT_INVENTORY);
2705 memcpy(&data[2], s.c_str(), s.size());
2708 m_con.Send(peer_id, 0, data, true);
2711 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2713 DSTACK(__FUNCTION_NAME);
2715 std::ostringstream os(std::ios_base::binary);
2719 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2720 os.write((char*)buf, 2);
2723 writeU16(buf, message.size());
2724 os.write((char*)buf, 2);
2727 for(u32 i=0; i<message.size(); i++)
2731 os.write((char*)buf, 2);
2735 std::string s = os.str();
2736 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2738 m_con.Send(peer_id, 0, data, true);
2741 void Server::BroadcastChatMessage(const std::wstring &message)
2743 for(core::map<u16, RemoteClient*>::Iterator
2744 i = m_clients.getIterator();
2745 i.atEnd() == false; i++)
2747 // Get client and check that it is valid
2748 RemoteClient *client = i.getNode()->getValue();
2749 assert(client->peer_id == i.getNode()->getKey());
2750 if(client->serialization_version == SER_FMT_VER_INVALID)
2753 SendChatMessage(client->peer_id, message);
2757 void Server::SendBlocks(float dtime)
2759 DSTACK(__FUNCTION_NAME);
2761 JMutexAutoLock envlock(m_env_mutex);
2763 core::array<PrioritySortedBlockTransfer> queue;
2765 s32 total_sending = 0;
2767 for(core::map<u16, RemoteClient*>::Iterator
2768 i = m_clients.getIterator();
2769 i.atEnd() == false; i++)
2771 RemoteClient *client = i.getNode()->getValue();
2772 assert(client->peer_id == i.getNode()->getKey());
2774 total_sending += client->SendingCount();
2776 if(client->serialization_version == SER_FMT_VER_INVALID)
2779 client->GetNextBlocks(this, dtime, queue);
2783 // Lowest priority number comes first.
2784 // Lowest is most important.
2787 JMutexAutoLock conlock(m_con_mutex);
2789 for(u32 i=0; i<queue.size(); i++)
2791 //TODO: Calculate limit dynamically
2792 if(total_sending >= g_settings.getS32
2793 ("max_simultaneous_block_sends_server_total"))
2796 PrioritySortedBlockTransfer q = queue[i];
2798 MapBlock *block = NULL;
2801 block = m_env.getMap().getBlockNoCreate(q.pos);
2803 catch(InvalidPositionException &e)
2808 RemoteClient *client = getClient(q.peer_id);
2810 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2812 client->SentBlock(q.pos);
2819 RemoteClient* Server::getClient(u16 peer_id)
2821 DSTACK(__FUNCTION_NAME);
2822 //JMutexAutoLock lock(m_con_mutex);
2823 core::map<u16, RemoteClient*>::Node *n;
2824 n = m_clients.find(peer_id);
2825 // A client should exist for all peers
2827 return n->getValue();
2830 void Server::UpdateBlockWaterPressure(MapBlock *block,
2831 core::map<v3s16, MapBlock*> &modified_blocks)
2833 MapVoxelManipulator v(&m_env.getMap());
2834 v.m_disable_water_climb =
2835 g_settings.getBool("disable_water_climb");
2837 VoxelArea area(block->getPosRelative(),
2838 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
2842 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2844 catch(ProcessingLimitException &e)
2846 dstream<<"Processing limit reached (1)"<<std::endl;
2849 v.blitBack(modified_blocks);