3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
27 #include "clientserver.h"
29 #include "jmutexautolock.h"
31 #include "constants.h"
33 #include "materials.h"
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
37 void * ServerThread::Thread()
41 DSTACK(__FUNCTION_NAME);
43 BEGIN_DEBUG_EXCEPTION_HANDLER
48 m_server->AsyncRunStep();
50 //dout_server<<"Running m_server->Receive()"<<std::endl;
53 catch(con::NoIncomingDataException &e)
58 END_DEBUG_EXCEPTION_HANDLER
63 void * EmergeThread::Thread()
67 DSTACK(__FUNCTION_NAME);
71 BEGIN_DEBUG_EXCEPTION_HANDLER
74 Get block info from queue, emerge them and send them
77 After queue is empty, exit.
81 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
85 SharedPtr<QueuedBlockEmerge> q(qptr);
89 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
91 //TimeTaker timer("block emerge");
94 Try to emerge it from somewhere.
96 If it is only wanted as optional, only loading from disk
101 Check if any peer wants it as non-optional. In that case it
104 Also decrement the emerge queue count in clients.
107 bool optional = true;
110 core::map<u16, u8>::Iterator i;
111 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
113 //u16 peer_id = i.getNode()->getKey();
116 u8 flags = i.getNode()->getValue();
117 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
123 /*dstream<<"EmergeThread: p="
124 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
125 <<"optional="<<optional<<std::endl;*/
127 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
129 core::map<v3s16, MapBlock*> changed_blocks;
130 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
132 MapBlock *block = NULL;
133 bool got_block = true;
134 core::map<v3s16, MapBlock*> modified_blocks;
138 //TimeTaker envlockwaittimer("block emerge envlock wait time");
141 JMutexAutoLock envlock(m_server->m_env_mutex);
143 //envlockwaittimer.stop();
145 //TimeTaker timer("block emerge (while env locked)");
148 bool only_from_disk = false;
151 only_from_disk = true;
153 block = map.emergeBlock(
157 lighting_invalidated_blocks);
159 // If it is a dummy, block was not found on disk
162 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
166 catch(InvalidPositionException &e)
169 // This happens when position is over limit.
175 if(debug && changed_blocks.size() > 0)
177 dout_server<<DTIME<<"Got changed_blocks: ";
178 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
179 i.atEnd() == false; i++)
181 MapBlock *block = i.getNode()->getValue();
182 v3s16 p = block->getPos();
183 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
185 dout_server<<std::endl;
189 Update water pressure
192 m_server->UpdateBlockWaterPressure(block, modified_blocks);
194 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
195 i.atEnd() == false; i++)
197 MapBlock *block = i.getNode()->getValue();
198 m_server->UpdateBlockWaterPressure(block, modified_blocks);
199 //v3s16 p = i.getNode()->getKey();
200 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
204 Collect a list of blocks that have been modified in
205 addition to the fetched one.
208 // Add all the "changed blocks" to modified_blocks
209 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
210 i.atEnd() == false; i++)
212 MapBlock *block = i.getNode()->getValue();
213 modified_blocks.insert(block->getPos(), block);
216 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
217 <<" blocks"<<std::endl;*/
219 //TimeTaker timer("** updateLighting", g_device);
221 // Update lighting without locking the environment mutex,
222 // add modified blocks to changed blocks
223 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
225 // If we got no block, there should be no invalidated blocks
228 assert(lighting_invalidated_blocks.size() == 0);
234 Set sent status of modified blocks on clients
237 // NOTE: Server's clients are also behind the connection mutex
238 JMutexAutoLock lock(m_server->m_con_mutex);
241 Add the originally fetched block to the modified list
245 modified_blocks.insert(p, block);
249 Set the modified blocks unsent for all the clients
252 for(core::map<u16, RemoteClient*>::Iterator
253 i = m_server->m_clients.getIterator();
254 i.atEnd() == false; i++)
256 RemoteClient *client = i.getNode()->getValue();
258 if(modified_blocks.size() > 0)
260 // Remove block from sent history
261 client->SetBlocksNotSent(modified_blocks);
267 END_DEBUG_EXCEPTION_HANDLER
272 void RemoteClient::GetNextBlocks(Server *server, float dtime,
273 core::array<PrioritySortedBlockTransfer> &dest)
275 DSTACK(__FUNCTION_NAME);
279 JMutexAutoLock lock(m_blocks_sent_mutex);
280 m_nearest_unsent_reset_timer += dtime;
283 // Won't send anything if already sending
285 JMutexAutoLock lock(m_blocks_sending_mutex);
287 if(m_blocks_sending.size() >= g_settings.getU16
288 ("max_simultaneous_block_sends_per_client"))
290 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
295 Player *player = server->m_env.getPlayer(peer_id);
297 assert(player != NULL);
299 v3f playerpos = player->getPosition();
300 v3f playerspeed = player->getSpeed();
302 v3s16 center_nodepos = floatToInt(playerpos);
304 v3s16 center = getNodeBlockPos(center_nodepos);
307 Get the starting value of the block finder radius.
309 s16 last_nearest_unsent_d;
312 JMutexAutoLock lock(m_blocks_sent_mutex);
314 if(m_last_center != center)
316 m_nearest_unsent_d = 0;
317 m_last_center = center;
320 /*dstream<<"m_nearest_unsent_reset_timer="
321 <<m_nearest_unsent_reset_timer<<std::endl;*/
322 if(m_nearest_unsent_reset_timer > 5.0)
324 m_nearest_unsent_reset_timer = 0;
325 m_nearest_unsent_d = 0;
326 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
329 last_nearest_unsent_d = m_nearest_unsent_d;
331 d_start = m_nearest_unsent_d;
334 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
335 ("max_simultaneous_block_sends_per_client");
336 u16 maximum_simultaneous_block_sends =
337 maximum_simultaneous_block_sends_setting;
340 Check the time from last addNode/removeNode.
342 Decrease send rate if player is building stuff.
345 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
346 m_time_from_building.m_value += dtime;
347 /*if(m_time_from_building.m_value
348 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
349 if(m_time_from_building.m_value < g_settings.getFloat(
350 "full_block_send_enable_min_time_from_building"))
352 maximum_simultaneous_block_sends
353 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
357 u32 num_blocks_selected;
359 JMutexAutoLock lock(m_blocks_sending_mutex);
360 num_blocks_selected = m_blocks_sending.size();
364 next time d will be continued from the d from which the nearest
365 unsent block was found this time.
367 This is because not necessarily any of the blocks found this
368 time are actually sent.
370 s32 new_nearest_unsent_d = -1;
372 // Serialization version used
373 //u8 ser_version = serialization_version;
375 //bool has_incomplete_blocks = false;
377 s16 d_max = g_settings.getS16("max_block_send_distance");
378 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
380 //dstream<<"Starting from "<<d_start<<std::endl;
382 for(s16 d = d_start; d <= d_max; d++)
384 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
386 //if(has_incomplete_blocks == false)
388 JMutexAutoLock lock(m_blocks_sent_mutex);
390 If m_nearest_unsent_d was changed by the EmergeThread
391 (it can change it to 0 through SetBlockNotSent),
393 Else update m_nearest_unsent_d
395 if(m_nearest_unsent_d != last_nearest_unsent_d)
397 d = m_nearest_unsent_d;
398 last_nearest_unsent_d = m_nearest_unsent_d;
403 Get the border/face dot coordinates of a "d-radiused"
406 core::list<v3s16> list;
407 getFacePositions(list, d);
409 core::list<v3s16>::Iterator li;
410 for(li=list.begin(); li!=list.end(); li++)
412 v3s16 p = *li + center;
416 - Don't allow too many simultaneous transfers
417 - EXCEPT when the blocks are very close
419 Also, don't send blocks that are already flying.
422 u16 maximum_simultaneous_block_sends_now =
423 maximum_simultaneous_block_sends;
425 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
427 maximum_simultaneous_block_sends_now =
428 maximum_simultaneous_block_sends_setting;
432 JMutexAutoLock lock(m_blocks_sending_mutex);
434 // Limit is dynamically lowered when building
435 if(num_blocks_selected
436 >= maximum_simultaneous_block_sends_now)
438 /*dstream<<"Not sending more blocks. Queue full. "
439 <<m_blocks_sending.size()
444 if(m_blocks_sending.find(p) != NULL)
451 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
452 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
453 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
454 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
455 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
456 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
459 bool generate = d <= d_max_gen;
461 // Limit the generating area vertically to 2/3
462 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
466 Don't send already sent blocks
469 JMutexAutoLock lock(m_blocks_sent_mutex);
471 if(m_blocks_sent.find(p) != NULL)
476 Check if map has this block
478 MapBlock *block = NULL;
481 block = server->m_env.getMap().getBlockNoCreate(p);
483 catch(InvalidPositionException &e)
487 bool surely_not_found_on_disk = false;
490 /*if(block->isIncomplete())
492 has_incomplete_blocks = true;
498 surely_not_found_on_disk = true;
503 If block has been marked to not exist on disk (dummy)
504 and generating new ones is not wanted, skip block.
506 if(generate == false && surely_not_found_on_disk == true)
513 Record the lowest d from which a a block has been
514 found being not sent and possibly to exist
516 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
518 new_nearest_unsent_d = d;
522 Add inexistent block to emerge queue.
524 if(block == NULL || surely_not_found_on_disk)
526 /*SharedPtr<JMutexAutoLock> lock
527 (m_num_blocks_in_emerge_queue.getLock());*/
529 //TODO: Get value from somewhere
530 // Allow only one block in emerge queue
531 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
533 // Add it to the emerge queue and trigger the thread
536 if(generate == false)
537 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
539 server->m_emerge_queue.addBlock(peer_id, p, flags);
540 server->m_emergethread.trigger();
551 PrioritySortedBlockTransfer q((float)d, p, peer_id);
555 num_blocks_selected += 1;
560 if(new_nearest_unsent_d != -1)
562 JMutexAutoLock lock(m_blocks_sent_mutex);
563 m_nearest_unsent_d = new_nearest_unsent_d;
567 void RemoteClient::SendObjectData(
570 core::map<v3s16, bool> &stepped_blocks
573 DSTACK(__FUNCTION_NAME);
575 // Can't send anything without knowing version
576 if(serialization_version == SER_FMT_VER_INVALID)
578 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
584 Send a TOCLIENT_OBJECTDATA packet.
588 u16 number of player positions
599 std::ostringstream os(std::ios_base::binary);
603 writeU16(buf, TOCLIENT_OBJECTDATA);
604 os.write((char*)buf, 2);
607 Get and write player data
610 // Get connected players
611 core::list<Player*> players = server->m_env.getPlayers(true);
613 // Write player count
614 u16 playercount = players.size();
615 writeU16(buf, playercount);
616 os.write((char*)buf, 2);
618 core::list<Player*>::Iterator i;
619 for(i = players.begin();
620 i != players.end(); i++)
624 v3f pf = player->getPosition();
625 v3f sf = player->getSpeed();
627 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
628 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
629 s32 pitch_i (player->getPitch() * 100);
630 s32 yaw_i (player->getYaw() * 100);
632 writeU16(buf, player->peer_id);
633 os.write((char*)buf, 2);
634 writeV3S32(buf, position_i);
635 os.write((char*)buf, 12);
636 writeV3S32(buf, speed_i);
637 os.write((char*)buf, 12);
638 writeS32(buf, pitch_i);
639 os.write((char*)buf, 4);
640 writeS32(buf, yaw_i);
641 os.write((char*)buf, 4);
645 Get and write object data
651 For making players to be able to build to their nearby
652 environment (building is not possible on blocks that are not
655 - Add blocks to emerge queue if they are not found
657 SUGGESTION: These could be ignored from the backside of the player
660 Player *player = server->m_env.getPlayer(peer_id);
664 v3f playerpos = player->getPosition();
665 v3f playerspeed = player->getSpeed();
667 v3s16 center_nodepos = floatToInt(playerpos);
668 v3s16 center = getNodeBlockPos(center_nodepos);
670 s16 d_max = g_settings.getS16("active_object_range");
672 // Number of blocks whose objects were written to bos
675 std::ostringstream bos(std::ios_base::binary);
677 for(s16 d = 0; d <= d_max; d++)
679 core::list<v3s16> list;
680 getFacePositions(list, d);
682 core::list<v3s16>::Iterator li;
683 for(li=list.begin(); li!=list.end(); li++)
685 v3s16 p = *li + center;
688 Ignore blocks that haven't been sent to the client
691 JMutexAutoLock sentlock(m_blocks_sent_mutex);
692 if(m_blocks_sent.find(p) == NULL)
696 // Try stepping block and add it to a send queue
701 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
704 Step block if not in stepped_blocks and add to stepped_blocks.
706 if(stepped_blocks.find(p) == NULL)
708 block->stepObjects(dtime, true, server->getDayNightRatio());
709 stepped_blocks.insert(p, true);
710 block->setChangedFlag();
713 // Skip block if there are no objects
714 if(block->getObjectCount() == 0)
723 bos.write((char*)buf, 6);
726 block->serializeObjects(bos, serialization_version);
731 Stop collecting objects if data is already too big
733 // Sum of player and object data sizes
734 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
735 // break out if data too big
736 if(sum > MAX_OBJECTDATA_SIZE)
738 goto skip_subsequent;
742 catch(InvalidPositionException &e)
745 // Add it to the emerge queue and trigger the thread.
746 // Fetch the block only if it is on disk.
748 // Grab and increment counter
749 /*SharedPtr<JMutexAutoLock> lock
750 (m_num_blocks_in_emerge_queue.getLock());
751 m_num_blocks_in_emerge_queue.m_value++;*/
753 // Add to queue as an anonymous fetch from disk
754 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
755 server->m_emerge_queue.addBlock(0, p, flags);
756 server->m_emergethread.trigger();
764 writeU16(buf, blockcount);
765 os.write((char*)buf, 2);
767 // Write block objects
774 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
777 std::string s = os.str();
778 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
779 // Send as unreliable
780 server->m_con.Send(peer_id, 0, data, false);
783 void RemoteClient::GotBlock(v3s16 p)
785 JMutexAutoLock lock(m_blocks_sending_mutex);
786 JMutexAutoLock lock2(m_blocks_sent_mutex);
787 if(m_blocks_sending.find(p) != NULL)
788 m_blocks_sending.remove(p);
790 dstream<<"RemoteClient::GotBlock(): Didn't find in"
791 " m_blocks_sending"<<std::endl;
792 m_blocks_sent.insert(p, true);
795 void RemoteClient::SentBlock(v3s16 p)
797 JMutexAutoLock lock(m_blocks_sending_mutex);
798 if(m_blocks_sending.size() > 15)
800 dstream<<"RemoteClient::SentBlock(): "
801 <<"m_blocks_sending.size()="
802 <<m_blocks_sending.size()<<std::endl;
804 if(m_blocks_sending.find(p) == NULL)
805 m_blocks_sending.insert(p, 0.0);
807 dstream<<"RemoteClient::SentBlock(): Sent block"
808 " already in m_blocks_sending"<<std::endl;
811 void RemoteClient::SetBlockNotSent(v3s16 p)
813 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
814 JMutexAutoLock sentlock(m_blocks_sent_mutex);
816 m_nearest_unsent_d = 0;
818 if(m_blocks_sending.find(p) != NULL)
819 m_blocks_sending.remove(p);
820 if(m_blocks_sent.find(p) != NULL)
821 m_blocks_sent.remove(p);
824 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
826 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
827 JMutexAutoLock sentlock(m_blocks_sent_mutex);
829 m_nearest_unsent_d = 0;
831 for(core::map<v3s16, MapBlock*>::Iterator
832 i = blocks.getIterator();
833 i.atEnd()==false; i++)
835 v3s16 p = i.getNode()->getKey();
837 if(m_blocks_sending.find(p) != NULL)
838 m_blocks_sending.remove(p);
839 if(m_blocks_sent.find(p) != NULL)
840 m_blocks_sent.remove(p);
848 PlayerInfo::PlayerInfo()
853 void PlayerInfo::PrintLine(std::ostream *s)
856 (*s)<<"\""<<name<<"\" ("
857 <<position.X<<","<<position.Y
858 <<","<<position.Z<<") ";
860 (*s)<<" avg_rtt="<<avg_rtt;
864 u32 PIChecksum(core::list<PlayerInfo> &l)
866 core::list<PlayerInfo>::Iterator i;
869 for(i=l.begin(); i!=l.end(); i++)
871 checksum += a * (i->id+1);
872 checksum ^= 0x435aafcd;
883 std::string mapsavedir,
887 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
888 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
890 m_emergethread(this),
893 m_time_of_day_send_timer(0),
896 m_flowwater_timer = 0.0;
897 m_print_info_timer = 0.0;
898 m_objectdata_timer = 0.0;
899 m_emergethread_trigger_timer = 0.0;
900 m_savemap_timer = 0.0;
904 m_step_dtime_mutex.Init();
913 JMutexAutoLock clientslock(m_con_mutex);
915 for(core::map<u16, RemoteClient*>::Iterator
916 i = m_clients.getIterator();
917 i.atEnd() == false; i++)
920 // NOTE: These are removed by env destructor
922 u16 peer_id = i.getNode()->getKey();
923 JMutexAutoLock envlock(m_env_mutex);
924 m_env.removePlayer(peer_id);
928 delete i.getNode()->getValue();
932 void Server::start(unsigned short port)
934 DSTACK(__FUNCTION_NAME);
935 // Stop thread if already running
938 // Initialize connection
939 m_con.setTimeoutMs(30);
943 m_thread.setRun(true);
946 dout_server<<"Server started on port "<<port<<std::endl;
951 DSTACK(__FUNCTION_NAME);
952 // Stop threads (set run=false first so both start stopping)
953 m_thread.setRun(false);
954 m_emergethread.setRun(false);
956 m_emergethread.stop();
958 dout_server<<"Server threads stopped"<<std::endl;
961 void Server::step(float dtime)
963 DSTACK(__FUNCTION_NAME);
968 JMutexAutoLock lock(m_step_dtime_mutex);
969 m_step_dtime += dtime;
973 void Server::AsyncRunStep()
975 DSTACK(__FUNCTION_NAME);
979 JMutexAutoLock lock1(m_step_dtime_mutex);
980 dtime = m_step_dtime;
983 // Send blocks to clients
989 //dstream<<"Server steps "<<dtime<<std::endl;
990 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
993 JMutexAutoLock lock1(m_step_dtime_mutex);
994 m_step_dtime -= dtime;
1001 m_uptime.set(m_uptime.get() + dtime);
1005 Update m_time_of_day
1008 m_time_counter += dtime;
1009 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1010 u32 units = (u32)(m_time_counter*speed);
1011 m_time_counter -= (f32)units / speed;
1012 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1014 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1017 Send to clients at constant intervals
1020 m_time_of_day_send_timer -= dtime;
1021 if(m_time_of_day_send_timer < 0.0)
1023 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1025 //JMutexAutoLock envlock(m_env_mutex);
1026 JMutexAutoLock conlock(m_con_mutex);
1028 for(core::map<u16, RemoteClient*>::Iterator
1029 i = m_clients.getIterator();
1030 i.atEnd() == false; i++)
1032 RemoteClient *client = i.getNode()->getValue();
1033 //Player *player = m_env.getPlayer(client->peer_id);
1035 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1036 m_time_of_day.get());
1038 m_con.Send(client->peer_id, 0, data, true);
1044 // Process connection's timeouts
1045 JMutexAutoLock lock2(m_con_mutex);
1046 m_con.RunTimeouts(dtime);
1050 // This has to be called so that the client list gets synced
1051 // with the peer list of the connection
1052 handlePeerChanges();
1057 // This also runs Map's timers
1058 JMutexAutoLock lock(m_env_mutex);
1072 if(g_settings.getBool("endless_water") == false)
1077 float &counter = m_flowwater_timer;
1079 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1084 core::map<v3s16, MapBlock*> modified_blocks;
1088 JMutexAutoLock envlock(m_env_mutex);
1090 MapVoxelManipulator v(&m_env.getMap());
1091 v.m_disable_water_climb =
1092 g_settings.getBool("disable_water_climb");
1094 if(g_settings.getBool("endless_water") == false)
1095 v.flowWater(m_flow_active_nodes, 0, false, 250);
1097 v.flowWater(m_flow_active_nodes, 0, false, 50);
1099 v.blitBack(modified_blocks);
1101 ServerMap &map = ((ServerMap&)m_env.getMap());
1104 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1105 map.updateLighting(modified_blocks, lighting_modified_blocks);
1107 // Add blocks modified by lighting to modified_blocks
1108 for(core::map<v3s16, MapBlock*>::Iterator
1109 i = lighting_modified_blocks.getIterator();
1110 i.atEnd() == false; i++)
1112 MapBlock *block = i.getNode()->getValue();
1113 modified_blocks.insert(block->getPos(), block);
1118 Set the modified blocks unsent for all the clients
1121 JMutexAutoLock lock2(m_con_mutex);
1123 for(core::map<u16, RemoteClient*>::Iterator
1124 i = m_clients.getIterator();
1125 i.atEnd() == false; i++)
1127 RemoteClient *client = i.getNode()->getValue();
1129 if(modified_blocks.size() > 0)
1131 // Remove block from sent history
1132 client->SetBlocksNotSent(modified_blocks);
1136 } // interval counter
1139 // Periodically print some info
1141 float &counter = m_print_info_timer;
1147 JMutexAutoLock lock2(m_con_mutex);
1149 for(core::map<u16, RemoteClient*>::Iterator
1150 i = m_clients.getIterator();
1151 i.atEnd() == false; i++)
1153 //u16 peer_id = i.getNode()->getKey();
1154 RemoteClient *client = i.getNode()->getValue();
1155 client->PrintInfo(std::cout);
1163 NOTE: Some of this could be moved to RemoteClient
1167 JMutexAutoLock envlock(m_env_mutex);
1168 JMutexAutoLock conlock(m_con_mutex);
1170 for(core::map<u16, RemoteClient*>::Iterator
1171 i = m_clients.getIterator();
1172 i.atEnd() == false; i++)
1174 RemoteClient *client = i.getNode()->getValue();
1175 Player *player = m_env.getPlayer(client->peer_id);
1177 JMutexAutoLock digmutex(client->m_dig_mutex);
1179 if(client->m_dig_tool_item == -1)
1182 client->m_dig_time_remaining -= dtime;
1184 if(client->m_dig_time_remaining > 0)
1186 client->m_time_from_building.set(0.0);
1190 v3s16 p_under = client->m_dig_position;
1192 // Mandatory parameter; actually used for nothing
1193 core::map<v3s16, MapBlock*> modified_blocks;
1199 // Get material at position
1200 material = m_env.getMap().getNode(p_under).d;
1201 // If it's not diggable, do nothing
1202 if(content_diggable(material) == false)
1204 derr_server<<"Server: Not finishing digging: Node not diggable"
1206 client->m_dig_tool_item = -1;
1210 catch(InvalidPositionException &e)
1212 derr_server<<"Server: Not finishing digging: Node not found"
1214 client->m_dig_tool_item = -1;
1220 SharedBuffer<u8> reply(replysize);
1221 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1222 writeS16(&reply[2], p_under.X);
1223 writeS16(&reply[4], p_under.Y);
1224 writeS16(&reply[6], p_under.Z);
1226 m_con.SendToAll(0, reply, true);
1228 if(g_settings.getBool("creative_mode") == false)
1230 // Add to inventory and send inventory
1231 InventoryItem *item = new MaterialItem(material, 1);
1232 player->inventory.addItem("main", item);
1233 SendInventory(player->peer_id);
1238 (this takes some time so it is done after the quick stuff)
1240 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1246 // Update water pressure around modification
1247 // This also adds it to m_flow_active_nodes if appropriate
1249 MapVoxelManipulator v(&m_env.getMap());
1250 v.m_disable_water_climb =
1251 g_settings.getBool("disable_water_climb");
1253 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1257 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1259 catch(ProcessingLimitException &e)
1261 dstream<<"Processing limit reached (1)"<<std::endl;
1264 v.blitBack(modified_blocks);
1269 // Send object positions
1271 float &counter = m_objectdata_timer;
1273 if(counter >= g_settings.getFloat("objectdata_interval"))
1275 JMutexAutoLock lock1(m_env_mutex);
1276 JMutexAutoLock lock2(m_con_mutex);
1277 SendObjectData(counter);
1283 // Trigger emergethread (it gets somehow gets to a
1284 // non-triggered but bysy state sometimes)
1286 float &counter = m_emergethread_trigger_timer;
1292 m_emergethread.trigger();
1298 float &counter = m_savemap_timer;
1300 if(counter >= g_settings.getFloat("server_map_save_interval"))
1304 JMutexAutoLock lock(m_env_mutex);
1306 // Save only changed parts
1307 m_env.getMap().save(true);
1309 // Delete unused sectors
1310 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1311 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1312 if(deleted_count > 0)
1314 dout_server<<"Server: Unloaded "<<deleted_count
1315 <<" sectors from memory"<<std::endl;
1321 void Server::Receive()
1323 DSTACK(__FUNCTION_NAME);
1324 u32 data_maxsize = 10000;
1325 Buffer<u8> data(data_maxsize);
1330 JMutexAutoLock conlock(m_con_mutex);
1331 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1334 // This has to be called so that the client list gets synced
1335 // with the peer list of the connection
1336 handlePeerChanges();
1338 ProcessData(*data, datasize, peer_id);
1340 catch(con::InvalidIncomingDataException &e)
1342 derr_server<<"Server::Receive(): "
1343 "InvalidIncomingDataException: what()="
1344 <<e.what()<<std::endl;
1346 catch(con::PeerNotFoundException &e)
1348 //NOTE: This is not needed anymore
1350 // The peer has been disconnected.
1351 // Find the associated player and remove it.
1353 /*JMutexAutoLock envlock(m_env_mutex);
1355 dout_server<<"ServerThread: peer_id="<<peer_id
1356 <<" has apparently closed connection. "
1357 <<"Removing player."<<std::endl;
1359 m_env.removePlayer(peer_id);*/
1363 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1365 DSTACK(__FUNCTION_NAME);
1366 // Environment is locked first.
1367 JMutexAutoLock envlock(m_env_mutex);
1368 JMutexAutoLock conlock(m_con_mutex);
1372 peer = m_con.GetPeer(peer_id);
1374 catch(con::PeerNotFoundException &e)
1376 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1377 <<peer_id<<" not found"<<std::endl;
1381 //u8 peer_ser_ver = peer->serialization_version;
1382 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1390 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1392 if(command == TOSERVER_INIT)
1394 // [0] u16 TOSERVER_INIT
1395 // [2] u8 SER_FMT_VER_HIGHEST
1396 // [3] u8[20] player_name
1401 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1402 <<peer->id<<std::endl;
1404 // First byte after command is maximum supported
1405 // serialization version
1406 u8 client_max = data[2];
1407 u8 our_max = SER_FMT_VER_HIGHEST;
1408 // Use the highest version supported by both
1409 u8 deployed = core::min_(client_max, our_max);
1410 // If it's lower than the lowest supported, give up.
1411 if(deployed < SER_FMT_VER_LOWEST)
1412 deployed = SER_FMT_VER_INVALID;
1414 //peer->serialization_version = deployed;
1415 getClient(peer->id)->pending_serialization_version = deployed;
1417 if(deployed == SER_FMT_VER_INVALID)
1419 derr_server<<DTIME<<"Server: Cannot negotiate "
1420 "serialization version with peer "
1421 <<peer_id<<std::endl;
1430 const u32 playername_size = 20;
1431 char playername[playername_size];
1432 for(u32 i=0; i<playername_size-1; i++)
1434 playername[i] = data[3+i];
1436 playername[playername_size-1] = 0;
1439 Player *player = emergePlayer(playername, "");
1440 //Player *player = m_env.getPlayer(peer_id);
1442 // If a client is already connected to the player, cancel
1443 if(player->peer_id != 0)
1445 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1446 <<" tried to connect to "
1447 "an already connected player (peer_id="
1448 <<player->peer_id<<")"<<std::endl;
1452 // Set client of player
1453 player->peer_id = peer_id;
1455 // Check if player doesn't exist
1457 throw con::InvalidIncomingDataException
1458 ("Server::ProcessData(): INIT: Player doesn't exist");
1460 /*// update name if it was supplied
1461 if(datasize >= 20+3)
1464 player->updateName((const char*)&data[3]);
1467 // Now answer with a TOCLIENT_INIT
1469 SharedBuffer<u8> reply(2+1+6);
1470 writeU16(&reply[0], TOCLIENT_INIT);
1471 writeU8(&reply[2], deployed);
1472 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1474 m_con.Send(peer_id, 0, reply, true);
1478 if(command == TOSERVER_INIT2)
1480 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1481 <<peer->id<<std::endl;
1484 getClient(peer->id)->serialization_version
1485 = getClient(peer->id)->pending_serialization_version;
1488 Send some initialization data
1491 // Send player info to all players
1494 // Send inventory to player
1495 SendInventory(peer->id);
1499 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1500 m_time_of_day.get());
1501 m_con.Send(peer->id, 0, data, true);
1504 // Send information about server to player in chat
1506 std::wostringstream os(std::ios_base::binary);
1509 os<<L"uptime="<<m_uptime.get();
1510 // Information about clients
1512 for(core::map<u16, RemoteClient*>::Iterator
1513 i = m_clients.getIterator();
1514 i.atEnd() == false; i++)
1516 // Get client and check that it is valid
1517 RemoteClient *client = i.getNode()->getValue();
1518 assert(client->peer_id == i.getNode()->getKey());
1519 if(client->serialization_version == SER_FMT_VER_INVALID)
1522 Player *player = m_env.getPlayer(client->peer_id);
1523 // Get name of player
1524 std::wstring name = L"unknown";
1526 name = narrow_to_wide(player->getName());
1527 // Add name to information string
1532 SendChatMessage(peer_id, os.str());
1535 // Send information about joining in chat
1537 std::wstring name = L"unknown";
1538 Player *player = m_env.getPlayer(peer_id);
1540 name = narrow_to_wide(player->getName());
1542 std::wstring message;
1545 message += L" joined game";
1546 BroadcastChatMessage(message);
1552 if(peer_ser_ver == SER_FMT_VER_INVALID)
1554 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1555 " serialization format invalid or not initialized."
1556 " Skipping incoming command="<<command<<std::endl;
1560 Player *player = m_env.getPlayer(peer_id);
1563 derr_server<<"Server::ProcessData(): Cancelling: "
1564 "No player for peer_id="<<peer_id
1568 if(command == TOSERVER_PLAYERPOS)
1570 if(datasize < 2+12+12+4+4)
1574 v3s32 ps = readV3S32(&data[start+2]);
1575 v3s32 ss = readV3S32(&data[start+2+12]);
1576 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1577 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1578 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1579 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1580 pitch = wrapDegrees(pitch);
1581 yaw = wrapDegrees(yaw);
1582 player->setPosition(position);
1583 player->setSpeed(speed);
1584 player->setPitch(pitch);
1585 player->setYaw(yaw);
1587 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1588 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1589 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1591 else if(command == TOSERVER_GOTBLOCKS)
1604 u16 count = data[2];
1605 for(u16 i=0; i<count; i++)
1607 if((s16)datasize < 2+1+(i+1)*6)
1608 throw con::InvalidIncomingDataException
1609 ("GOTBLOCKS length is too short");
1610 v3s16 p = readV3S16(&data[2+1+i*6]);
1611 /*dstream<<"Server: GOTBLOCKS ("
1612 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1613 RemoteClient *client = getClient(peer_id);
1614 client->GotBlock(p);
1617 else if(command == TOSERVER_DELETEDBLOCKS)
1630 u16 count = data[2];
1631 for(u16 i=0; i<count; i++)
1633 if((s16)datasize < 2+1+(i+1)*6)
1634 throw con::InvalidIncomingDataException
1635 ("DELETEDBLOCKS length is too short");
1636 v3s16 p = readV3S16(&data[2+1+i*6]);
1637 /*dstream<<"Server: DELETEDBLOCKS ("
1638 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1639 RemoteClient *client = getClient(peer_id);
1640 client->SetBlockNotSent(p);
1643 else if(command == TOSERVER_CLICK_OBJECT)
1650 [2] u8 button (0=left, 1=right)
1655 u8 button = readU8(&data[2]);
1657 p.X = readS16(&data[3]);
1658 p.Y = readS16(&data[5]);
1659 p.Z = readS16(&data[7]);
1660 s16 id = readS16(&data[9]);
1661 //u16 item_i = readU16(&data[11]);
1663 MapBlock *block = NULL;
1666 block = m_env.getMap().getBlockNoCreate(p);
1668 catch(InvalidPositionException &e)
1670 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1674 MapBlockObject *obj = block->getObject(id);
1678 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1682 //TODO: Check that object is reasonably close
1687 InventoryList *ilist = player->inventory.getList("main");
1688 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1691 // Skip if inventory has no free space
1692 if(ilist->getUsedSlots() == ilist->getSize())
1694 dout_server<<"Player inventory has no free space"<<std::endl;
1699 Create the inventory item
1701 InventoryItem *item = NULL;
1702 // If it is an item-object, take the item from it
1703 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1705 item = ((ItemObject*)obj)->createInventoryItem();
1707 // Else create an item of the object
1710 item = new MapBlockObjectItem
1711 (obj->getInventoryString());
1714 // Add to inventory and send inventory
1715 ilist->addItem(item);
1716 SendInventory(player->peer_id);
1719 // Remove from block
1720 block->removeObject(id);
1723 else if(command == TOSERVER_GROUND_ACTION)
1731 [3] v3s16 nodepos_undersurface
1732 [9] v3s16 nodepos_abovesurface
1737 2: stop digging (all parameters ignored)
1739 u8 action = readU8(&data[2]);
1741 p_under.X = readS16(&data[3]);
1742 p_under.Y = readS16(&data[5]);
1743 p_under.Z = readS16(&data[7]);
1745 p_over.X = readS16(&data[9]);
1746 p_over.Y = readS16(&data[11]);
1747 p_over.Z = readS16(&data[13]);
1748 u16 item_i = readU16(&data[15]);
1750 //TODO: Check that target is reasonably close
1758 NOTE: This can be used in the future to check if
1759 somebody is cheating, by checking the timing.
1766 else if(action == 2)
1769 RemoteClient *client = getClient(peer->id);
1770 JMutexAutoLock digmutex(client->m_dig_mutex);
1771 client->m_dig_tool_item = -1;
1776 3: Digging completed
1778 else if(action == 3)
1780 // Mandatory parameter; actually used for nothing
1781 core::map<v3s16, MapBlock*> modified_blocks;
1787 // Get material at position
1788 material = m_env.getMap().getNode(p_under).d;
1789 // If it's not diggable, do nothing
1790 if(content_diggable(material) == false)
1792 derr_server<<"Server: Not finishing digging: Node not diggable"
1797 catch(InvalidPositionException &e)
1799 derr_server<<"Server: Not finishing digging: Node not found"
1804 //TODO: Send to only other clients
1807 Send the removal to all other clients
1812 SharedBuffer<u8> reply(replysize);
1813 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1814 writeS16(&reply[2], p_under.X);
1815 writeS16(&reply[4], p_under.Y);
1816 writeS16(&reply[6], p_under.Z);
1818 for(core::map<u16, RemoteClient*>::Iterator
1819 i = m_clients.getIterator();
1820 i.atEnd() == false; i++)
1822 // Get client and check that it is valid
1823 RemoteClient *client = i.getNode()->getValue();
1824 assert(client->peer_id == i.getNode()->getKey());
1825 if(client->serialization_version == SER_FMT_VER_INVALID)
1828 // Don't send if it's the same one
1829 if(peer_id == client->peer_id)
1833 m_con.Send(client->peer_id, 0, reply, true);
1837 Update and send inventory
1840 if(g_settings.getBool("creative_mode") == false)
1845 InventoryList *mlist = player->inventory.getList("main");
1848 InventoryItem *item = mlist->getItem(item_i);
1849 if(item && (std::string)item->getName() == "ToolItem")
1851 ToolItem *titem = (ToolItem*)item;
1852 std::string toolname = titem->getToolName();
1854 // Get digging properties for material and tool
1855 DiggingProperties prop =
1856 getDiggingProperties(material, toolname);
1858 if(prop.diggable == false)
1860 derr_server<<"Server: WARNING: Player digged"
1861 <<" with impossible material + tool"
1862 <<" combination"<<std::endl;
1865 bool weared_out = titem->addWear(prop.wear);
1869 mlist->deleteItem(item_i);
1875 Add digged item to inventory
1877 InventoryItem *item = new MaterialItem(material, 1);
1878 player->inventory.addItem("main", item);
1883 SendInventory(player->peer_id);
1888 (this takes some time so it is done after the quick stuff)
1890 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1896 // Update water pressure around modification
1897 // This also adds it to m_flow_active_nodes if appropriate
1899 MapVoxelManipulator v(&m_env.getMap());
1900 v.m_disable_water_climb =
1901 g_settings.getBool("disable_water_climb");
1903 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1907 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1909 catch(ProcessingLimitException &e)
1911 dstream<<"Processing limit reached (1)"<<std::endl;
1914 v.blitBack(modified_blocks);
1920 else if(action == 1)
1923 InventoryList *ilist = player->inventory.getList("main");
1928 InventoryItem *item = ilist->getItem(item_i);
1930 // If there is no item, it is not possible to add it anywhere
1935 Handle material items
1937 if(std::string("MaterialItem") == item->getName())
1940 // Don't add a node if this is not a free space
1941 MapNode n2 = m_env.getMap().getNode(p_over);
1942 if(content_buildable_to(n2.d) == false)
1945 catch(InvalidPositionException &e)
1947 derr_server<<"Server: Ignoring ADDNODE: Node not found"
1952 // Reset build time counter
1953 getClient(peer->id)->m_time_from_building.set(0.0);
1956 MaterialItem *mitem = (MaterialItem*)item;
1958 n.d = mitem->getMaterial();
1959 if(content_directional(n.d))
1960 n.dir = packDir(p_under - p_over);
1964 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
1965 SharedBuffer<u8> reply(replysize);
1966 writeU16(&reply[0], TOCLIENT_ADDNODE);
1967 writeS16(&reply[2], p_over.X);
1968 writeS16(&reply[4], p_over.Y);
1969 writeS16(&reply[6], p_over.Z);
1970 n.serialize(&reply[8], peer_ser_ver);
1972 m_con.SendToAll(0, reply, true);
1977 InventoryList *ilist = player->inventory.getList("main");
1978 if(g_settings.getBool("creative_mode") == false && ilist)
1980 // Remove from inventory and send inventory
1981 if(mitem->getCount() == 1)
1982 ilist->deleteItem(item_i);
1986 SendInventory(peer_id);
1992 This takes some time so it is done after the quick stuff
1994 core::map<v3s16, MapBlock*> modified_blocks;
1995 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2001 InventoryList *ilist = player->inventory.getList("main");
2002 if(g_settings.getBool("creative_mode") == false && ilist)
2004 // Remove from inventory and send inventory
2005 if(mitem->getCount() == 1)
2006 ilist->deleteItem(item_i);
2010 SendInventory(peer_id);
2016 This takes some time so it is done after the quick stuff
2018 core::map<v3s16, MapBlock*> modified_blocks;
2019 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2022 Set the modified blocks unsent for all the clients
2025 //JMutexAutoLock lock2(m_con_mutex);
2027 for(core::map<u16, RemoteClient*>::Iterator
2028 i = m_clients.getIterator();
2029 i.atEnd() == false; i++)
2031 RemoteClient *client = i.getNode()->getValue();
2033 if(modified_blocks.size() > 0)
2035 // Remove block from sent history
2036 client->SetBlocksNotSent(modified_blocks);
2045 // Update water pressure around modification
2046 // This also adds it to m_flow_active_nodes if appropriate
2048 MapVoxelManipulator v(&m_env.getMap());
2049 v.m_disable_water_climb =
2050 g_settings.getBool("disable_water_climb");
2052 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2056 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2058 catch(ProcessingLimitException &e)
2060 dstream<<"Processing limit reached (1)"<<std::endl;
2063 v.blitBack(modified_blocks);
2070 v3s16 blockpos = getNodeBlockPos(p_over);
2072 MapBlock *block = NULL;
2075 block = m_env.getMap().getBlockNoCreate(blockpos);
2077 catch(InvalidPositionException &e)
2079 derr_server<<"Error while placing object: "
2080 "block not found"<<std::endl;
2084 v3s16 block_pos_i_on_map = block->getPosRelative();
2085 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2087 v3f pos = intToFloat(p_over);
2088 pos -= block_pos_f_on_map;
2090 /*dout_server<<"pos="
2091 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2094 MapBlockObject *obj = NULL;
2097 Handle block object items
2099 if(std::string("MBOItem") == item->getName())
2101 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2103 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2104 "inventorystring=\""
2105 <<oitem->getInventoryString()
2106 <<"\""<<std::endl;*/
2108 obj = oitem->createObject
2109 (pos, player->getYaw(), player->getPitch());
2116 dout_server<<"Placing a miscellaneous item on map"
2119 Create an ItemObject that contains the item.
2121 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2122 std::ostringstream os(std::ios_base::binary);
2123 item->serialize(os);
2124 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2125 iobj->setItemString(os.str());
2131 derr_server<<"WARNING: item resulted in NULL object, "
2132 <<"not placing onto map"
2137 block->addObject(obj);
2139 dout_server<<"Placed object"<<std::endl;
2141 InventoryList *ilist = player->inventory.getList("main");
2142 if(g_settings.getBool("creative_mode") == false && ilist)
2144 // Remove from inventory and send inventory
2145 ilist->deleteItem(item_i);
2147 SendInventory(peer_id);
2155 Catch invalid actions
2159 derr_server<<"WARNING: Server: Invalid action "
2160 <<action<<std::endl;
2164 else if(command == TOSERVER_RELEASE)
2173 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2176 else if(command == TOSERVER_SIGNTEXT)
2185 std::string datastring((char*)&data[2], datasize-2);
2186 std::istringstream is(datastring, std::ios_base::binary);
2189 is.read((char*)buf, 6);
2190 v3s16 blockpos = readV3S16(buf);
2191 is.read((char*)buf, 2);
2192 s16 id = readS16(buf);
2193 is.read((char*)buf, 2);
2194 u16 textlen = readU16(buf);
2196 for(u16 i=0; i<textlen; i++)
2198 is.read((char*)buf, 1);
2199 text += (char)buf[0];
2202 MapBlock *block = NULL;
2205 block = m_env.getMap().getBlockNoCreate(blockpos);
2207 catch(InvalidPositionException &e)
2209 derr_server<<"Error while setting sign text: "
2210 "block not found"<<std::endl;
2214 MapBlockObject *obj = block->getObject(id);
2217 derr_server<<"Error while setting sign text: "
2218 "object not found"<<std::endl;
2222 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2224 derr_server<<"Error while setting sign text: "
2225 "object is not a sign"<<std::endl;
2229 ((SignObject*)obj)->setText(text);
2231 obj->getBlock()->setChangedFlag();
2233 else if(command == TOSERVER_INVENTORY_ACTION)
2235 /*// Ignore inventory changes if in creative mode
2236 if(g_settings.getBool("creative_mode") == true)
2238 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2242 // Strip command and create a stream
2243 std::string datastring((char*)&data[2], datasize-2);
2244 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2245 std::istringstream is(datastring, std::ios_base::binary);
2247 InventoryAction *a = InventoryAction::deSerialize(is);
2251 Handle craftresult specially if not in creative mode
2253 bool disable_action = false;
2254 if(a->getType() == IACTION_MOVE
2255 && g_settings.getBool("creative_mode") == false)
2257 IMoveAction *ma = (IMoveAction*)a;
2258 // Don't allow moving anything to craftresult
2259 if(ma->to_name == "craftresult")
2262 disable_action = true;
2264 // When something is removed from craftresult
2265 if(ma->from_name == "craftresult")
2267 disable_action = true;
2268 // Remove stuff from craft
2269 InventoryList *clist = player->inventory.getList("craft");
2272 u16 count = ma->count;
2275 clist->decrementMaterials(count);
2278 // Feed action to player inventory
2279 a->apply(&player->inventory);
2282 // If something appeared in craftresult, throw it
2284 InventoryList *rlist = player->inventory.getList("craftresult");
2285 InventoryList *mlist = player->inventory.getList("main");
2286 if(rlist && mlist && rlist->getUsedSlots() == 1)
2288 InventoryItem *item1 = rlist->changeItem(0, NULL);
2289 mlist->addItem(item1);
2293 if(disable_action == false)
2295 // Feed action to player inventory
2296 a->apply(&player->inventory);
2301 SendInventory(player->peer_id);
2305 dstream<<"TOSERVER_INVENTORY_ACTION: "
2306 <<"InventoryAction::deSerialize() returned NULL"
2310 else if(command == TOSERVER_CHAT_MESSAGE)
2318 std::string datastring((char*)&data[2], datasize-2);
2319 std::istringstream is(datastring, std::ios_base::binary);
2322 is.read((char*)buf, 2);
2323 u16 len = readU16(buf);
2325 std::wstring message;
2326 for(u16 i=0; i<len; i++)
2328 is.read((char*)buf, 2);
2329 message += (wchar_t)readU16(buf);
2332 // Get player name of this client
2333 std::wstring name = narrow_to_wide(player->getName());
2335 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2337 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2340 Send the message to all other clients
2342 for(core::map<u16, RemoteClient*>::Iterator
2343 i = m_clients.getIterator();
2344 i.atEnd() == false; i++)
2346 // Get client and check that it is valid
2347 RemoteClient *client = i.getNode()->getValue();
2348 assert(client->peer_id == i.getNode()->getKey());
2349 if(client->serialization_version == SER_FMT_VER_INVALID)
2352 // Don't send if it's the same one
2353 if(peer_id == client->peer_id)
2356 SendChatMessage(client->peer_id, line);
2361 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2362 "unknown command "<<command<<std::endl;
2366 catch(SendFailedException &e)
2368 derr_server<<"Server::ProcessData(): SendFailedException: "
2374 /*void Server::Send(u16 peer_id, u16 channelnum,
2375 SharedBuffer<u8> data, bool reliable)
2377 JMutexAutoLock lock(m_con_mutex);
2378 m_con.Send(peer_id, channelnum, data, reliable);
2381 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2383 DSTACK(__FUNCTION_NAME);
2385 Create a packet with the block in the right format
2388 std::ostringstream os(std::ios_base::binary);
2389 block->serialize(os, ver);
2390 std::string s = os.str();
2391 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2393 u32 replysize = 8 + blockdata.getSize();
2394 SharedBuffer<u8> reply(replysize);
2395 v3s16 p = block->getPos();
2396 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2397 writeS16(&reply[2], p.X);
2398 writeS16(&reply[4], p.Y);
2399 writeS16(&reply[6], p.Z);
2400 memcpy(&reply[8], *blockdata, blockdata.getSize());
2402 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2403 <<": \tpacket size: "<<replysize<<std::endl;*/
2408 m_con.Send(peer_id, 1, reply, true);
2411 core::list<PlayerInfo> Server::getPlayerInfo()
2413 DSTACK(__FUNCTION_NAME);
2414 JMutexAutoLock envlock(m_env_mutex);
2415 JMutexAutoLock conlock(m_con_mutex);
2417 core::list<PlayerInfo> list;
2419 core::list<Player*> players = m_env.getPlayers();
2421 core::list<Player*>::Iterator i;
2422 for(i = players.begin();
2423 i != players.end(); i++)
2427 Player *player = *i;
2430 con::Peer *peer = m_con.GetPeer(player->peer_id);
2431 // Copy info from peer to info struct
2433 info.address = peer->address;
2434 info.avg_rtt = peer->avg_rtt;
2436 catch(con::PeerNotFoundException &e)
2438 // Set dummy peer info
2440 info.address = Address(0,0,0,0,0);
2444 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2445 info.position = player->getPosition();
2447 list.push_back(info);
2453 void Server::peerAdded(con::Peer *peer)
2455 DSTACK(__FUNCTION_NAME);
2456 dout_server<<"Server::peerAdded(): peer->id="
2457 <<peer->id<<std::endl;
2460 c.type = PEER_ADDED;
2461 c.peer_id = peer->id;
2463 m_peer_change_queue.push_back(c);
2466 void Server::deletingPeer(con::Peer *peer, bool timeout)
2468 DSTACK(__FUNCTION_NAME);
2469 dout_server<<"Server::deletingPeer(): peer->id="
2470 <<peer->id<<", timeout="<<timeout<<std::endl;
2473 c.type = PEER_REMOVED;
2474 c.peer_id = peer->id;
2475 c.timeout = timeout;
2476 m_peer_change_queue.push_back(c);
2479 void Server::SendObjectData(float dtime)
2481 DSTACK(__FUNCTION_NAME);
2483 core::map<v3s16, bool> stepped_blocks;
2485 for(core::map<u16, RemoteClient*>::Iterator
2486 i = m_clients.getIterator();
2487 i.atEnd() == false; i++)
2489 u16 peer_id = i.getNode()->getKey();
2490 RemoteClient *client = i.getNode()->getValue();
2491 assert(client->peer_id == peer_id);
2493 if(client->serialization_version == SER_FMT_VER_INVALID)
2496 client->SendObjectData(this, dtime, stepped_blocks);
2500 void Server::SendPlayerInfos()
2502 DSTACK(__FUNCTION_NAME);
2504 //JMutexAutoLock envlock(m_env_mutex);
2506 // Get connected players
2507 core::list<Player*> players = m_env.getPlayers(true);
2509 u32 player_count = players.getSize();
2510 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2512 SharedBuffer<u8> data(datasize);
2513 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2516 core::list<Player*>::Iterator i;
2517 for(i = players.begin();
2518 i != players.end(); i++)
2520 Player *player = *i;
2522 /*dstream<<"Server sending player info for player with "
2523 "peer_id="<<player->peer_id<<std::endl;*/
2525 writeU16(&data[start], player->peer_id);
2526 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2527 start += 2+PLAYERNAME_SIZE;
2530 //JMutexAutoLock conlock(m_con_mutex);
2533 m_con.SendToAll(0, data, true);
2551 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2557 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2563 enum ItemSpecType type;
2564 // Only other one of these is used
2570 items: a pointer to an array of 9 pointers to items
2571 specs: a pointer to an array of 9 ItemSpecs
2573 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2575 u16 items_min_x = 100;
2576 u16 items_max_x = 100;
2577 u16 items_min_y = 100;
2578 u16 items_max_y = 100;
2579 for(u16 y=0; y<3; y++)
2580 for(u16 x=0; x<3; x++)
2582 if(items[y*3 + x] == NULL)
2584 if(items_min_x == 100 || x < items_min_x)
2586 if(items_min_y == 100 || y < items_min_y)
2588 if(items_max_x == 100 || x > items_max_x)
2590 if(items_max_y == 100 || y > items_max_y)
2593 // No items at all, just return false
2594 if(items_min_x == 100)
2597 u16 items_w = items_max_x - items_min_x + 1;
2598 u16 items_h = items_max_y - items_min_y + 1;
2600 u16 specs_min_x = 100;
2601 u16 specs_max_x = 100;
2602 u16 specs_min_y = 100;
2603 u16 specs_max_y = 100;
2604 for(u16 y=0; y<3; y++)
2605 for(u16 x=0; x<3; x++)
2607 if(specs[y*3 + x].type == ITEM_NONE)
2609 if(specs_min_x == 100 || x < specs_min_x)
2611 if(specs_min_y == 100 || y < specs_min_y)
2613 if(specs_max_x == 100 || x > specs_max_x)
2615 if(specs_max_y == 100 || y > specs_max_y)
2618 // No specs at all, just return false
2619 if(specs_min_x == 100)
2622 u16 specs_w = specs_max_x - specs_min_x + 1;
2623 u16 specs_h = specs_max_y - specs_min_y + 1;
2626 if(items_w != specs_w || items_h != specs_h)
2629 for(u16 y=0; y<specs_h; y++)
2630 for(u16 x=0; x<specs_w; x++)
2632 u16 items_x = items_min_x + x;
2633 u16 items_y = items_min_y + y;
2634 u16 specs_x = specs_min_x + x;
2635 u16 specs_y = specs_min_y + y;
2636 InventoryItem *item = items[items_y * 3 + items_x];
2637 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2639 if(spec.type == ITEM_NONE)
2641 // Has to be no item
2647 // There should be an item
2651 std::string itemname = item->getName();
2653 if(spec.type == ITEM_MATERIAL)
2655 if(itemname != "MaterialItem")
2657 MaterialItem *mitem = (MaterialItem*)item;
2658 if(mitem->getMaterial() != spec.num)
2661 else if(spec.type == ITEM_CRAFT)
2663 if(itemname != "CraftItem")
2665 CraftItem *mitem = (CraftItem*)item;
2666 if(mitem->getSubName() != spec.name)
2669 else if(spec.type == ITEM_TOOL)
2671 // Not supported yet
2674 else if(spec.type == ITEM_MBO)
2676 // Not supported yet
2681 // Not supported yet
2689 void Server::SendInventory(u16 peer_id)
2691 DSTACK(__FUNCTION_NAME);
2693 Player* player = m_env.getPlayer(peer_id);
2696 Calculate crafting stuff
2698 if(g_settings.getBool("creative_mode") == false)
2700 InventoryList *clist = player->inventory.getList("craft");
2701 InventoryList *rlist = player->inventory.getList("craftresult");
2704 rlist->clearItems();
2708 InventoryItem *items[9];
2709 for(u16 i=0; i<9; i++)
2711 items[i] = clist->getItem(i);
2720 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2721 if(checkItemCombination(items, specs))
2723 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2732 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2733 if(checkItemCombination(items, specs))
2735 rlist->addItem(new CraftItem("Stick", 4));
2744 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2745 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2746 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2747 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2748 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2749 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2750 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2751 if(checkItemCombination(items, specs))
2753 rlist->addItem(new MapBlockObjectItem("Sign"));
2762 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2763 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2764 if(checkItemCombination(items, specs))
2766 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2775 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2776 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2777 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2778 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2779 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2780 if(checkItemCombination(items, specs))
2782 rlist->addItem(new ToolItem("WPick", 0));
2791 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2792 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2793 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2794 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2795 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2796 if(checkItemCombination(items, specs))
2798 rlist->addItem(new ToolItem("STPick", 0));
2807 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2808 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2809 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2810 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2811 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2812 if(checkItemCombination(items, specs))
2814 rlist->addItem(new ToolItem("MesePick", 0));
2819 } // if creative_mode == false
2825 std::ostringstream os;
2826 //os.imbue(std::locale("C"));
2828 player->inventory.serialize(os);
2830 std::string s = os.str();
2832 SharedBuffer<u8> data(s.size()+2);
2833 writeU16(&data[0], TOCLIENT_INVENTORY);
2834 memcpy(&data[2], s.c_str(), s.size());
2837 m_con.Send(peer_id, 0, data, true);
2840 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2842 DSTACK(__FUNCTION_NAME);
2844 std::ostringstream os(std::ios_base::binary);
2848 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2849 os.write((char*)buf, 2);
2852 writeU16(buf, message.size());
2853 os.write((char*)buf, 2);
2856 for(u32 i=0; i<message.size(); i++)
2860 os.write((char*)buf, 2);
2864 std::string s = os.str();
2865 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2867 m_con.Send(peer_id, 0, data, true);
2870 void Server::BroadcastChatMessage(const std::wstring &message)
2872 for(core::map<u16, RemoteClient*>::Iterator
2873 i = m_clients.getIterator();
2874 i.atEnd() == false; i++)
2876 // Get client and check that it is valid
2877 RemoteClient *client = i.getNode()->getValue();
2878 assert(client->peer_id == i.getNode()->getKey());
2879 if(client->serialization_version == SER_FMT_VER_INVALID)
2882 SendChatMessage(client->peer_id, message);
2886 void Server::SendBlocks(float dtime)
2888 DSTACK(__FUNCTION_NAME);
2890 JMutexAutoLock envlock(m_env_mutex);
2892 core::array<PrioritySortedBlockTransfer> queue;
2894 s32 total_sending = 0;
2896 for(core::map<u16, RemoteClient*>::Iterator
2897 i = m_clients.getIterator();
2898 i.atEnd() == false; i++)
2900 RemoteClient *client = i.getNode()->getValue();
2901 assert(client->peer_id == i.getNode()->getKey());
2903 total_sending += client->SendingCount();
2905 if(client->serialization_version == SER_FMT_VER_INVALID)
2908 client->GetNextBlocks(this, dtime, queue);
2912 // Lowest priority number comes first.
2913 // Lowest is most important.
2916 JMutexAutoLock conlock(m_con_mutex);
2918 for(u32 i=0; i<queue.size(); i++)
2920 //TODO: Calculate limit dynamically
2921 if(total_sending >= g_settings.getS32
2922 ("max_simultaneous_block_sends_server_total"))
2925 PrioritySortedBlockTransfer q = queue[i];
2927 MapBlock *block = NULL;
2930 block = m_env.getMap().getBlockNoCreate(q.pos);
2932 catch(InvalidPositionException &e)
2937 RemoteClient *client = getClient(q.peer_id);
2939 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2941 client->SentBlock(q.pos);
2948 RemoteClient* Server::getClient(u16 peer_id)
2950 DSTACK(__FUNCTION_NAME);
2951 //JMutexAutoLock lock(m_con_mutex);
2952 core::map<u16, RemoteClient*>::Node *n;
2953 n = m_clients.find(peer_id);
2954 // A client should exist for all peers
2956 return n->getValue();
2959 Player *Server::emergePlayer(const char *name, const char *password)
2962 Try to get an existing player
2964 Player *player = m_env.getPlayer(name);
2975 player = new ServerRemotePlayer();
2976 //player->peer_id = c.peer_id;
2977 player->peer_id = PEER_ID_INEXISTENT;
2978 player->updateName(name);
2984 // We're going to throw the player to this position
2985 //v2s16 nodepos(29990,29990);
2986 //v2s16 nodepos(9990,9990);
2988 v2s16 sectorpos = getNodeSectorPos(nodepos);
2990 m_env.getMap().emergeSector(sectorpos);
2991 // Get ground height at point
2992 f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
2993 // The sector should have been generated -> groundheight exists
2994 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
2995 // Don't go underwater
2996 if(groundheight < WATER_LEVEL)
2997 groundheight = WATER_LEVEL;
3002 f32 groundheight = 0;
3003 // Try to find a good place a few times
3004 for(s32 i=0; i<100; i++)
3006 s32 range = 1 + i*2;
3007 // We're going to try to throw the player to this position
3008 nodepos = v2s16(-range/2 + (myrand()%range),
3009 -range/2 + (myrand()%range));
3010 v2s16 sectorpos = getNodeSectorPos(nodepos);
3012 m_env.getMap().emergeSector(sectorpos);
3013 // Get ground height at point
3014 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3015 // The sector should have been generated -> groundheight exists
3016 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3017 // Don't go underwater
3018 if(groundheight < WATER_LEVEL)
3020 // Don't go inside ground
3022 v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3023 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);
3024 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3025 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3030 }catch(InvalidPositionException &e)
3032 // Ignore invalid position
3035 // Found a good place
3040 // If no suitable place was not found, go above water at least.
3041 if(groundheight < WATER_LEVEL)
3042 groundheight = WATER_LEVEL;
3044 player->setPosition(intToFloat(v3s16(
3051 Add player to environment
3054 m_env.addPlayer(player);
3057 Add stuff to inventory
3060 if(g_settings.getBool("creative_mode"))
3062 // Give some good picks
3064 InventoryItem *item = new ToolItem("STPick", 0);
3065 void* r = player->inventory.addItem("main", item);
3069 InventoryItem *item = new ToolItem("MesePick", 0);
3070 void* r = player->inventory.addItem("main", item);
3077 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3080 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3081 player->inventory.addItem("main", item);
3084 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3086 // Skip some materials
3087 if(i == CONTENT_OCEAN || i == CONTENT_TORCH)
3090 InventoryItem *item = new MaterialItem(i, 1);
3091 player->inventory.addItem("main", item);
3095 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3096 void* r = player->inventory.addItem("main", item);
3103 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3104 void* r = player->inventory.addItem("main", item);
3108 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3109 void* r = player->inventory.addItem("main", item);
3113 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3114 void* r = player->inventory.addItem("main", item);
3118 InventoryItem *item = new CraftItem("Stick", 4);
3119 void* r = player->inventory.addItem("main", item);
3123 InventoryItem *item = new ToolItem("WPick", 32000);
3124 void* r = player->inventory.addItem("main", item);
3128 InventoryItem *item = new ToolItem("STPick", 32000);
3129 void* r = player->inventory.addItem("main", item);
3132 /*// Give some lights
3134 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3135 bool r = player->inventory.addItem("main", item);
3139 for(u16 i=0; i<4; i++)
3141 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3142 bool r = player->inventory.addItem("main", item);
3145 /*// Give some other stuff
3147 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3148 bool r = player->inventory.addItem("main", item);
3157 void Server::UpdateBlockWaterPressure(MapBlock *block,
3158 core::map<v3s16, MapBlock*> &modified_blocks)
3160 MapVoxelManipulator v(&m_env.getMap());
3161 v.m_disable_water_climb =
3162 g_settings.getBool("disable_water_climb");
3164 VoxelArea area(block->getPosRelative(),
3165 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3169 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3171 catch(ProcessingLimitException &e)
3173 dstream<<"Processing limit reached (1)"<<std::endl;
3176 v.blitBack(modified_blocks);
3179 void Server::handlePeerChange(PeerChange &c)
3181 JMutexAutoLock envlock(m_env_mutex);
3182 JMutexAutoLock conlock(m_con_mutex);
3184 if(c.type == PEER_ADDED)
3191 core::map<u16, RemoteClient*>::Node *n;
3192 n = m_clients.find(c.peer_id);
3193 // The client shouldn't already exist
3197 RemoteClient *client = new RemoteClient();
3198 client->peer_id = c.peer_id;
3199 m_clients.insert(client->peer_id, client);
3202 else if(c.type == PEER_REMOVED)
3209 core::map<u16, RemoteClient*>::Node *n;
3210 n = m_clients.find(c.peer_id);
3211 // The client should exist
3214 // Collect information about leaving in chat
3215 std::wstring message;
3217 std::wstring name = L"unknown";
3218 Player *player = m_env.getPlayer(c.peer_id);
3220 name = narrow_to_wide(player->getName());
3224 message += L" left game";
3226 message += L" (timed out)";
3231 m_env.removePlayer(c.peer_id);
3234 // Set player client disconnected
3236 Player *player = m_env.getPlayer(c.peer_id);
3237 player->peer_id = 0;
3241 delete m_clients[c.peer_id];
3242 m_clients.remove(c.peer_id);
3244 // Send player info to all remaining clients
3247 // Send leave chat message to all remaining clients
3248 BroadcastChatMessage(message);
3257 void Server::handlePeerChanges()
3259 while(m_peer_change_queue.size() > 0)
3261 PeerChange c = m_peer_change_queue.pop_front();
3263 dout_server<<"Server: Handling peer change: "
3264 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3267 handlePeerChange(c);