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 // First check if the block already exists
156 block = map.getBlockNoCreate(p);
161 block = map.emergeBlock(
165 lighting_invalidated_blocks);
168 EXPERIMENTAL: Create a few other blocks too
175 lighting_invalidated_blocks);
181 lighting_invalidated_blocks);
185 // If it is a dummy, block was not found on disk
188 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
192 catch(InvalidPositionException &e)
195 // This happens when position is over limit.
201 if(debug && changed_blocks.size() > 0)
203 dout_server<<DTIME<<"Got changed_blocks: ";
204 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
205 i.atEnd() == false; i++)
207 MapBlock *block = i.getNode()->getValue();
208 v3s16 p = block->getPos();
209 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
211 dout_server<<std::endl;
215 Update water pressure
218 m_server->UpdateBlockWaterPressure(block, modified_blocks);
220 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
221 i.atEnd() == false; i++)
223 MapBlock *block = i.getNode()->getValue();
224 m_server->UpdateBlockWaterPressure(block, modified_blocks);
225 //v3s16 p = i.getNode()->getKey();
226 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
230 Collect a list of blocks that have been modified in
231 addition to the fetched one.
234 // Add all the "changed blocks" to modified_blocks
235 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
236 i.atEnd() == false; i++)
238 MapBlock *block = i.getNode()->getValue();
239 modified_blocks.insert(block->getPos(), block);
242 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
243 <<" blocks"<<std::endl;*/
245 //TimeTaker timer("** updateLighting", g_device);
247 // Update lighting without locking the environment mutex,
248 // add modified blocks to changed blocks
249 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
251 // If we got no block, there should be no invalidated blocks
254 assert(lighting_invalidated_blocks.size() == 0);
260 Set sent status of modified blocks on clients
263 // NOTE: Server's clients are also behind the connection mutex
264 JMutexAutoLock lock(m_server->m_con_mutex);
267 Add the originally fetched block to the modified list
271 modified_blocks.insert(p, block);
275 Set the modified blocks unsent for all the clients
278 for(core::map<u16, RemoteClient*>::Iterator
279 i = m_server->m_clients.getIterator();
280 i.atEnd() == false; i++)
282 RemoteClient *client = i.getNode()->getValue();
284 if(modified_blocks.size() > 0)
286 // Remove block from sent history
287 client->SetBlocksNotSent(modified_blocks);
293 END_DEBUG_EXCEPTION_HANDLER
298 void RemoteClient::GetNextBlocks(Server *server, float dtime,
299 core::array<PrioritySortedBlockTransfer> &dest)
301 DSTACK(__FUNCTION_NAME);
305 JMutexAutoLock lock(m_blocks_sent_mutex);
306 m_nearest_unsent_reset_timer += dtime;
309 // Won't send anything if already sending
311 JMutexAutoLock lock(m_blocks_sending_mutex);
313 if(m_blocks_sending.size() >= g_settings.getU16
314 ("max_simultaneous_block_sends_per_client"))
316 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
321 Player *player = server->m_env.getPlayer(peer_id);
323 assert(player != NULL);
325 v3f playerpos = player->getPosition();
326 v3f playerspeed = player->getSpeed();
328 v3s16 center_nodepos = floatToInt(playerpos);
330 v3s16 center = getNodeBlockPos(center_nodepos);
333 Get the starting value of the block finder radius.
335 s16 last_nearest_unsent_d;
338 JMutexAutoLock lock(m_blocks_sent_mutex);
340 if(m_last_center != center)
342 m_nearest_unsent_d = 0;
343 m_last_center = center;
346 /*dstream<<"m_nearest_unsent_reset_timer="
347 <<m_nearest_unsent_reset_timer<<std::endl;*/
348 if(m_nearest_unsent_reset_timer > 5.0)
350 m_nearest_unsent_reset_timer = 0;
351 m_nearest_unsent_d = 0;
352 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
355 last_nearest_unsent_d = m_nearest_unsent_d;
357 d_start = m_nearest_unsent_d;
360 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
361 ("max_simultaneous_block_sends_per_client");
362 u16 maximum_simultaneous_block_sends =
363 maximum_simultaneous_block_sends_setting;
366 Check the time from last addNode/removeNode.
368 Decrease send rate if player is building stuff.
371 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
372 m_time_from_building.m_value += dtime;
373 /*if(m_time_from_building.m_value
374 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
375 if(m_time_from_building.m_value < g_settings.getFloat(
376 "full_block_send_enable_min_time_from_building"))
378 maximum_simultaneous_block_sends
379 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
383 u32 num_blocks_selected;
385 JMutexAutoLock lock(m_blocks_sending_mutex);
386 num_blocks_selected = m_blocks_sending.size();
390 next time d will be continued from the d from which the nearest
391 unsent block was found this time.
393 This is because not necessarily any of the blocks found this
394 time are actually sent.
396 s32 new_nearest_unsent_d = -1;
398 // Serialization version used
399 //u8 ser_version = serialization_version;
401 //bool has_incomplete_blocks = false;
403 s16 d_max = g_settings.getS16("max_block_send_distance");
404 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
406 //dstream<<"Starting from "<<d_start<<std::endl;
408 for(s16 d = d_start; d <= d_max; d++)
410 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
412 //if(has_incomplete_blocks == false)
414 JMutexAutoLock lock(m_blocks_sent_mutex);
416 If m_nearest_unsent_d was changed by the EmergeThread
417 (it can change it to 0 through SetBlockNotSent),
419 Else update m_nearest_unsent_d
421 if(m_nearest_unsent_d != last_nearest_unsent_d)
423 d = m_nearest_unsent_d;
424 last_nearest_unsent_d = m_nearest_unsent_d;
429 Get the border/face dot coordinates of a "d-radiused"
432 core::list<v3s16> list;
433 getFacePositions(list, d);
435 core::list<v3s16>::Iterator li;
436 for(li=list.begin(); li!=list.end(); li++)
438 v3s16 p = *li + center;
442 - Don't allow too many simultaneous transfers
443 - EXCEPT when the blocks are very close
445 Also, don't send blocks that are already flying.
448 u16 maximum_simultaneous_block_sends_now =
449 maximum_simultaneous_block_sends;
451 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
453 maximum_simultaneous_block_sends_now =
454 maximum_simultaneous_block_sends_setting;
458 JMutexAutoLock lock(m_blocks_sending_mutex);
460 // Limit is dynamically lowered when building
461 if(num_blocks_selected
462 >= maximum_simultaneous_block_sends_now)
464 /*dstream<<"Not sending more blocks. Queue full. "
465 <<m_blocks_sending.size()
470 if(m_blocks_sending.find(p) != NULL)
477 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
478 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
479 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
480 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
481 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
482 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
485 bool generate = d <= d_max_gen;
489 // Don't generate above player
495 // Limit the generating area vertically to 2/3
496 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
501 Don't send already sent blocks
504 JMutexAutoLock lock(m_blocks_sent_mutex);
506 if(m_blocks_sent.find(p) != NULL)
513 Ignore block if it is not at ground surface
515 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
516 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
517 f32 y = server->m_env.getMap().getGroundHeight(p2d);
518 // The sector might not exist yet, thus no heightmap
519 if(y > GROUNDHEIGHT_VALID_MINVALUE)
521 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
522 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3)
528 Check if map has this block
530 MapBlock *block = NULL;
533 block = server->m_env.getMap().getBlockNoCreate(p);
535 catch(InvalidPositionException &e)
539 bool surely_not_found_on_disk = false;
542 /*if(block->isIncomplete())
544 has_incomplete_blocks = true;
550 surely_not_found_on_disk = true;
555 If block has been marked to not exist on disk (dummy)
556 and generating new ones is not wanted, skip block.
558 if(generate == false && surely_not_found_on_disk == true)
565 Record the lowest d from which a a block has been
566 found being not sent and possibly to exist
568 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
570 new_nearest_unsent_d = d;
574 Add inexistent block to emerge queue.
576 if(block == NULL || surely_not_found_on_disk)
578 /*SharedPtr<JMutexAutoLock> lock
579 (m_num_blocks_in_emerge_queue.getLock());*/
581 //TODO: Get value from somewhere
582 // Allow only one block in emerge queue
583 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
585 // Add it to the emerge queue and trigger the thread
588 if(generate == false)
589 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
591 server->m_emerge_queue.addBlock(peer_id, p, flags);
592 server->m_emergethread.trigger();
603 PrioritySortedBlockTransfer q((float)d, p, peer_id);
607 num_blocks_selected += 1;
612 if(new_nearest_unsent_d != -1)
614 JMutexAutoLock lock(m_blocks_sent_mutex);
615 m_nearest_unsent_d = new_nearest_unsent_d;
619 void RemoteClient::SendObjectData(
622 core::map<v3s16, bool> &stepped_blocks
625 DSTACK(__FUNCTION_NAME);
627 // Can't send anything without knowing version
628 if(serialization_version == SER_FMT_VER_INVALID)
630 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
636 Send a TOCLIENT_OBJECTDATA packet.
640 u16 number of player positions
651 std::ostringstream os(std::ios_base::binary);
655 writeU16(buf, TOCLIENT_OBJECTDATA);
656 os.write((char*)buf, 2);
659 Get and write player data
662 // Get connected players
663 core::list<Player*> players = server->m_env.getPlayers(true);
665 // Write player count
666 u16 playercount = players.size();
667 writeU16(buf, playercount);
668 os.write((char*)buf, 2);
670 core::list<Player*>::Iterator i;
671 for(i = players.begin();
672 i != players.end(); i++)
676 v3f pf = player->getPosition();
677 v3f sf = player->getSpeed();
679 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
680 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
681 s32 pitch_i (player->getPitch() * 100);
682 s32 yaw_i (player->getYaw() * 100);
684 writeU16(buf, player->peer_id);
685 os.write((char*)buf, 2);
686 writeV3S32(buf, position_i);
687 os.write((char*)buf, 12);
688 writeV3S32(buf, speed_i);
689 os.write((char*)buf, 12);
690 writeS32(buf, pitch_i);
691 os.write((char*)buf, 4);
692 writeS32(buf, yaw_i);
693 os.write((char*)buf, 4);
697 Get and write object data
703 For making players to be able to build to their nearby
704 environment (building is not possible on blocks that are not
707 - Add blocks to emerge queue if they are not found
709 SUGGESTION: These could be ignored from the backside of the player
712 Player *player = server->m_env.getPlayer(peer_id);
716 v3f playerpos = player->getPosition();
717 v3f playerspeed = player->getSpeed();
719 v3s16 center_nodepos = floatToInt(playerpos);
720 v3s16 center = getNodeBlockPos(center_nodepos);
722 s16 d_max = g_settings.getS16("active_object_range");
724 // Number of blocks whose objects were written to bos
727 std::ostringstream bos(std::ios_base::binary);
729 for(s16 d = 0; d <= d_max; d++)
731 core::list<v3s16> list;
732 getFacePositions(list, d);
734 core::list<v3s16>::Iterator li;
735 for(li=list.begin(); li!=list.end(); li++)
737 v3s16 p = *li + center;
740 Ignore blocks that haven't been sent to the client
743 JMutexAutoLock sentlock(m_blocks_sent_mutex);
744 if(m_blocks_sent.find(p) == NULL)
748 // Try stepping block and add it to a send queue
753 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
756 Step block if not in stepped_blocks and add to stepped_blocks.
758 if(stepped_blocks.find(p) == NULL)
760 block->stepObjects(dtime, true, server->getDayNightRatio());
761 stepped_blocks.insert(p, true);
762 block->setChangedFlag();
765 // Skip block if there are no objects
766 if(block->getObjectCount() == 0)
775 bos.write((char*)buf, 6);
778 block->serializeObjects(bos, serialization_version);
783 Stop collecting objects if data is already too big
785 // Sum of player and object data sizes
786 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
787 // break out if data too big
788 if(sum > MAX_OBJECTDATA_SIZE)
790 goto skip_subsequent;
794 catch(InvalidPositionException &e)
797 // Add it to the emerge queue and trigger the thread.
798 // Fetch the block only if it is on disk.
800 // Grab and increment counter
801 /*SharedPtr<JMutexAutoLock> lock
802 (m_num_blocks_in_emerge_queue.getLock());
803 m_num_blocks_in_emerge_queue.m_value++;*/
805 // Add to queue as an anonymous fetch from disk
806 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
807 server->m_emerge_queue.addBlock(0, p, flags);
808 server->m_emergethread.trigger();
816 writeU16(buf, blockcount);
817 os.write((char*)buf, 2);
819 // Write block objects
826 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
829 std::string s = os.str();
830 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
831 // Send as unreliable
832 server->m_con.Send(peer_id, 0, data, false);
835 void RemoteClient::GotBlock(v3s16 p)
837 JMutexAutoLock lock(m_blocks_sending_mutex);
838 JMutexAutoLock lock2(m_blocks_sent_mutex);
839 if(m_blocks_sending.find(p) != NULL)
840 m_blocks_sending.remove(p);
842 dstream<<"RemoteClient::GotBlock(): Didn't find in"
843 " m_blocks_sending"<<std::endl;
844 m_blocks_sent.insert(p, true);
847 void RemoteClient::SentBlock(v3s16 p)
849 JMutexAutoLock lock(m_blocks_sending_mutex);
850 if(m_blocks_sending.size() > 15)
852 dstream<<"RemoteClient::SentBlock(): "
853 <<"m_blocks_sending.size()="
854 <<m_blocks_sending.size()<<std::endl;
856 if(m_blocks_sending.find(p) == NULL)
857 m_blocks_sending.insert(p, 0.0);
859 dstream<<"RemoteClient::SentBlock(): Sent block"
860 " already in m_blocks_sending"<<std::endl;
863 void RemoteClient::SetBlockNotSent(v3s16 p)
865 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
866 JMutexAutoLock sentlock(m_blocks_sent_mutex);
868 m_nearest_unsent_d = 0;
870 if(m_blocks_sending.find(p) != NULL)
871 m_blocks_sending.remove(p);
872 if(m_blocks_sent.find(p) != NULL)
873 m_blocks_sent.remove(p);
876 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
878 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
879 JMutexAutoLock sentlock(m_blocks_sent_mutex);
881 m_nearest_unsent_d = 0;
883 for(core::map<v3s16, MapBlock*>::Iterator
884 i = blocks.getIterator();
885 i.atEnd()==false; i++)
887 v3s16 p = i.getNode()->getKey();
889 if(m_blocks_sending.find(p) != NULL)
890 m_blocks_sending.remove(p);
891 if(m_blocks_sent.find(p) != NULL)
892 m_blocks_sent.remove(p);
900 PlayerInfo::PlayerInfo()
905 void PlayerInfo::PrintLine(std::ostream *s)
908 (*s)<<"\""<<name<<"\" ("
909 <<position.X<<","<<position.Y
910 <<","<<position.Z<<") ";
912 (*s)<<" avg_rtt="<<avg_rtt;
916 u32 PIChecksum(core::list<PlayerInfo> &l)
918 core::list<PlayerInfo>::Iterator i;
921 for(i=l.begin(); i!=l.end(); i++)
923 checksum += a * (i->id+1);
924 checksum ^= 0x435aafcd;
935 std::string mapsavedir,
939 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
940 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
942 m_emergethread(this),
945 m_time_of_day_send_timer(0),
948 m_flowwater_timer = 0.0;
949 m_print_info_timer = 0.0;
950 m_objectdata_timer = 0.0;
951 m_emergethread_trigger_timer = 0.0;
952 m_savemap_timer = 0.0;
956 m_step_dtime_mutex.Init();
965 JMutexAutoLock clientslock(m_con_mutex);
967 for(core::map<u16, RemoteClient*>::Iterator
968 i = m_clients.getIterator();
969 i.atEnd() == false; i++)
972 // NOTE: These are removed by env destructor
974 u16 peer_id = i.getNode()->getKey();
975 JMutexAutoLock envlock(m_env_mutex);
976 m_env.removePlayer(peer_id);
980 delete i.getNode()->getValue();
984 void Server::start(unsigned short port)
986 DSTACK(__FUNCTION_NAME);
987 // Stop thread if already running
990 // Initialize connection
991 m_con.setTimeoutMs(30);
995 m_thread.setRun(true);
998 dout_server<<"Server started on port "<<port<<std::endl;
1003 DSTACK(__FUNCTION_NAME);
1004 // Stop threads (set run=false first so both start stopping)
1005 m_thread.setRun(false);
1006 m_emergethread.setRun(false);
1008 m_emergethread.stop();
1010 dout_server<<"Server threads stopped"<<std::endl;
1013 void Server::step(float dtime)
1015 DSTACK(__FUNCTION_NAME);
1020 JMutexAutoLock lock(m_step_dtime_mutex);
1021 m_step_dtime += dtime;
1025 void Server::AsyncRunStep()
1027 DSTACK(__FUNCTION_NAME);
1031 JMutexAutoLock lock1(m_step_dtime_mutex);
1032 dtime = m_step_dtime;
1035 // Send blocks to clients
1041 //dstream<<"Server steps "<<dtime<<std::endl;
1042 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1045 JMutexAutoLock lock1(m_step_dtime_mutex);
1046 m_step_dtime -= dtime;
1053 m_uptime.set(m_uptime.get() + dtime);
1057 Update m_time_of_day
1060 m_time_counter += dtime;
1061 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1062 u32 units = (u32)(m_time_counter*speed);
1063 m_time_counter -= (f32)units / speed;
1064 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1066 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1069 Send to clients at constant intervals
1072 m_time_of_day_send_timer -= dtime;
1073 if(m_time_of_day_send_timer < 0.0)
1075 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1077 //JMutexAutoLock envlock(m_env_mutex);
1078 JMutexAutoLock conlock(m_con_mutex);
1080 for(core::map<u16, RemoteClient*>::Iterator
1081 i = m_clients.getIterator();
1082 i.atEnd() == false; i++)
1084 RemoteClient *client = i.getNode()->getValue();
1085 //Player *player = m_env.getPlayer(client->peer_id);
1087 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1088 m_time_of_day.get());
1090 m_con.Send(client->peer_id, 0, data, true);
1096 // Process connection's timeouts
1097 JMutexAutoLock lock2(m_con_mutex);
1098 m_con.RunTimeouts(dtime);
1102 // This has to be called so that the client list gets synced
1103 // with the peer list of the connection
1104 handlePeerChanges();
1109 // This also runs Map's timers
1110 JMutexAutoLock lock(m_env_mutex);
1124 if(g_settings.getBool("endless_water") == false)
1129 float &counter = m_flowwater_timer;
1131 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1136 core::map<v3s16, MapBlock*> modified_blocks;
1140 JMutexAutoLock envlock(m_env_mutex);
1142 MapVoxelManipulator v(&m_env.getMap());
1143 v.m_disable_water_climb =
1144 g_settings.getBool("disable_water_climb");
1146 if(g_settings.getBool("endless_water") == false)
1147 v.flowWater(m_flow_active_nodes, 0, false, 250);
1149 v.flowWater(m_flow_active_nodes, 0, false, 50);
1151 v.blitBack(modified_blocks);
1153 ServerMap &map = ((ServerMap&)m_env.getMap());
1156 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1157 map.updateLighting(modified_blocks, lighting_modified_blocks);
1159 // Add blocks modified by lighting to modified_blocks
1160 for(core::map<v3s16, MapBlock*>::Iterator
1161 i = lighting_modified_blocks.getIterator();
1162 i.atEnd() == false; i++)
1164 MapBlock *block = i.getNode()->getValue();
1165 modified_blocks.insert(block->getPos(), block);
1170 Set the modified blocks unsent for all the clients
1173 JMutexAutoLock lock2(m_con_mutex);
1175 for(core::map<u16, RemoteClient*>::Iterator
1176 i = m_clients.getIterator();
1177 i.atEnd() == false; i++)
1179 RemoteClient *client = i.getNode()->getValue();
1181 if(modified_blocks.size() > 0)
1183 // Remove block from sent history
1184 client->SetBlocksNotSent(modified_blocks);
1188 } // interval counter
1191 // Periodically print some info
1193 float &counter = m_print_info_timer;
1199 JMutexAutoLock lock2(m_con_mutex);
1201 for(core::map<u16, RemoteClient*>::Iterator
1202 i = m_clients.getIterator();
1203 i.atEnd() == false; i++)
1205 //u16 peer_id = i.getNode()->getKey();
1206 RemoteClient *client = i.getNode()->getValue();
1207 client->PrintInfo(std::cout);
1215 NOTE: Some of this could be moved to RemoteClient
1219 JMutexAutoLock envlock(m_env_mutex);
1220 JMutexAutoLock conlock(m_con_mutex);
1222 for(core::map<u16, RemoteClient*>::Iterator
1223 i = m_clients.getIterator();
1224 i.atEnd() == false; i++)
1226 RemoteClient *client = i.getNode()->getValue();
1227 Player *player = m_env.getPlayer(client->peer_id);
1229 JMutexAutoLock digmutex(client->m_dig_mutex);
1231 if(client->m_dig_tool_item == -1)
1234 client->m_dig_time_remaining -= dtime;
1236 if(client->m_dig_time_remaining > 0)
1238 client->m_time_from_building.set(0.0);
1242 v3s16 p_under = client->m_dig_position;
1244 // Mandatory parameter; actually used for nothing
1245 core::map<v3s16, MapBlock*> modified_blocks;
1251 // Get material at position
1252 material = m_env.getMap().getNode(p_under).d;
1253 // If it's not diggable, do nothing
1254 if(content_diggable(material) == false)
1256 derr_server<<"Server: Not finishing digging: Node not diggable"
1258 client->m_dig_tool_item = -1;
1262 catch(InvalidPositionException &e)
1264 derr_server<<"Server: Not finishing digging: Node not found"
1266 client->m_dig_tool_item = -1;
1272 SharedBuffer<u8> reply(replysize);
1273 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1274 writeS16(&reply[2], p_under.X);
1275 writeS16(&reply[4], p_under.Y);
1276 writeS16(&reply[6], p_under.Z);
1278 m_con.SendToAll(0, reply, true);
1280 if(g_settings.getBool("creative_mode") == false)
1282 // Add to inventory and send inventory
1283 InventoryItem *item = new MaterialItem(material, 1);
1284 player->inventory.addItem("main", item);
1285 SendInventory(player->peer_id);
1290 (this takes some time so it is done after the quick stuff)
1292 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1298 // Update water pressure around modification
1299 // This also adds it to m_flow_active_nodes if appropriate
1301 MapVoxelManipulator v(&m_env.getMap());
1302 v.m_disable_water_climb =
1303 g_settings.getBool("disable_water_climb");
1305 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1309 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1311 catch(ProcessingLimitException &e)
1313 dstream<<"Processing limit reached (1)"<<std::endl;
1316 v.blitBack(modified_blocks);
1321 // Send object positions
1323 float &counter = m_objectdata_timer;
1325 if(counter >= g_settings.getFloat("objectdata_interval"))
1327 JMutexAutoLock lock1(m_env_mutex);
1328 JMutexAutoLock lock2(m_con_mutex);
1329 SendObjectData(counter);
1335 // Trigger emergethread (it gets somehow gets to a
1336 // non-triggered but bysy state sometimes)
1338 float &counter = m_emergethread_trigger_timer;
1344 m_emergethread.trigger();
1350 float &counter = m_savemap_timer;
1352 if(counter >= g_settings.getFloat("server_map_save_interval"))
1356 JMutexAutoLock lock(m_env_mutex);
1358 // Save only changed parts
1359 m_env.getMap().save(true);
1361 // Delete unused sectors
1362 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1363 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1364 if(deleted_count > 0)
1366 dout_server<<"Server: Unloaded "<<deleted_count
1367 <<" sectors from memory"<<std::endl;
1373 void Server::Receive()
1375 DSTACK(__FUNCTION_NAME);
1376 u32 data_maxsize = 10000;
1377 Buffer<u8> data(data_maxsize);
1382 JMutexAutoLock conlock(m_con_mutex);
1383 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1386 // This has to be called so that the client list gets synced
1387 // with the peer list of the connection
1388 handlePeerChanges();
1390 ProcessData(*data, datasize, peer_id);
1392 catch(con::InvalidIncomingDataException &e)
1394 derr_server<<"Server::Receive(): "
1395 "InvalidIncomingDataException: what()="
1396 <<e.what()<<std::endl;
1398 catch(con::PeerNotFoundException &e)
1400 //NOTE: This is not needed anymore
1402 // The peer has been disconnected.
1403 // Find the associated player and remove it.
1405 /*JMutexAutoLock envlock(m_env_mutex);
1407 dout_server<<"ServerThread: peer_id="<<peer_id
1408 <<" has apparently closed connection. "
1409 <<"Removing player."<<std::endl;
1411 m_env.removePlayer(peer_id);*/
1415 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1417 DSTACK(__FUNCTION_NAME);
1418 // Environment is locked first.
1419 JMutexAutoLock envlock(m_env_mutex);
1420 JMutexAutoLock conlock(m_con_mutex);
1424 peer = m_con.GetPeer(peer_id);
1426 catch(con::PeerNotFoundException &e)
1428 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1429 <<peer_id<<" not found"<<std::endl;
1433 //u8 peer_ser_ver = peer->serialization_version;
1434 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1442 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1444 if(command == TOSERVER_INIT)
1446 // [0] u16 TOSERVER_INIT
1447 // [2] u8 SER_FMT_VER_HIGHEST
1448 // [3] u8[20] player_name
1453 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1454 <<peer->id<<std::endl;
1456 // First byte after command is maximum supported
1457 // serialization version
1458 u8 client_max = data[2];
1459 u8 our_max = SER_FMT_VER_HIGHEST;
1460 // Use the highest version supported by both
1461 u8 deployed = core::min_(client_max, our_max);
1462 // If it's lower than the lowest supported, give up.
1463 if(deployed < SER_FMT_VER_LOWEST)
1464 deployed = SER_FMT_VER_INVALID;
1466 //peer->serialization_version = deployed;
1467 getClient(peer->id)->pending_serialization_version = deployed;
1469 if(deployed == SER_FMT_VER_INVALID)
1471 derr_server<<DTIME<<"Server: Cannot negotiate "
1472 "serialization version with peer "
1473 <<peer_id<<std::endl;
1482 const u32 playername_size = 20;
1483 char playername[playername_size];
1484 for(u32 i=0; i<playername_size-1; i++)
1486 playername[i] = data[3+i];
1488 playername[playername_size-1] = 0;
1491 Player *player = emergePlayer(playername, "");
1492 //Player *player = m_env.getPlayer(peer_id);
1494 // If a client is already connected to the player, cancel
1495 if(player->peer_id != 0)
1497 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1498 <<" tried to connect to "
1499 "an already connected player (peer_id="
1500 <<player->peer_id<<")"<<std::endl;
1504 // Set client of player
1505 player->peer_id = peer_id;
1507 // Check if player doesn't exist
1509 throw con::InvalidIncomingDataException
1510 ("Server::ProcessData(): INIT: Player doesn't exist");
1512 /*// update name if it was supplied
1513 if(datasize >= 20+3)
1516 player->updateName((const char*)&data[3]);
1519 // Now answer with a TOCLIENT_INIT
1521 SharedBuffer<u8> reply(2+1+6);
1522 writeU16(&reply[0], TOCLIENT_INIT);
1523 writeU8(&reply[2], deployed);
1524 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1526 m_con.Send(peer_id, 0, reply, true);
1530 if(command == TOSERVER_INIT2)
1532 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1533 <<peer->id<<std::endl;
1536 getClient(peer->id)->serialization_version
1537 = getClient(peer->id)->pending_serialization_version;
1540 Send some initialization data
1543 // Send player info to all players
1546 // Send inventory to player
1547 SendInventory(peer->id);
1551 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1552 m_time_of_day.get());
1553 m_con.Send(peer->id, 0, data, true);
1556 // Send information about server to player in chat
1558 std::wostringstream os(std::ios_base::binary);
1561 os<<L"uptime="<<m_uptime.get();
1562 // Information about clients
1564 for(core::map<u16, RemoteClient*>::Iterator
1565 i = m_clients.getIterator();
1566 i.atEnd() == false; i++)
1568 // Get client and check that it is valid
1569 RemoteClient *client = i.getNode()->getValue();
1570 assert(client->peer_id == i.getNode()->getKey());
1571 if(client->serialization_version == SER_FMT_VER_INVALID)
1574 Player *player = m_env.getPlayer(client->peer_id);
1575 // Get name of player
1576 std::wstring name = L"unknown";
1578 name = narrow_to_wide(player->getName());
1579 // Add name to information string
1584 SendChatMessage(peer_id, os.str());
1587 // Send information about joining in chat
1589 std::wstring name = L"unknown";
1590 Player *player = m_env.getPlayer(peer_id);
1592 name = narrow_to_wide(player->getName());
1594 std::wstring message;
1597 message += L" joined game";
1598 BroadcastChatMessage(message);
1604 if(peer_ser_ver == SER_FMT_VER_INVALID)
1606 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1607 " serialization format invalid or not initialized."
1608 " Skipping incoming command="<<command<<std::endl;
1612 Player *player = m_env.getPlayer(peer_id);
1615 derr_server<<"Server::ProcessData(): Cancelling: "
1616 "No player for peer_id="<<peer_id
1620 if(command == TOSERVER_PLAYERPOS)
1622 if(datasize < 2+12+12+4+4)
1626 v3s32 ps = readV3S32(&data[start+2]);
1627 v3s32 ss = readV3S32(&data[start+2+12]);
1628 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1629 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1630 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1631 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1632 pitch = wrapDegrees(pitch);
1633 yaw = wrapDegrees(yaw);
1634 player->setPosition(position);
1635 player->setSpeed(speed);
1636 player->setPitch(pitch);
1637 player->setYaw(yaw);
1639 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1640 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1641 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1643 else if(command == TOSERVER_GOTBLOCKS)
1656 u16 count = data[2];
1657 for(u16 i=0; i<count; i++)
1659 if((s16)datasize < 2+1+(i+1)*6)
1660 throw con::InvalidIncomingDataException
1661 ("GOTBLOCKS length is too short");
1662 v3s16 p = readV3S16(&data[2+1+i*6]);
1663 /*dstream<<"Server: GOTBLOCKS ("
1664 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1665 RemoteClient *client = getClient(peer_id);
1666 client->GotBlock(p);
1669 else if(command == TOSERVER_DELETEDBLOCKS)
1682 u16 count = data[2];
1683 for(u16 i=0; i<count; i++)
1685 if((s16)datasize < 2+1+(i+1)*6)
1686 throw con::InvalidIncomingDataException
1687 ("DELETEDBLOCKS length is too short");
1688 v3s16 p = readV3S16(&data[2+1+i*6]);
1689 /*dstream<<"Server: DELETEDBLOCKS ("
1690 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1691 RemoteClient *client = getClient(peer_id);
1692 client->SetBlockNotSent(p);
1695 else if(command == TOSERVER_CLICK_OBJECT)
1702 [2] u8 button (0=left, 1=right)
1707 u8 button = readU8(&data[2]);
1709 p.X = readS16(&data[3]);
1710 p.Y = readS16(&data[5]);
1711 p.Z = readS16(&data[7]);
1712 s16 id = readS16(&data[9]);
1713 //u16 item_i = readU16(&data[11]);
1715 MapBlock *block = NULL;
1718 block = m_env.getMap().getBlockNoCreate(p);
1720 catch(InvalidPositionException &e)
1722 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1726 MapBlockObject *obj = block->getObject(id);
1730 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1734 //TODO: Check that object is reasonably close
1739 InventoryList *ilist = player->inventory.getList("main");
1740 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1743 // Skip if inventory has no free space
1744 if(ilist->getUsedSlots() == ilist->getSize())
1746 dout_server<<"Player inventory has no free space"<<std::endl;
1751 Create the inventory item
1753 InventoryItem *item = NULL;
1754 // If it is an item-object, take the item from it
1755 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1757 item = ((ItemObject*)obj)->createInventoryItem();
1759 // Else create an item of the object
1762 item = new MapBlockObjectItem
1763 (obj->getInventoryString());
1766 // Add to inventory and send inventory
1767 ilist->addItem(item);
1768 SendInventory(player->peer_id);
1771 // Remove from block
1772 block->removeObject(id);
1775 else if(command == TOSERVER_GROUND_ACTION)
1783 [3] v3s16 nodepos_undersurface
1784 [9] v3s16 nodepos_abovesurface
1789 2: stop digging (all parameters ignored)
1791 u8 action = readU8(&data[2]);
1793 p_under.X = readS16(&data[3]);
1794 p_under.Y = readS16(&data[5]);
1795 p_under.Z = readS16(&data[7]);
1797 p_over.X = readS16(&data[9]);
1798 p_over.Y = readS16(&data[11]);
1799 p_over.Z = readS16(&data[13]);
1800 u16 item_i = readU16(&data[15]);
1802 //TODO: Check that target is reasonably close
1810 NOTE: This can be used in the future to check if
1811 somebody is cheating, by checking the timing.
1818 else if(action == 2)
1821 RemoteClient *client = getClient(peer->id);
1822 JMutexAutoLock digmutex(client->m_dig_mutex);
1823 client->m_dig_tool_item = -1;
1828 3: Digging completed
1830 else if(action == 3)
1832 // Mandatory parameter; actually used for nothing
1833 core::map<v3s16, MapBlock*> modified_blocks;
1839 // Get material at position
1840 material = m_env.getMap().getNode(p_under).d;
1841 // If it's not diggable, do nothing
1842 if(content_diggable(material) == false)
1844 derr_server<<"Server: Not finishing digging: Node not diggable"
1849 catch(InvalidPositionException &e)
1851 derr_server<<"Server: Not finishing digging: Node not found"
1856 //TODO: Send to only other clients
1859 Send the removal to all other clients
1864 SharedBuffer<u8> reply(replysize);
1865 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1866 writeS16(&reply[2], p_under.X);
1867 writeS16(&reply[4], p_under.Y);
1868 writeS16(&reply[6], p_under.Z);
1870 for(core::map<u16, RemoteClient*>::Iterator
1871 i = m_clients.getIterator();
1872 i.atEnd() == false; i++)
1874 // Get client and check that it is valid
1875 RemoteClient *client = i.getNode()->getValue();
1876 assert(client->peer_id == i.getNode()->getKey());
1877 if(client->serialization_version == SER_FMT_VER_INVALID)
1880 // Don't send if it's the same one
1881 if(peer_id == client->peer_id)
1885 m_con.Send(client->peer_id, 0, reply, true);
1889 Update and send inventory
1892 if(g_settings.getBool("creative_mode") == false)
1897 InventoryList *mlist = player->inventory.getList("main");
1900 InventoryItem *item = mlist->getItem(item_i);
1901 if(item && (std::string)item->getName() == "ToolItem")
1903 ToolItem *titem = (ToolItem*)item;
1904 std::string toolname = titem->getToolName();
1906 // Get digging properties for material and tool
1907 DiggingProperties prop =
1908 getDiggingProperties(material, toolname);
1910 if(prop.diggable == false)
1912 derr_server<<"Server: WARNING: Player digged"
1913 <<" with impossible material + tool"
1914 <<" combination"<<std::endl;
1917 bool weared_out = titem->addWear(prop.wear);
1921 mlist->deleteItem(item_i);
1927 Add digged item to inventory
1929 InventoryItem *item = new MaterialItem(material, 1);
1930 player->inventory.addItem("main", item);
1935 SendInventory(player->peer_id);
1940 (this takes some time so it is done after the quick stuff)
1942 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1948 // Update water pressure around modification
1949 // This also adds it to m_flow_active_nodes if appropriate
1951 MapVoxelManipulator v(&m_env.getMap());
1952 v.m_disable_water_climb =
1953 g_settings.getBool("disable_water_climb");
1955 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1959 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1961 catch(ProcessingLimitException &e)
1963 dstream<<"Processing limit reached (1)"<<std::endl;
1966 v.blitBack(modified_blocks);
1972 else if(action == 1)
1975 InventoryList *ilist = player->inventory.getList("main");
1980 InventoryItem *item = ilist->getItem(item_i);
1982 // If there is no item, it is not possible to add it anywhere
1987 Handle material items
1989 if(std::string("MaterialItem") == item->getName())
1992 // Don't add a node if this is not a free space
1993 MapNode n2 = m_env.getMap().getNode(p_over);
1994 if(content_buildable_to(n2.d) == false)
1997 catch(InvalidPositionException &e)
1999 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2004 // Reset build time counter
2005 getClient(peer->id)->m_time_from_building.set(0.0);
2008 MaterialItem *mitem = (MaterialItem*)item;
2010 n.d = mitem->getMaterial();
2011 if(content_directional(n.d))
2012 n.dir = packDir(p_under - p_over);
2016 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2017 SharedBuffer<u8> reply(replysize);
2018 writeU16(&reply[0], TOCLIENT_ADDNODE);
2019 writeS16(&reply[2], p_over.X);
2020 writeS16(&reply[4], p_over.Y);
2021 writeS16(&reply[6], p_over.Z);
2022 n.serialize(&reply[8], peer_ser_ver);
2024 m_con.SendToAll(0, reply, true);
2029 InventoryList *ilist = player->inventory.getList("main");
2030 if(g_settings.getBool("creative_mode") == false && ilist)
2032 // Remove from inventory and send inventory
2033 if(mitem->getCount() == 1)
2034 ilist->deleteItem(item_i);
2038 SendInventory(peer_id);
2044 This takes some time so it is done after the quick stuff
2046 core::map<v3s16, MapBlock*> modified_blocks;
2047 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2053 InventoryList *ilist = player->inventory.getList("main");
2054 if(g_settings.getBool("creative_mode") == false && ilist)
2056 // Remove from inventory and send inventory
2057 if(mitem->getCount() == 1)
2058 ilist->deleteItem(item_i);
2062 SendInventory(peer_id);
2068 This takes some time so it is done after the quick stuff
2070 core::map<v3s16, MapBlock*> modified_blocks;
2071 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2074 Set the modified blocks unsent for all the clients
2077 //JMutexAutoLock lock2(m_con_mutex);
2079 for(core::map<u16, RemoteClient*>::Iterator
2080 i = m_clients.getIterator();
2081 i.atEnd() == false; i++)
2083 RemoteClient *client = i.getNode()->getValue();
2085 if(modified_blocks.size() > 0)
2087 // Remove block from sent history
2088 client->SetBlocksNotSent(modified_blocks);
2097 // Update water pressure around modification
2098 // This also adds it to m_flow_active_nodes if appropriate
2100 MapVoxelManipulator v(&m_env.getMap());
2101 v.m_disable_water_climb =
2102 g_settings.getBool("disable_water_climb");
2104 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2108 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2110 catch(ProcessingLimitException &e)
2112 dstream<<"Processing limit reached (1)"<<std::endl;
2115 v.blitBack(modified_blocks);
2122 v3s16 blockpos = getNodeBlockPos(p_over);
2124 MapBlock *block = NULL;
2127 block = m_env.getMap().getBlockNoCreate(blockpos);
2129 catch(InvalidPositionException &e)
2131 derr_server<<"Error while placing object: "
2132 "block not found"<<std::endl;
2136 v3s16 block_pos_i_on_map = block->getPosRelative();
2137 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2139 v3f pos = intToFloat(p_over);
2140 pos -= block_pos_f_on_map;
2142 /*dout_server<<"pos="
2143 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2146 MapBlockObject *obj = NULL;
2149 Handle block object items
2151 if(std::string("MBOItem") == item->getName())
2153 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2155 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2156 "inventorystring=\""
2157 <<oitem->getInventoryString()
2158 <<"\""<<std::endl;*/
2160 obj = oitem->createObject
2161 (pos, player->getYaw(), player->getPitch());
2168 dout_server<<"Placing a miscellaneous item on map"
2171 Create an ItemObject that contains the item.
2173 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2174 std::ostringstream os(std::ios_base::binary);
2175 item->serialize(os);
2176 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2177 iobj->setItemString(os.str());
2183 derr_server<<"WARNING: item resulted in NULL object, "
2184 <<"not placing onto map"
2189 block->addObject(obj);
2191 dout_server<<"Placed object"<<std::endl;
2193 InventoryList *ilist = player->inventory.getList("main");
2194 if(g_settings.getBool("creative_mode") == false && ilist)
2196 // Remove from inventory and send inventory
2197 ilist->deleteItem(item_i);
2199 SendInventory(peer_id);
2207 Catch invalid actions
2211 derr_server<<"WARNING: Server: Invalid action "
2212 <<action<<std::endl;
2216 else if(command == TOSERVER_RELEASE)
2225 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2228 else if(command == TOSERVER_SIGNTEXT)
2237 std::string datastring((char*)&data[2], datasize-2);
2238 std::istringstream is(datastring, std::ios_base::binary);
2241 is.read((char*)buf, 6);
2242 v3s16 blockpos = readV3S16(buf);
2243 is.read((char*)buf, 2);
2244 s16 id = readS16(buf);
2245 is.read((char*)buf, 2);
2246 u16 textlen = readU16(buf);
2248 for(u16 i=0; i<textlen; i++)
2250 is.read((char*)buf, 1);
2251 text += (char)buf[0];
2254 MapBlock *block = NULL;
2257 block = m_env.getMap().getBlockNoCreate(blockpos);
2259 catch(InvalidPositionException &e)
2261 derr_server<<"Error while setting sign text: "
2262 "block not found"<<std::endl;
2266 MapBlockObject *obj = block->getObject(id);
2269 derr_server<<"Error while setting sign text: "
2270 "object not found"<<std::endl;
2274 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2276 derr_server<<"Error while setting sign text: "
2277 "object is not a sign"<<std::endl;
2281 ((SignObject*)obj)->setText(text);
2283 obj->getBlock()->setChangedFlag();
2285 else if(command == TOSERVER_INVENTORY_ACTION)
2287 /*// Ignore inventory changes if in creative mode
2288 if(g_settings.getBool("creative_mode") == true)
2290 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2294 // Strip command and create a stream
2295 std::string datastring((char*)&data[2], datasize-2);
2296 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2297 std::istringstream is(datastring, std::ios_base::binary);
2299 InventoryAction *a = InventoryAction::deSerialize(is);
2303 Handle craftresult specially if not in creative mode
2305 bool disable_action = false;
2306 if(a->getType() == IACTION_MOVE
2307 && g_settings.getBool("creative_mode") == false)
2309 IMoveAction *ma = (IMoveAction*)a;
2310 // Don't allow moving anything to craftresult
2311 if(ma->to_name == "craftresult")
2314 disable_action = true;
2316 // When something is removed from craftresult
2317 if(ma->from_name == "craftresult")
2319 disable_action = true;
2320 // Remove stuff from craft
2321 InventoryList *clist = player->inventory.getList("craft");
2324 u16 count = ma->count;
2327 clist->decrementMaterials(count);
2330 // Feed action to player inventory
2331 a->apply(&player->inventory);
2334 // If something appeared in craftresult, throw it
2336 InventoryList *rlist = player->inventory.getList("craftresult");
2337 InventoryList *mlist = player->inventory.getList("main");
2338 if(rlist && mlist && rlist->getUsedSlots() == 1)
2340 InventoryItem *item1 = rlist->changeItem(0, NULL);
2341 mlist->addItem(item1);
2345 if(disable_action == false)
2347 // Feed action to player inventory
2348 a->apply(&player->inventory);
2353 SendInventory(player->peer_id);
2357 dstream<<"TOSERVER_INVENTORY_ACTION: "
2358 <<"InventoryAction::deSerialize() returned NULL"
2362 else if(command == TOSERVER_CHAT_MESSAGE)
2370 std::string datastring((char*)&data[2], datasize-2);
2371 std::istringstream is(datastring, std::ios_base::binary);
2374 is.read((char*)buf, 2);
2375 u16 len = readU16(buf);
2377 std::wstring message;
2378 for(u16 i=0; i<len; i++)
2380 is.read((char*)buf, 2);
2381 message += (wchar_t)readU16(buf);
2384 // Get player name of this client
2385 std::wstring name = narrow_to_wide(player->getName());
2387 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2389 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2392 Send the message to all other clients
2394 for(core::map<u16, RemoteClient*>::Iterator
2395 i = m_clients.getIterator();
2396 i.atEnd() == false; i++)
2398 // Get client and check that it is valid
2399 RemoteClient *client = i.getNode()->getValue();
2400 assert(client->peer_id == i.getNode()->getKey());
2401 if(client->serialization_version == SER_FMT_VER_INVALID)
2404 // Don't send if it's the same one
2405 if(peer_id == client->peer_id)
2408 SendChatMessage(client->peer_id, line);
2413 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2414 "unknown command "<<command<<std::endl;
2418 catch(SendFailedException &e)
2420 derr_server<<"Server::ProcessData(): SendFailedException: "
2426 /*void Server::Send(u16 peer_id, u16 channelnum,
2427 SharedBuffer<u8> data, bool reliable)
2429 JMutexAutoLock lock(m_con_mutex);
2430 m_con.Send(peer_id, channelnum, data, reliable);
2433 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2435 DSTACK(__FUNCTION_NAME);
2437 Create a packet with the block in the right format
2440 std::ostringstream os(std::ios_base::binary);
2441 block->serialize(os, ver);
2442 std::string s = os.str();
2443 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2445 u32 replysize = 8 + blockdata.getSize();
2446 SharedBuffer<u8> reply(replysize);
2447 v3s16 p = block->getPos();
2448 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2449 writeS16(&reply[2], p.X);
2450 writeS16(&reply[4], p.Y);
2451 writeS16(&reply[6], p.Z);
2452 memcpy(&reply[8], *blockdata, blockdata.getSize());
2454 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2455 <<": \tpacket size: "<<replysize<<std::endl;*/
2460 m_con.Send(peer_id, 1, reply, true);
2463 core::list<PlayerInfo> Server::getPlayerInfo()
2465 DSTACK(__FUNCTION_NAME);
2466 JMutexAutoLock envlock(m_env_mutex);
2467 JMutexAutoLock conlock(m_con_mutex);
2469 core::list<PlayerInfo> list;
2471 core::list<Player*> players = m_env.getPlayers();
2473 core::list<Player*>::Iterator i;
2474 for(i = players.begin();
2475 i != players.end(); i++)
2479 Player *player = *i;
2482 con::Peer *peer = m_con.GetPeer(player->peer_id);
2483 // Copy info from peer to info struct
2485 info.address = peer->address;
2486 info.avg_rtt = peer->avg_rtt;
2488 catch(con::PeerNotFoundException &e)
2490 // Set dummy peer info
2492 info.address = Address(0,0,0,0,0);
2496 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2497 info.position = player->getPosition();
2499 list.push_back(info);
2505 void Server::peerAdded(con::Peer *peer)
2507 DSTACK(__FUNCTION_NAME);
2508 dout_server<<"Server::peerAdded(): peer->id="
2509 <<peer->id<<std::endl;
2512 c.type = PEER_ADDED;
2513 c.peer_id = peer->id;
2515 m_peer_change_queue.push_back(c);
2518 void Server::deletingPeer(con::Peer *peer, bool timeout)
2520 DSTACK(__FUNCTION_NAME);
2521 dout_server<<"Server::deletingPeer(): peer->id="
2522 <<peer->id<<", timeout="<<timeout<<std::endl;
2525 c.type = PEER_REMOVED;
2526 c.peer_id = peer->id;
2527 c.timeout = timeout;
2528 m_peer_change_queue.push_back(c);
2531 void Server::SendObjectData(float dtime)
2533 DSTACK(__FUNCTION_NAME);
2535 core::map<v3s16, bool> stepped_blocks;
2537 for(core::map<u16, RemoteClient*>::Iterator
2538 i = m_clients.getIterator();
2539 i.atEnd() == false; i++)
2541 u16 peer_id = i.getNode()->getKey();
2542 RemoteClient *client = i.getNode()->getValue();
2543 assert(client->peer_id == peer_id);
2545 if(client->serialization_version == SER_FMT_VER_INVALID)
2548 client->SendObjectData(this, dtime, stepped_blocks);
2552 void Server::SendPlayerInfos()
2554 DSTACK(__FUNCTION_NAME);
2556 //JMutexAutoLock envlock(m_env_mutex);
2558 // Get connected players
2559 core::list<Player*> players = m_env.getPlayers(true);
2561 u32 player_count = players.getSize();
2562 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2564 SharedBuffer<u8> data(datasize);
2565 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2568 core::list<Player*>::Iterator i;
2569 for(i = players.begin();
2570 i != players.end(); i++)
2572 Player *player = *i;
2574 /*dstream<<"Server sending player info for player with "
2575 "peer_id="<<player->peer_id<<std::endl;*/
2577 writeU16(&data[start], player->peer_id);
2578 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2579 start += 2+PLAYERNAME_SIZE;
2582 //JMutexAutoLock conlock(m_con_mutex);
2585 m_con.SendToAll(0, data, true);
2603 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2609 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2615 enum ItemSpecType type;
2616 // Only other one of these is used
2622 items: a pointer to an array of 9 pointers to items
2623 specs: a pointer to an array of 9 ItemSpecs
2625 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2627 u16 items_min_x = 100;
2628 u16 items_max_x = 100;
2629 u16 items_min_y = 100;
2630 u16 items_max_y = 100;
2631 for(u16 y=0; y<3; y++)
2632 for(u16 x=0; x<3; x++)
2634 if(items[y*3 + x] == NULL)
2636 if(items_min_x == 100 || x < items_min_x)
2638 if(items_min_y == 100 || y < items_min_y)
2640 if(items_max_x == 100 || x > items_max_x)
2642 if(items_max_y == 100 || y > items_max_y)
2645 // No items at all, just return false
2646 if(items_min_x == 100)
2649 u16 items_w = items_max_x - items_min_x + 1;
2650 u16 items_h = items_max_y - items_min_y + 1;
2652 u16 specs_min_x = 100;
2653 u16 specs_max_x = 100;
2654 u16 specs_min_y = 100;
2655 u16 specs_max_y = 100;
2656 for(u16 y=0; y<3; y++)
2657 for(u16 x=0; x<3; x++)
2659 if(specs[y*3 + x].type == ITEM_NONE)
2661 if(specs_min_x == 100 || x < specs_min_x)
2663 if(specs_min_y == 100 || y < specs_min_y)
2665 if(specs_max_x == 100 || x > specs_max_x)
2667 if(specs_max_y == 100 || y > specs_max_y)
2670 // No specs at all, just return false
2671 if(specs_min_x == 100)
2674 u16 specs_w = specs_max_x - specs_min_x + 1;
2675 u16 specs_h = specs_max_y - specs_min_y + 1;
2678 if(items_w != specs_w || items_h != specs_h)
2681 for(u16 y=0; y<specs_h; y++)
2682 for(u16 x=0; x<specs_w; x++)
2684 u16 items_x = items_min_x + x;
2685 u16 items_y = items_min_y + y;
2686 u16 specs_x = specs_min_x + x;
2687 u16 specs_y = specs_min_y + y;
2688 InventoryItem *item = items[items_y * 3 + items_x];
2689 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2691 if(spec.type == ITEM_NONE)
2693 // Has to be no item
2699 // There should be an item
2703 std::string itemname = item->getName();
2705 if(spec.type == ITEM_MATERIAL)
2707 if(itemname != "MaterialItem")
2709 MaterialItem *mitem = (MaterialItem*)item;
2710 if(mitem->getMaterial() != spec.num)
2713 else if(spec.type == ITEM_CRAFT)
2715 if(itemname != "CraftItem")
2717 CraftItem *mitem = (CraftItem*)item;
2718 if(mitem->getSubName() != spec.name)
2721 else if(spec.type == ITEM_TOOL)
2723 // Not supported yet
2726 else if(spec.type == ITEM_MBO)
2728 // Not supported yet
2733 // Not supported yet
2741 void Server::SendInventory(u16 peer_id)
2743 DSTACK(__FUNCTION_NAME);
2745 Player* player = m_env.getPlayer(peer_id);
2748 Calculate crafting stuff
2750 if(g_settings.getBool("creative_mode") == false)
2752 InventoryList *clist = player->inventory.getList("craft");
2753 InventoryList *rlist = player->inventory.getList("craftresult");
2756 rlist->clearItems();
2760 InventoryItem *items[9];
2761 for(u16 i=0; i<9; i++)
2763 items[i] = clist->getItem(i);
2772 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2773 if(checkItemCombination(items, specs))
2775 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2784 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2785 if(checkItemCombination(items, specs))
2787 rlist->addItem(new CraftItem("Stick", 4));
2796 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2797 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2798 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2799 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2800 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2801 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2802 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2803 if(checkItemCombination(items, specs))
2805 rlist->addItem(new MapBlockObjectItem("Sign"));
2814 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2815 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2816 if(checkItemCombination(items, specs))
2818 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2827 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2828 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2829 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2830 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2831 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2832 if(checkItemCombination(items, specs))
2834 rlist->addItem(new ToolItem("WPick", 0));
2843 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2844 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2845 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2846 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2847 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2848 if(checkItemCombination(items, specs))
2850 rlist->addItem(new ToolItem("STPick", 0));
2859 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2860 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2861 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2862 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2863 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2864 if(checkItemCombination(items, specs))
2866 rlist->addItem(new ToolItem("MesePick", 0));
2871 } // if creative_mode == false
2877 std::ostringstream os;
2878 //os.imbue(std::locale("C"));
2880 player->inventory.serialize(os);
2882 std::string s = os.str();
2884 SharedBuffer<u8> data(s.size()+2);
2885 writeU16(&data[0], TOCLIENT_INVENTORY);
2886 memcpy(&data[2], s.c_str(), s.size());
2889 m_con.Send(peer_id, 0, data, true);
2892 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2894 DSTACK(__FUNCTION_NAME);
2896 std::ostringstream os(std::ios_base::binary);
2900 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2901 os.write((char*)buf, 2);
2904 writeU16(buf, message.size());
2905 os.write((char*)buf, 2);
2908 for(u32 i=0; i<message.size(); i++)
2912 os.write((char*)buf, 2);
2916 std::string s = os.str();
2917 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2919 m_con.Send(peer_id, 0, data, true);
2922 void Server::BroadcastChatMessage(const std::wstring &message)
2924 for(core::map<u16, RemoteClient*>::Iterator
2925 i = m_clients.getIterator();
2926 i.atEnd() == false; i++)
2928 // Get client and check that it is valid
2929 RemoteClient *client = i.getNode()->getValue();
2930 assert(client->peer_id == i.getNode()->getKey());
2931 if(client->serialization_version == SER_FMT_VER_INVALID)
2934 SendChatMessage(client->peer_id, message);
2938 void Server::SendBlocks(float dtime)
2940 DSTACK(__FUNCTION_NAME);
2942 JMutexAutoLock envlock(m_env_mutex);
2944 core::array<PrioritySortedBlockTransfer> queue;
2946 s32 total_sending = 0;
2948 for(core::map<u16, RemoteClient*>::Iterator
2949 i = m_clients.getIterator();
2950 i.atEnd() == false; i++)
2952 RemoteClient *client = i.getNode()->getValue();
2953 assert(client->peer_id == i.getNode()->getKey());
2955 total_sending += client->SendingCount();
2957 if(client->serialization_version == SER_FMT_VER_INVALID)
2960 client->GetNextBlocks(this, dtime, queue);
2964 // Lowest priority number comes first.
2965 // Lowest is most important.
2968 JMutexAutoLock conlock(m_con_mutex);
2970 for(u32 i=0; i<queue.size(); i++)
2972 //TODO: Calculate limit dynamically
2973 if(total_sending >= g_settings.getS32
2974 ("max_simultaneous_block_sends_server_total"))
2977 PrioritySortedBlockTransfer q = queue[i];
2979 MapBlock *block = NULL;
2982 block = m_env.getMap().getBlockNoCreate(q.pos);
2984 catch(InvalidPositionException &e)
2989 RemoteClient *client = getClient(q.peer_id);
2991 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2993 client->SentBlock(q.pos);
3000 RemoteClient* Server::getClient(u16 peer_id)
3002 DSTACK(__FUNCTION_NAME);
3003 //JMutexAutoLock lock(m_con_mutex);
3004 core::map<u16, RemoteClient*>::Node *n;
3005 n = m_clients.find(peer_id);
3006 // A client should exist for all peers
3008 return n->getValue();
3011 Player *Server::emergePlayer(const char *name, const char *password)
3014 Try to get an existing player
3016 Player *player = m_env.getPlayer(name);
3027 player = new ServerRemotePlayer();
3028 //player->peer_id = c.peer_id;
3029 player->peer_id = PEER_ID_INEXISTENT;
3030 player->updateName(name);
3036 dstream<<"Server: Finding spawn place for player \""
3037 <<player->getName()<<"\""<<std::endl;
3041 f32 groundheight = 0;
3042 // Try to find a good place a few times
3043 for(s32 i=0; i<100; i++)
3045 s32 range = 1 + i*4;
3046 // We're going to try to throw the player to this position
3047 nodepos = v2s16(-range/2 + (myrand()%range),
3048 -range/2 + (myrand()%range));
3049 v2s16 sectorpos = getNodeSectorPos(nodepos);
3051 m_env.getMap().emergeSector(sectorpos);
3052 // Get ground height at point
3053 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3054 // The sector should have been generated -> groundheight exists
3055 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3056 // Don't go underwater
3057 if(groundheight < WATER_LEVEL)
3059 // Don't go inside ground
3061 v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3062 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);
3063 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3064 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3069 }catch(InvalidPositionException &e)
3071 // Ignore invalid position
3074 // Found a good place
3079 // If no suitable place was not found, go above water at least.
3080 if(groundheight < WATER_LEVEL)
3081 groundheight = WATER_LEVEL;
3083 player->setPosition(intToFloat(v3s16(
3090 Add player to environment
3093 m_env.addPlayer(player);
3096 Add stuff to inventory
3099 if(g_settings.getBool("creative_mode"))
3101 // Give some good picks
3103 InventoryItem *item = new ToolItem("STPick", 0);
3104 void* r = player->inventory.addItem("main", item);
3108 InventoryItem *item = new ToolItem("MesePick", 0);
3109 void* r = player->inventory.addItem("main", item);
3116 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3119 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3120 player->inventory.addItem("main", item);
3123 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3125 // Skip some materials
3126 if(i == CONTENT_OCEAN || i == CONTENT_TORCH)
3129 InventoryItem *item = new MaterialItem(i, 1);
3130 player->inventory.addItem("main", item);
3134 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3135 void* r = player->inventory.addItem("main", item);
3142 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3143 void* r = player->inventory.addItem("main", item);
3147 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3148 void* r = player->inventory.addItem("main", item);
3152 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3153 void* r = player->inventory.addItem("main", item);
3157 InventoryItem *item = new CraftItem("Stick", 4);
3158 void* r = player->inventory.addItem("main", item);
3162 InventoryItem *item = new ToolItem("WPick", 32000);
3163 void* r = player->inventory.addItem("main", item);
3167 InventoryItem *item = new ToolItem("STPick", 32000);
3168 void* r = player->inventory.addItem("main", item);
3171 /*// Give some lights
3173 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3174 bool r = player->inventory.addItem("main", item);
3178 for(u16 i=0; i<4; i++)
3180 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3181 bool r = player->inventory.addItem("main", item);
3184 /*// Give some other stuff
3186 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3187 bool r = player->inventory.addItem("main", item);
3196 void Server::UpdateBlockWaterPressure(MapBlock *block,
3197 core::map<v3s16, MapBlock*> &modified_blocks)
3199 MapVoxelManipulator v(&m_env.getMap());
3200 v.m_disable_water_climb =
3201 g_settings.getBool("disable_water_climb");
3203 VoxelArea area(block->getPosRelative(),
3204 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3208 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3210 catch(ProcessingLimitException &e)
3212 dstream<<"Processing limit reached (1)"<<std::endl;
3215 v.blitBack(modified_blocks);
3218 void Server::handlePeerChange(PeerChange &c)
3220 JMutexAutoLock envlock(m_env_mutex);
3221 JMutexAutoLock conlock(m_con_mutex);
3223 if(c.type == PEER_ADDED)
3230 core::map<u16, RemoteClient*>::Node *n;
3231 n = m_clients.find(c.peer_id);
3232 // The client shouldn't already exist
3236 RemoteClient *client = new RemoteClient();
3237 client->peer_id = c.peer_id;
3238 m_clients.insert(client->peer_id, client);
3241 else if(c.type == PEER_REMOVED)
3248 core::map<u16, RemoteClient*>::Node *n;
3249 n = m_clients.find(c.peer_id);
3250 // The client should exist
3253 // Collect information about leaving in chat
3254 std::wstring message;
3256 std::wstring name = L"unknown";
3257 Player *player = m_env.getPlayer(c.peer_id);
3259 name = narrow_to_wide(player->getName());
3263 message += L" left game";
3265 message += L" (timed out)";
3270 m_env.removePlayer(c.peer_id);
3273 // Set player client disconnected
3275 Player *player = m_env.getPlayer(c.peer_id);
3276 player->peer_id = 0;
3280 delete m_clients[c.peer_id];
3281 m_clients.remove(c.peer_id);
3283 // Send player info to all remaining clients
3286 // Send leave chat message to all remaining clients
3287 BroadcastChatMessage(message);
3296 void Server::handlePeerChanges()
3298 while(m_peer_change_queue.size() > 0)
3300 PeerChange c = m_peer_change_queue.pop_front();
3302 dout_server<<"Server: Handling peer change: "
3303 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3306 handlePeerChange(c);