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);
332 // Camera position and direction
334 playerpos + v3f(0, BS+BS/2, 0);
335 v3f camera_dir = v3f(0,0,1);
336 camera_dir.rotateYZBy(player->getPitch());
337 camera_dir.rotateXZBy(player->getYaw());
340 Get the starting value of the block finder radius.
342 s16 last_nearest_unsent_d;
345 JMutexAutoLock lock(m_blocks_sent_mutex);
347 if(m_last_center != center)
349 m_nearest_unsent_d = 0;
350 m_last_center = center;
353 /*dstream<<"m_nearest_unsent_reset_timer="
354 <<m_nearest_unsent_reset_timer<<std::endl;*/
355 if(m_nearest_unsent_reset_timer > 5.0)
357 m_nearest_unsent_reset_timer = 0;
358 m_nearest_unsent_d = 0;
359 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
362 last_nearest_unsent_d = m_nearest_unsent_d;
364 d_start = m_nearest_unsent_d;
367 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
368 ("max_simultaneous_block_sends_per_client");
369 u16 maximum_simultaneous_block_sends =
370 maximum_simultaneous_block_sends_setting;
373 Check the time from last addNode/removeNode.
375 Decrease send rate if player is building stuff.
378 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
379 m_time_from_building.m_value += dtime;
380 /*if(m_time_from_building.m_value
381 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
382 if(m_time_from_building.m_value < g_settings.getFloat(
383 "full_block_send_enable_min_time_from_building"))
385 maximum_simultaneous_block_sends
386 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
390 u32 num_blocks_selected;
392 JMutexAutoLock lock(m_blocks_sending_mutex);
393 num_blocks_selected = m_blocks_sending.size();
397 next time d will be continued from the d from which the nearest
398 unsent block was found this time.
400 This is because not necessarily any of the blocks found this
401 time are actually sent.
403 s32 new_nearest_unsent_d = -1;
405 // Serialization version used
406 //u8 ser_version = serialization_version;
408 //bool has_incomplete_blocks = false;
410 s16 d_max = g_settings.getS16("max_block_send_distance");
411 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
413 //dstream<<"Starting from "<<d_start<<std::endl;
415 for(s16 d = d_start; d <= d_max; d++)
417 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
419 //if(has_incomplete_blocks == false)
421 JMutexAutoLock lock(m_blocks_sent_mutex);
423 If m_nearest_unsent_d was changed by the EmergeThread
424 (it can change it to 0 through SetBlockNotSent),
426 Else update m_nearest_unsent_d
428 if(m_nearest_unsent_d != last_nearest_unsent_d)
430 d = m_nearest_unsent_d;
431 last_nearest_unsent_d = m_nearest_unsent_d;
436 Get the border/face dot coordinates of a "d-radiused"
439 core::list<v3s16> list;
440 getFacePositions(list, d);
442 core::list<v3s16>::Iterator li;
443 for(li=list.begin(); li!=list.end(); li++)
445 v3s16 p = *li + center;
449 - Don't allow too many simultaneous transfers
450 - EXCEPT when the blocks are very close
452 Also, don't send blocks that are already flying.
455 u16 maximum_simultaneous_block_sends_now =
456 maximum_simultaneous_block_sends;
458 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
460 maximum_simultaneous_block_sends_now =
461 maximum_simultaneous_block_sends_setting;
465 JMutexAutoLock lock(m_blocks_sending_mutex);
467 // Limit is dynamically lowered when building
468 if(num_blocks_selected
469 >= maximum_simultaneous_block_sends_now)
471 /*dstream<<"Not sending more blocks. Queue full. "
472 <<m_blocks_sending.size()
477 if(m_blocks_sending.find(p) != NULL)
484 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
485 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
486 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
487 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
488 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
489 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
492 bool generate = d <= d_max_gen;
496 // Don't generate above player
502 // Limit the generating area vertically to 2/3
503 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
508 Don't draw if not in sight
511 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
517 Don't send already sent blocks
520 JMutexAutoLock lock(m_blocks_sent_mutex);
522 if(m_blocks_sent.find(p) != NULL)
529 Ignore block if it is not at ground surface
530 but don't ignore water surface blocks
532 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
533 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
534 f32 y = server->m_env.getMap().getGroundHeight(p2d);
535 // The sector might not exist yet, thus no heightmap
536 if(y > GROUNDHEIGHT_VALID_MINVALUE)
538 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
539 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
540 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
546 Check if map has this block
548 MapBlock *block = NULL;
551 block = server->m_env.getMap().getBlockNoCreate(p);
553 catch(InvalidPositionException &e)
557 bool surely_not_found_on_disk = false;
560 /*if(block->isIncomplete())
562 has_incomplete_blocks = true;
568 surely_not_found_on_disk = true;
573 If block has been marked to not exist on disk (dummy)
574 and generating new ones is not wanted, skip block.
576 if(generate == false && surely_not_found_on_disk == true)
583 Record the lowest d from which a a block has been
584 found being not sent and possibly to exist
586 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
588 new_nearest_unsent_d = d;
592 Add inexistent block to emerge queue.
594 if(block == NULL || surely_not_found_on_disk)
596 /*SharedPtr<JMutexAutoLock> lock
597 (m_num_blocks_in_emerge_queue.getLock());*/
599 //TODO: Get value from somewhere
600 // Allow only one block in emerge queue
601 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
603 // Add it to the emerge queue and trigger the thread
606 if(generate == false)
607 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
609 server->m_emerge_queue.addBlock(peer_id, p, flags);
610 server->m_emergethread.trigger();
621 PrioritySortedBlockTransfer q((float)d, p, peer_id);
625 num_blocks_selected += 1;
630 if(new_nearest_unsent_d != -1)
632 JMutexAutoLock lock(m_blocks_sent_mutex);
633 m_nearest_unsent_d = new_nearest_unsent_d;
637 void RemoteClient::SendObjectData(
640 core::map<v3s16, bool> &stepped_blocks
643 DSTACK(__FUNCTION_NAME);
645 // Can't send anything without knowing version
646 if(serialization_version == SER_FMT_VER_INVALID)
648 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
654 Send a TOCLIENT_OBJECTDATA packet.
658 u16 number of player positions
669 std::ostringstream os(std::ios_base::binary);
673 writeU16(buf, TOCLIENT_OBJECTDATA);
674 os.write((char*)buf, 2);
677 Get and write player data
680 // Get connected players
681 core::list<Player*> players = server->m_env.getPlayers(true);
683 // Write player count
684 u16 playercount = players.size();
685 writeU16(buf, playercount);
686 os.write((char*)buf, 2);
688 core::list<Player*>::Iterator i;
689 for(i = players.begin();
690 i != players.end(); i++)
694 v3f pf = player->getPosition();
695 v3f sf = player->getSpeed();
697 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
698 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
699 s32 pitch_i (player->getPitch() * 100);
700 s32 yaw_i (player->getYaw() * 100);
702 writeU16(buf, player->peer_id);
703 os.write((char*)buf, 2);
704 writeV3S32(buf, position_i);
705 os.write((char*)buf, 12);
706 writeV3S32(buf, speed_i);
707 os.write((char*)buf, 12);
708 writeS32(buf, pitch_i);
709 os.write((char*)buf, 4);
710 writeS32(buf, yaw_i);
711 os.write((char*)buf, 4);
715 Get and write object data
721 For making players to be able to build to their nearby
722 environment (building is not possible on blocks that are not
725 - Add blocks to emerge queue if they are not found
727 SUGGESTION: These could be ignored from the backside of the player
730 Player *player = server->m_env.getPlayer(peer_id);
734 v3f playerpos = player->getPosition();
735 v3f playerspeed = player->getSpeed();
737 v3s16 center_nodepos = floatToInt(playerpos);
738 v3s16 center = getNodeBlockPos(center_nodepos);
740 s16 d_max = g_settings.getS16("active_object_range");
742 // Number of blocks whose objects were written to bos
745 std::ostringstream bos(std::ios_base::binary);
747 for(s16 d = 0; d <= d_max; d++)
749 core::list<v3s16> list;
750 getFacePositions(list, d);
752 core::list<v3s16>::Iterator li;
753 for(li=list.begin(); li!=list.end(); li++)
755 v3s16 p = *li + center;
758 Ignore blocks that haven't been sent to the client
761 JMutexAutoLock sentlock(m_blocks_sent_mutex);
762 if(m_blocks_sent.find(p) == NULL)
766 // Try stepping block and add it to a send queue
771 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
774 Step block if not in stepped_blocks and add to stepped_blocks.
776 if(stepped_blocks.find(p) == NULL)
778 block->stepObjects(dtime, true, server->getDayNightRatio());
779 stepped_blocks.insert(p, true);
780 block->setChangedFlag();
783 // Skip block if there are no objects
784 if(block->getObjectCount() == 0)
793 bos.write((char*)buf, 6);
796 block->serializeObjects(bos, serialization_version);
801 Stop collecting objects if data is already too big
803 // Sum of player and object data sizes
804 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
805 // break out if data too big
806 if(sum > MAX_OBJECTDATA_SIZE)
808 goto skip_subsequent;
812 catch(InvalidPositionException &e)
815 // Add it to the emerge queue and trigger the thread.
816 // Fetch the block only if it is on disk.
818 // Grab and increment counter
819 /*SharedPtr<JMutexAutoLock> lock
820 (m_num_blocks_in_emerge_queue.getLock());
821 m_num_blocks_in_emerge_queue.m_value++;*/
823 // Add to queue as an anonymous fetch from disk
824 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
825 server->m_emerge_queue.addBlock(0, p, flags);
826 server->m_emergethread.trigger();
834 writeU16(buf, blockcount);
835 os.write((char*)buf, 2);
837 // Write block objects
844 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
847 std::string s = os.str();
848 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
849 // Send as unreliable
850 server->m_con.Send(peer_id, 0, data, false);
853 void RemoteClient::GotBlock(v3s16 p)
855 JMutexAutoLock lock(m_blocks_sending_mutex);
856 JMutexAutoLock lock2(m_blocks_sent_mutex);
857 if(m_blocks_sending.find(p) != NULL)
858 m_blocks_sending.remove(p);
861 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
862 " m_blocks_sending"<<std::endl;*/
863 m_excess_gotblocks++;
865 m_blocks_sent.insert(p, true);
868 void RemoteClient::SentBlock(v3s16 p)
870 JMutexAutoLock lock(m_blocks_sending_mutex);
871 if(m_blocks_sending.size() > 15)
873 dstream<<"RemoteClient::SentBlock(): "
874 <<"m_blocks_sending.size()="
875 <<m_blocks_sending.size()<<std::endl;
877 if(m_blocks_sending.find(p) == NULL)
878 m_blocks_sending.insert(p, 0.0);
880 dstream<<"RemoteClient::SentBlock(): Sent block"
881 " already in m_blocks_sending"<<std::endl;
884 void RemoteClient::SetBlockNotSent(v3s16 p)
886 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
887 JMutexAutoLock sentlock(m_blocks_sent_mutex);
889 m_nearest_unsent_d = 0;
891 if(m_blocks_sending.find(p) != NULL)
892 m_blocks_sending.remove(p);
893 if(m_blocks_sent.find(p) != NULL)
894 m_blocks_sent.remove(p);
897 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
899 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
900 JMutexAutoLock sentlock(m_blocks_sent_mutex);
902 m_nearest_unsent_d = 0;
904 for(core::map<v3s16, MapBlock*>::Iterator
905 i = blocks.getIterator();
906 i.atEnd()==false; i++)
908 v3s16 p = i.getNode()->getKey();
910 if(m_blocks_sending.find(p) != NULL)
911 m_blocks_sending.remove(p);
912 if(m_blocks_sent.find(p) != NULL)
913 m_blocks_sent.remove(p);
921 PlayerInfo::PlayerInfo()
926 void PlayerInfo::PrintLine(std::ostream *s)
929 (*s)<<"\""<<name<<"\" ("
930 <<position.X<<","<<position.Y
931 <<","<<position.Z<<") ";
933 (*s)<<" avg_rtt="<<avg_rtt;
937 u32 PIChecksum(core::list<PlayerInfo> &l)
939 core::list<PlayerInfo>::Iterator i;
942 for(i=l.begin(); i!=l.end(); i++)
944 checksum += a * (i->id+1);
945 checksum ^= 0x435aafcd;
956 std::string mapsavedir,
960 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
961 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
963 m_emergethread(this),
966 m_time_of_day_send_timer(0),
969 m_flowwater_timer = 0.0;
970 m_print_info_timer = 0.0;
971 m_objectdata_timer = 0.0;
972 m_emergethread_trigger_timer = 0.0;
973 m_savemap_timer = 0.0;
977 m_step_dtime_mutex.Init();
986 JMutexAutoLock clientslock(m_con_mutex);
988 for(core::map<u16, RemoteClient*>::Iterator
989 i = m_clients.getIterator();
990 i.atEnd() == false; i++)
993 // NOTE: These are removed by env destructor
995 u16 peer_id = i.getNode()->getKey();
996 JMutexAutoLock envlock(m_env_mutex);
997 m_env.removePlayer(peer_id);
1001 delete i.getNode()->getValue();
1005 void Server::start(unsigned short port)
1007 DSTACK(__FUNCTION_NAME);
1008 // Stop thread if already running
1011 // Initialize connection
1012 m_con.setTimeoutMs(30);
1016 m_thread.setRun(true);
1019 dout_server<<"Server started on port "<<port<<std::endl;
1024 DSTACK(__FUNCTION_NAME);
1025 // Stop threads (set run=false first so both start stopping)
1026 m_thread.setRun(false);
1027 m_emergethread.setRun(false);
1029 m_emergethread.stop();
1031 dout_server<<"Server threads stopped"<<std::endl;
1034 void Server::step(float dtime)
1036 DSTACK(__FUNCTION_NAME);
1041 JMutexAutoLock lock(m_step_dtime_mutex);
1042 m_step_dtime += dtime;
1046 void Server::AsyncRunStep()
1048 DSTACK(__FUNCTION_NAME);
1052 JMutexAutoLock lock1(m_step_dtime_mutex);
1053 dtime = m_step_dtime;
1056 // Send blocks to clients
1062 //dstream<<"Server steps "<<dtime<<std::endl;
1063 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1066 JMutexAutoLock lock1(m_step_dtime_mutex);
1067 m_step_dtime -= dtime;
1074 m_uptime.set(m_uptime.get() + dtime);
1078 Update m_time_of_day
1081 m_time_counter += dtime;
1082 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1083 u32 units = (u32)(m_time_counter*speed);
1084 m_time_counter -= (f32)units / speed;
1085 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1087 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1090 Send to clients at constant intervals
1093 m_time_of_day_send_timer -= dtime;
1094 if(m_time_of_day_send_timer < 0.0)
1096 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1098 //JMutexAutoLock envlock(m_env_mutex);
1099 JMutexAutoLock conlock(m_con_mutex);
1101 for(core::map<u16, RemoteClient*>::Iterator
1102 i = m_clients.getIterator();
1103 i.atEnd() == false; i++)
1105 RemoteClient *client = i.getNode()->getValue();
1106 //Player *player = m_env.getPlayer(client->peer_id);
1108 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1109 m_time_of_day.get());
1111 m_con.Send(client->peer_id, 0, data, true);
1117 // Process connection's timeouts
1118 JMutexAutoLock lock2(m_con_mutex);
1119 m_con.RunTimeouts(dtime);
1123 // This has to be called so that the client list gets synced
1124 // with the peer list of the connection
1125 handlePeerChanges();
1130 // This also runs Map's timers
1131 JMutexAutoLock lock(m_env_mutex);
1142 if(g_settings.getBool("water_moves") == true)
1146 if(g_settings.getBool("endless_water") == false)
1151 float &counter = m_flowwater_timer;
1153 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1158 core::map<v3s16, MapBlock*> modified_blocks;
1162 JMutexAutoLock envlock(m_env_mutex);
1164 MapVoxelManipulator v(&m_env.getMap());
1165 v.m_disable_water_climb =
1166 g_settings.getBool("disable_water_climb");
1168 if(g_settings.getBool("endless_water") == false)
1169 v.flowWater(m_flow_active_nodes, 0, false, 250);
1171 v.flowWater(m_flow_active_nodes, 0, false, 50);
1173 v.blitBack(modified_blocks);
1175 ServerMap &map = ((ServerMap&)m_env.getMap());
1178 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1179 map.updateLighting(modified_blocks, lighting_modified_blocks);
1181 // Add blocks modified by lighting to modified_blocks
1182 for(core::map<v3s16, MapBlock*>::Iterator
1183 i = lighting_modified_blocks.getIterator();
1184 i.atEnd() == false; i++)
1186 MapBlock *block = i.getNode()->getValue();
1187 modified_blocks.insert(block->getPos(), block);
1192 Set the modified blocks unsent for all the clients
1195 JMutexAutoLock lock2(m_con_mutex);
1197 for(core::map<u16, RemoteClient*>::Iterator
1198 i = m_clients.getIterator();
1199 i.atEnd() == false; i++)
1201 RemoteClient *client = i.getNode()->getValue();
1203 if(modified_blocks.size() > 0)
1205 // Remove block from sent history
1206 client->SetBlocksNotSent(modified_blocks);
1210 } // interval counter
1213 // Periodically print some info
1215 float &counter = m_print_info_timer;
1221 JMutexAutoLock lock2(m_con_mutex);
1223 for(core::map<u16, RemoteClient*>::Iterator
1224 i = m_clients.getIterator();
1225 i.atEnd() == false; i++)
1227 //u16 peer_id = i.getNode()->getKey();
1228 RemoteClient *client = i.getNode()->getValue();
1229 client->PrintInfo(std::cout);
1237 NOTE: Some of this could be moved to RemoteClient
1241 JMutexAutoLock envlock(m_env_mutex);
1242 JMutexAutoLock conlock(m_con_mutex);
1244 for(core::map<u16, RemoteClient*>::Iterator
1245 i = m_clients.getIterator();
1246 i.atEnd() == false; i++)
1248 RemoteClient *client = i.getNode()->getValue();
1249 Player *player = m_env.getPlayer(client->peer_id);
1251 JMutexAutoLock digmutex(client->m_dig_mutex);
1253 if(client->m_dig_tool_item == -1)
1256 client->m_dig_time_remaining -= dtime;
1258 if(client->m_dig_time_remaining > 0)
1260 client->m_time_from_building.set(0.0);
1264 v3s16 p_under = client->m_dig_position;
1266 // Mandatory parameter; actually used for nothing
1267 core::map<v3s16, MapBlock*> modified_blocks;
1273 // Get material at position
1274 material = m_env.getMap().getNode(p_under).d;
1275 // If it's not diggable, do nothing
1276 if(content_diggable(material) == false)
1278 derr_server<<"Server: Not finishing digging: Node not diggable"
1280 client->m_dig_tool_item = -1;
1284 catch(InvalidPositionException &e)
1286 derr_server<<"Server: Not finishing digging: Node not found"
1288 client->m_dig_tool_item = -1;
1294 SharedBuffer<u8> reply(replysize);
1295 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1296 writeS16(&reply[2], p_under.X);
1297 writeS16(&reply[4], p_under.Y);
1298 writeS16(&reply[6], p_under.Z);
1300 m_con.SendToAll(0, reply, true);
1302 if(g_settings.getBool("creative_mode") == false)
1304 // Add to inventory and send inventory
1305 InventoryItem *item = new MaterialItem(material, 1);
1306 player->inventory.addItem("main", item);
1307 SendInventory(player->peer_id);
1312 (this takes some time so it is done after the quick stuff)
1314 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1320 // Update water pressure around modification
1321 // This also adds it to m_flow_active_nodes if appropriate
1323 MapVoxelManipulator v(&m_env.getMap());
1324 v.m_disable_water_climb =
1325 g_settings.getBool("disable_water_climb");
1327 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1331 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1333 catch(ProcessingLimitException &e)
1335 dstream<<"Processing limit reached (1)"<<std::endl;
1338 v.blitBack(modified_blocks);
1343 // Send object positions
1345 float &counter = m_objectdata_timer;
1347 if(counter >= g_settings.getFloat("objectdata_interval"))
1349 JMutexAutoLock lock1(m_env_mutex);
1350 JMutexAutoLock lock2(m_con_mutex);
1351 SendObjectData(counter);
1357 // Trigger emergethread (it gets somehow gets to a
1358 // non-triggered but bysy state sometimes)
1360 float &counter = m_emergethread_trigger_timer;
1366 m_emergethread.trigger();
1372 float &counter = m_savemap_timer;
1374 if(counter >= g_settings.getFloat("server_map_save_interval"))
1378 JMutexAutoLock lock(m_env_mutex);
1380 // Save only changed parts
1381 m_env.getMap().save(true);
1383 // Delete unused sectors
1384 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1385 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1386 if(deleted_count > 0)
1388 dout_server<<"Server: Unloaded "<<deleted_count
1389 <<" sectors from memory"<<std::endl;
1395 void Server::Receive()
1397 DSTACK(__FUNCTION_NAME);
1398 u32 data_maxsize = 10000;
1399 Buffer<u8> data(data_maxsize);
1404 JMutexAutoLock conlock(m_con_mutex);
1405 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1408 // This has to be called so that the client list gets synced
1409 // with the peer list of the connection
1410 handlePeerChanges();
1412 ProcessData(*data, datasize, peer_id);
1414 catch(con::InvalidIncomingDataException &e)
1416 derr_server<<"Server::Receive(): "
1417 "InvalidIncomingDataException: what()="
1418 <<e.what()<<std::endl;
1420 catch(con::PeerNotFoundException &e)
1422 //NOTE: This is not needed anymore
1424 // The peer has been disconnected.
1425 // Find the associated player and remove it.
1427 /*JMutexAutoLock envlock(m_env_mutex);
1429 dout_server<<"ServerThread: peer_id="<<peer_id
1430 <<" has apparently closed connection. "
1431 <<"Removing player."<<std::endl;
1433 m_env.removePlayer(peer_id);*/
1437 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1439 DSTACK(__FUNCTION_NAME);
1440 // Environment is locked first.
1441 JMutexAutoLock envlock(m_env_mutex);
1442 JMutexAutoLock conlock(m_con_mutex);
1446 peer = m_con.GetPeer(peer_id);
1448 catch(con::PeerNotFoundException &e)
1450 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1451 <<peer_id<<" not found"<<std::endl;
1455 //u8 peer_ser_ver = peer->serialization_version;
1456 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1464 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1466 if(command == TOSERVER_INIT)
1468 // [0] u16 TOSERVER_INIT
1469 // [2] u8 SER_FMT_VER_HIGHEST
1470 // [3] u8[20] player_name
1475 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1476 <<peer->id<<std::endl;
1478 // First byte after command is maximum supported
1479 // serialization version
1480 u8 client_max = data[2];
1481 u8 our_max = SER_FMT_VER_HIGHEST;
1482 // Use the highest version supported by both
1483 u8 deployed = core::min_(client_max, our_max);
1484 // If it's lower than the lowest supported, give up.
1485 if(deployed < SER_FMT_VER_LOWEST)
1486 deployed = SER_FMT_VER_INVALID;
1488 //peer->serialization_version = deployed;
1489 getClient(peer->id)->pending_serialization_version = deployed;
1491 if(deployed == SER_FMT_VER_INVALID)
1493 derr_server<<DTIME<<"Server: Cannot negotiate "
1494 "serialization version with peer "
1495 <<peer_id<<std::endl;
1504 const u32 playername_size = 20;
1505 char playername[playername_size];
1506 for(u32 i=0; i<playername_size-1; i++)
1508 playername[i] = data[3+i];
1510 playername[playername_size-1] = 0;
1513 Player *player = emergePlayer(playername, "");
1514 //Player *player = m_env.getPlayer(peer_id);
1516 // If a client is already connected to the player, cancel
1517 if(player->peer_id != 0)
1519 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1520 <<" tried to connect to "
1521 "an already connected player (peer_id="
1522 <<player->peer_id<<")"<<std::endl;
1526 // Set client of player
1527 player->peer_id = peer_id;
1529 // Check if player doesn't exist
1531 throw con::InvalidIncomingDataException
1532 ("Server::ProcessData(): INIT: Player doesn't exist");
1534 /*// update name if it was supplied
1535 if(datasize >= 20+3)
1538 player->updateName((const char*)&data[3]);
1541 // Now answer with a TOCLIENT_INIT
1543 SharedBuffer<u8> reply(2+1+6);
1544 writeU16(&reply[0], TOCLIENT_INIT);
1545 writeU8(&reply[2], deployed);
1546 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1548 m_con.Send(peer_id, 0, reply, true);
1552 if(command == TOSERVER_INIT2)
1554 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1555 <<peer->id<<std::endl;
1558 getClient(peer->id)->serialization_version
1559 = getClient(peer->id)->pending_serialization_version;
1562 Send some initialization data
1565 // Send player info to all players
1568 // Send inventory to player
1569 SendInventory(peer->id);
1573 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1574 m_time_of_day.get());
1575 m_con.Send(peer->id, 0, data, true);
1578 // Send information about server to player in chat
1580 std::wostringstream os(std::ios_base::binary);
1583 os<<L"uptime="<<m_uptime.get();
1584 // Information about clients
1586 for(core::map<u16, RemoteClient*>::Iterator
1587 i = m_clients.getIterator();
1588 i.atEnd() == false; i++)
1590 // Get client and check that it is valid
1591 RemoteClient *client = i.getNode()->getValue();
1592 assert(client->peer_id == i.getNode()->getKey());
1593 if(client->serialization_version == SER_FMT_VER_INVALID)
1596 Player *player = m_env.getPlayer(client->peer_id);
1597 // Get name of player
1598 std::wstring name = L"unknown";
1600 name = narrow_to_wide(player->getName());
1601 // Add name to information string
1606 SendChatMessage(peer_id, os.str());
1609 // Send information about joining in chat
1611 std::wstring name = L"unknown";
1612 Player *player = m_env.getPlayer(peer_id);
1614 name = narrow_to_wide(player->getName());
1616 std::wstring message;
1619 message += L" joined game";
1620 BroadcastChatMessage(message);
1626 if(peer_ser_ver == SER_FMT_VER_INVALID)
1628 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1629 " serialization format invalid or not initialized."
1630 " Skipping incoming command="<<command<<std::endl;
1634 Player *player = m_env.getPlayer(peer_id);
1637 derr_server<<"Server::ProcessData(): Cancelling: "
1638 "No player for peer_id="<<peer_id
1642 if(command == TOSERVER_PLAYERPOS)
1644 if(datasize < 2+12+12+4+4)
1648 v3s32 ps = readV3S32(&data[start+2]);
1649 v3s32 ss = readV3S32(&data[start+2+12]);
1650 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1651 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1652 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1653 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1654 pitch = wrapDegrees(pitch);
1655 yaw = wrapDegrees(yaw);
1656 player->setPosition(position);
1657 player->setSpeed(speed);
1658 player->setPitch(pitch);
1659 player->setYaw(yaw);
1661 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1662 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1663 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1665 else if(command == TOSERVER_GOTBLOCKS)
1678 u16 count = data[2];
1679 for(u16 i=0; i<count; i++)
1681 if((s16)datasize < 2+1+(i+1)*6)
1682 throw con::InvalidIncomingDataException
1683 ("GOTBLOCKS length is too short");
1684 v3s16 p = readV3S16(&data[2+1+i*6]);
1685 /*dstream<<"Server: GOTBLOCKS ("
1686 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1687 RemoteClient *client = getClient(peer_id);
1688 client->GotBlock(p);
1691 else if(command == TOSERVER_DELETEDBLOCKS)
1704 u16 count = data[2];
1705 for(u16 i=0; i<count; i++)
1707 if((s16)datasize < 2+1+(i+1)*6)
1708 throw con::InvalidIncomingDataException
1709 ("DELETEDBLOCKS length is too short");
1710 v3s16 p = readV3S16(&data[2+1+i*6]);
1711 /*dstream<<"Server: DELETEDBLOCKS ("
1712 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1713 RemoteClient *client = getClient(peer_id);
1714 client->SetBlockNotSent(p);
1717 else if(command == TOSERVER_CLICK_OBJECT)
1724 [2] u8 button (0=left, 1=right)
1729 u8 button = readU8(&data[2]);
1731 p.X = readS16(&data[3]);
1732 p.Y = readS16(&data[5]);
1733 p.Z = readS16(&data[7]);
1734 s16 id = readS16(&data[9]);
1735 //u16 item_i = readU16(&data[11]);
1737 MapBlock *block = NULL;
1740 block = m_env.getMap().getBlockNoCreate(p);
1742 catch(InvalidPositionException &e)
1744 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1748 MapBlockObject *obj = block->getObject(id);
1752 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1756 //TODO: Check that object is reasonably close
1761 InventoryList *ilist = player->inventory.getList("main");
1762 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1765 // Skip if inventory has no free space
1766 if(ilist->getUsedSlots() == ilist->getSize())
1768 dout_server<<"Player inventory has no free space"<<std::endl;
1773 Create the inventory item
1775 InventoryItem *item = NULL;
1776 // If it is an item-object, take the item from it
1777 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1779 item = ((ItemObject*)obj)->createInventoryItem();
1781 // Else create an item of the object
1784 item = new MapBlockObjectItem
1785 (obj->getInventoryString());
1788 // Add to inventory and send inventory
1789 ilist->addItem(item);
1790 SendInventory(player->peer_id);
1793 // Remove from block
1794 block->removeObject(id);
1797 else if(command == TOSERVER_GROUND_ACTION)
1805 [3] v3s16 nodepos_undersurface
1806 [9] v3s16 nodepos_abovesurface
1811 2: stop digging (all parameters ignored)
1813 u8 action = readU8(&data[2]);
1815 p_under.X = readS16(&data[3]);
1816 p_under.Y = readS16(&data[5]);
1817 p_under.Z = readS16(&data[7]);
1819 p_over.X = readS16(&data[9]);
1820 p_over.Y = readS16(&data[11]);
1821 p_over.Z = readS16(&data[13]);
1822 u16 item_i = readU16(&data[15]);
1824 //TODO: Check that target is reasonably close
1832 NOTE: This can be used in the future to check if
1833 somebody is cheating, by checking the timing.
1840 else if(action == 2)
1843 RemoteClient *client = getClient(peer->id);
1844 JMutexAutoLock digmutex(client->m_dig_mutex);
1845 client->m_dig_tool_item = -1;
1850 3: Digging completed
1852 else if(action == 3)
1854 // Mandatory parameter; actually used for nothing
1855 core::map<v3s16, MapBlock*> modified_blocks;
1861 // Get material at position
1862 material = m_env.getMap().getNode(p_under).d;
1863 // If it's not diggable, do nothing
1864 if(content_diggable(material) == false)
1866 derr_server<<"Server: Not finishing digging: Node not diggable"
1871 catch(InvalidPositionException &e)
1873 derr_server<<"Server: Not finishing digging: Node not found"
1878 //TODO: Send to only other clients
1881 Send the removal to all other clients
1886 SharedBuffer<u8> reply(replysize);
1887 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1888 writeS16(&reply[2], p_under.X);
1889 writeS16(&reply[4], p_under.Y);
1890 writeS16(&reply[6], p_under.Z);
1892 for(core::map<u16, RemoteClient*>::Iterator
1893 i = m_clients.getIterator();
1894 i.atEnd() == false; i++)
1896 // Get client and check that it is valid
1897 RemoteClient *client = i.getNode()->getValue();
1898 assert(client->peer_id == i.getNode()->getKey());
1899 if(client->serialization_version == SER_FMT_VER_INVALID)
1902 // Don't send if it's the same one
1903 if(peer_id == client->peer_id)
1907 m_con.Send(client->peer_id, 0, reply, true);
1911 Update and send inventory
1914 if(g_settings.getBool("creative_mode") == false)
1919 InventoryList *mlist = player->inventory.getList("main");
1922 InventoryItem *item = mlist->getItem(item_i);
1923 if(item && (std::string)item->getName() == "ToolItem")
1925 ToolItem *titem = (ToolItem*)item;
1926 std::string toolname = titem->getToolName();
1928 // Get digging properties for material and tool
1929 DiggingProperties prop =
1930 getDiggingProperties(material, toolname);
1932 if(prop.diggable == false)
1934 derr_server<<"Server: WARNING: Player digged"
1935 <<" with impossible material + tool"
1936 <<" combination"<<std::endl;
1939 bool weared_out = titem->addWear(prop.wear);
1943 mlist->deleteItem(item_i);
1949 Add digged item to inventory
1951 InventoryItem *item = new MaterialItem(material, 1);
1952 player->inventory.addItem("main", item);
1957 SendInventory(player->peer_id);
1962 (this takes some time so it is done after the quick stuff)
1964 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1970 // Update water pressure around modification
1971 // This also adds it to m_flow_active_nodes if appropriate
1973 MapVoxelManipulator v(&m_env.getMap());
1974 v.m_disable_water_climb =
1975 g_settings.getBool("disable_water_climb");
1977 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1981 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1983 catch(ProcessingLimitException &e)
1985 dstream<<"Processing limit reached (1)"<<std::endl;
1988 v.blitBack(modified_blocks);
1994 else if(action == 1)
1997 InventoryList *ilist = player->inventory.getList("main");
2002 InventoryItem *item = ilist->getItem(item_i);
2004 // If there is no item, it is not possible to add it anywhere
2009 Handle material items
2011 if(std::string("MaterialItem") == item->getName())
2014 // Don't add a node if this is not a free space
2015 MapNode n2 = m_env.getMap().getNode(p_over);
2016 if(content_buildable_to(n2.d) == false)
2019 catch(InvalidPositionException &e)
2021 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2026 // Reset build time counter
2027 getClient(peer->id)->m_time_from_building.set(0.0);
2030 MaterialItem *mitem = (MaterialItem*)item;
2032 n.d = mitem->getMaterial();
2033 if(content_directional(n.d))
2034 n.dir = packDir(p_under - p_over);
2038 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2039 SharedBuffer<u8> reply(replysize);
2040 writeU16(&reply[0], TOCLIENT_ADDNODE);
2041 writeS16(&reply[2], p_over.X);
2042 writeS16(&reply[4], p_over.Y);
2043 writeS16(&reply[6], p_over.Z);
2044 n.serialize(&reply[8], peer_ser_ver);
2046 m_con.SendToAll(0, reply, true);
2051 InventoryList *ilist = player->inventory.getList("main");
2052 if(g_settings.getBool("creative_mode") == false && ilist)
2054 // Remove from inventory and send inventory
2055 if(mitem->getCount() == 1)
2056 ilist->deleteItem(item_i);
2060 SendInventory(peer_id);
2066 This takes some time so it is done after the quick stuff
2068 core::map<v3s16, MapBlock*> modified_blocks;
2069 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2075 InventoryList *ilist = player->inventory.getList("main");
2076 if(g_settings.getBool("creative_mode") == false && ilist)
2078 // Remove from inventory and send inventory
2079 if(mitem->getCount() == 1)
2080 ilist->deleteItem(item_i);
2084 SendInventory(peer_id);
2090 This takes some time so it is done after the quick stuff
2092 core::map<v3s16, MapBlock*> modified_blocks;
2093 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2096 Set the modified blocks unsent for all the clients
2099 //JMutexAutoLock lock2(m_con_mutex);
2101 for(core::map<u16, RemoteClient*>::Iterator
2102 i = m_clients.getIterator();
2103 i.atEnd() == false; i++)
2105 RemoteClient *client = i.getNode()->getValue();
2107 if(modified_blocks.size() > 0)
2109 // Remove block from sent history
2110 client->SetBlocksNotSent(modified_blocks);
2119 // Update water pressure around modification
2120 // This also adds it to m_flow_active_nodes if appropriate
2122 MapVoxelManipulator v(&m_env.getMap());
2123 v.m_disable_water_climb =
2124 g_settings.getBool("disable_water_climb");
2126 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2130 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2132 catch(ProcessingLimitException &e)
2134 dstream<<"Processing limit reached (1)"<<std::endl;
2137 v.blitBack(modified_blocks);
2144 v3s16 blockpos = getNodeBlockPos(p_over);
2146 MapBlock *block = NULL;
2149 block = m_env.getMap().getBlockNoCreate(blockpos);
2151 catch(InvalidPositionException &e)
2153 derr_server<<"Error while placing object: "
2154 "block not found"<<std::endl;
2158 v3s16 block_pos_i_on_map = block->getPosRelative();
2159 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2161 v3f pos = intToFloat(p_over);
2162 pos -= block_pos_f_on_map;
2164 /*dout_server<<"pos="
2165 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2168 MapBlockObject *obj = NULL;
2171 Handle block object items
2173 if(std::string("MBOItem") == item->getName())
2175 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2177 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2178 "inventorystring=\""
2179 <<oitem->getInventoryString()
2180 <<"\""<<std::endl;*/
2182 obj = oitem->createObject
2183 (pos, player->getYaw(), player->getPitch());
2190 dout_server<<"Placing a miscellaneous item on map"
2193 Create an ItemObject that contains the item.
2195 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2196 std::ostringstream os(std::ios_base::binary);
2197 item->serialize(os);
2198 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2199 iobj->setItemString(os.str());
2205 derr_server<<"WARNING: item resulted in NULL object, "
2206 <<"not placing onto map"
2211 block->addObject(obj);
2213 dout_server<<"Placed object"<<std::endl;
2215 InventoryList *ilist = player->inventory.getList("main");
2216 if(g_settings.getBool("creative_mode") == false && ilist)
2218 // Remove from inventory and send inventory
2219 ilist->deleteItem(item_i);
2221 SendInventory(peer_id);
2229 Catch invalid actions
2233 derr_server<<"WARNING: Server: Invalid action "
2234 <<action<<std::endl;
2238 else if(command == TOSERVER_RELEASE)
2247 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2250 else if(command == TOSERVER_SIGNTEXT)
2259 std::string datastring((char*)&data[2], datasize-2);
2260 std::istringstream is(datastring, std::ios_base::binary);
2263 is.read((char*)buf, 6);
2264 v3s16 blockpos = readV3S16(buf);
2265 is.read((char*)buf, 2);
2266 s16 id = readS16(buf);
2267 is.read((char*)buf, 2);
2268 u16 textlen = readU16(buf);
2270 for(u16 i=0; i<textlen; i++)
2272 is.read((char*)buf, 1);
2273 text += (char)buf[0];
2276 MapBlock *block = NULL;
2279 block = m_env.getMap().getBlockNoCreate(blockpos);
2281 catch(InvalidPositionException &e)
2283 derr_server<<"Error while setting sign text: "
2284 "block not found"<<std::endl;
2288 MapBlockObject *obj = block->getObject(id);
2291 derr_server<<"Error while setting sign text: "
2292 "object not found"<<std::endl;
2296 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2298 derr_server<<"Error while setting sign text: "
2299 "object is not a sign"<<std::endl;
2303 ((SignObject*)obj)->setText(text);
2305 obj->getBlock()->setChangedFlag();
2307 else if(command == TOSERVER_INVENTORY_ACTION)
2309 /*// Ignore inventory changes if in creative mode
2310 if(g_settings.getBool("creative_mode") == true)
2312 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2316 // Strip command and create a stream
2317 std::string datastring((char*)&data[2], datasize-2);
2318 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2319 std::istringstream is(datastring, std::ios_base::binary);
2321 InventoryAction *a = InventoryAction::deSerialize(is);
2325 Handle craftresult specially if not in creative mode
2327 bool disable_action = false;
2328 if(a->getType() == IACTION_MOVE
2329 && g_settings.getBool("creative_mode") == false)
2331 IMoveAction *ma = (IMoveAction*)a;
2332 // Don't allow moving anything to craftresult
2333 if(ma->to_name == "craftresult")
2336 disable_action = true;
2338 // When something is removed from craftresult
2339 if(ma->from_name == "craftresult")
2341 disable_action = true;
2342 // Remove stuff from craft
2343 InventoryList *clist = player->inventory.getList("craft");
2346 u16 count = ma->count;
2349 clist->decrementMaterials(count);
2352 // Feed action to player inventory
2353 a->apply(&player->inventory);
2356 // If something appeared in craftresult, throw it
2358 InventoryList *rlist = player->inventory.getList("craftresult");
2359 InventoryList *mlist = player->inventory.getList("main");
2360 if(rlist && mlist && rlist->getUsedSlots() == 1)
2362 InventoryItem *item1 = rlist->changeItem(0, NULL);
2363 mlist->addItem(item1);
2367 if(disable_action == false)
2369 // Feed action to player inventory
2370 a->apply(&player->inventory);
2375 SendInventory(player->peer_id);
2379 dstream<<"TOSERVER_INVENTORY_ACTION: "
2380 <<"InventoryAction::deSerialize() returned NULL"
2384 else if(command == TOSERVER_CHAT_MESSAGE)
2392 std::string datastring((char*)&data[2], datasize-2);
2393 std::istringstream is(datastring, std::ios_base::binary);
2396 is.read((char*)buf, 2);
2397 u16 len = readU16(buf);
2399 std::wstring message;
2400 for(u16 i=0; i<len; i++)
2402 is.read((char*)buf, 2);
2403 message += (wchar_t)readU16(buf);
2406 // Get player name of this client
2407 std::wstring name = narrow_to_wide(player->getName());
2409 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2411 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2414 Send the message to all other clients
2416 for(core::map<u16, RemoteClient*>::Iterator
2417 i = m_clients.getIterator();
2418 i.atEnd() == false; i++)
2420 // Get client and check that it is valid
2421 RemoteClient *client = i.getNode()->getValue();
2422 assert(client->peer_id == i.getNode()->getKey());
2423 if(client->serialization_version == SER_FMT_VER_INVALID)
2426 // Don't send if it's the same one
2427 if(peer_id == client->peer_id)
2430 SendChatMessage(client->peer_id, line);
2435 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2436 "unknown command "<<command<<std::endl;
2440 catch(SendFailedException &e)
2442 derr_server<<"Server::ProcessData(): SendFailedException: "
2448 /*void Server::Send(u16 peer_id, u16 channelnum,
2449 SharedBuffer<u8> data, bool reliable)
2451 JMutexAutoLock lock(m_con_mutex);
2452 m_con.Send(peer_id, channelnum, data, reliable);
2455 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2457 DSTACK(__FUNCTION_NAME);
2459 Create a packet with the block in the right format
2462 std::ostringstream os(std::ios_base::binary);
2463 block->serialize(os, ver);
2464 std::string s = os.str();
2465 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2467 u32 replysize = 8 + blockdata.getSize();
2468 SharedBuffer<u8> reply(replysize);
2469 v3s16 p = block->getPos();
2470 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2471 writeS16(&reply[2], p.X);
2472 writeS16(&reply[4], p.Y);
2473 writeS16(&reply[6], p.Z);
2474 memcpy(&reply[8], *blockdata, blockdata.getSize());
2476 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2477 <<": \tpacket size: "<<replysize<<std::endl;*/
2482 m_con.Send(peer_id, 1, reply, true);
2485 core::list<PlayerInfo> Server::getPlayerInfo()
2487 DSTACK(__FUNCTION_NAME);
2488 JMutexAutoLock envlock(m_env_mutex);
2489 JMutexAutoLock conlock(m_con_mutex);
2491 core::list<PlayerInfo> list;
2493 core::list<Player*> players = m_env.getPlayers();
2495 core::list<Player*>::Iterator i;
2496 for(i = players.begin();
2497 i != players.end(); i++)
2501 Player *player = *i;
2504 con::Peer *peer = m_con.GetPeer(player->peer_id);
2505 // Copy info from peer to info struct
2507 info.address = peer->address;
2508 info.avg_rtt = peer->avg_rtt;
2510 catch(con::PeerNotFoundException &e)
2512 // Set dummy peer info
2514 info.address = Address(0,0,0,0,0);
2518 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2519 info.position = player->getPosition();
2521 list.push_back(info);
2527 void Server::peerAdded(con::Peer *peer)
2529 DSTACK(__FUNCTION_NAME);
2530 dout_server<<"Server::peerAdded(): peer->id="
2531 <<peer->id<<std::endl;
2534 c.type = PEER_ADDED;
2535 c.peer_id = peer->id;
2537 m_peer_change_queue.push_back(c);
2540 void Server::deletingPeer(con::Peer *peer, bool timeout)
2542 DSTACK(__FUNCTION_NAME);
2543 dout_server<<"Server::deletingPeer(): peer->id="
2544 <<peer->id<<", timeout="<<timeout<<std::endl;
2547 c.type = PEER_REMOVED;
2548 c.peer_id = peer->id;
2549 c.timeout = timeout;
2550 m_peer_change_queue.push_back(c);
2553 void Server::SendObjectData(float dtime)
2555 DSTACK(__FUNCTION_NAME);
2557 core::map<v3s16, bool> stepped_blocks;
2559 for(core::map<u16, RemoteClient*>::Iterator
2560 i = m_clients.getIterator();
2561 i.atEnd() == false; i++)
2563 u16 peer_id = i.getNode()->getKey();
2564 RemoteClient *client = i.getNode()->getValue();
2565 assert(client->peer_id == peer_id);
2567 if(client->serialization_version == SER_FMT_VER_INVALID)
2570 client->SendObjectData(this, dtime, stepped_blocks);
2574 void Server::SendPlayerInfos()
2576 DSTACK(__FUNCTION_NAME);
2578 //JMutexAutoLock envlock(m_env_mutex);
2580 // Get connected players
2581 core::list<Player*> players = m_env.getPlayers(true);
2583 u32 player_count = players.getSize();
2584 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2586 SharedBuffer<u8> data(datasize);
2587 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2590 core::list<Player*>::Iterator i;
2591 for(i = players.begin();
2592 i != players.end(); i++)
2594 Player *player = *i;
2596 /*dstream<<"Server sending player info for player with "
2597 "peer_id="<<player->peer_id<<std::endl;*/
2599 writeU16(&data[start], player->peer_id);
2600 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2601 start += 2+PLAYERNAME_SIZE;
2604 //JMutexAutoLock conlock(m_con_mutex);
2607 m_con.SendToAll(0, data, true);
2625 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2631 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2637 enum ItemSpecType type;
2638 // Only other one of these is used
2644 items: a pointer to an array of 9 pointers to items
2645 specs: a pointer to an array of 9 ItemSpecs
2647 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2649 u16 items_min_x = 100;
2650 u16 items_max_x = 100;
2651 u16 items_min_y = 100;
2652 u16 items_max_y = 100;
2653 for(u16 y=0; y<3; y++)
2654 for(u16 x=0; x<3; x++)
2656 if(items[y*3 + x] == NULL)
2658 if(items_min_x == 100 || x < items_min_x)
2660 if(items_min_y == 100 || y < items_min_y)
2662 if(items_max_x == 100 || x > items_max_x)
2664 if(items_max_y == 100 || y > items_max_y)
2667 // No items at all, just return false
2668 if(items_min_x == 100)
2671 u16 items_w = items_max_x - items_min_x + 1;
2672 u16 items_h = items_max_y - items_min_y + 1;
2674 u16 specs_min_x = 100;
2675 u16 specs_max_x = 100;
2676 u16 specs_min_y = 100;
2677 u16 specs_max_y = 100;
2678 for(u16 y=0; y<3; y++)
2679 for(u16 x=0; x<3; x++)
2681 if(specs[y*3 + x].type == ITEM_NONE)
2683 if(specs_min_x == 100 || x < specs_min_x)
2685 if(specs_min_y == 100 || y < specs_min_y)
2687 if(specs_max_x == 100 || x > specs_max_x)
2689 if(specs_max_y == 100 || y > specs_max_y)
2692 // No specs at all, just return false
2693 if(specs_min_x == 100)
2696 u16 specs_w = specs_max_x - specs_min_x + 1;
2697 u16 specs_h = specs_max_y - specs_min_y + 1;
2700 if(items_w != specs_w || items_h != specs_h)
2703 for(u16 y=0; y<specs_h; y++)
2704 for(u16 x=0; x<specs_w; x++)
2706 u16 items_x = items_min_x + x;
2707 u16 items_y = items_min_y + y;
2708 u16 specs_x = specs_min_x + x;
2709 u16 specs_y = specs_min_y + y;
2710 InventoryItem *item = items[items_y * 3 + items_x];
2711 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2713 if(spec.type == ITEM_NONE)
2715 // Has to be no item
2721 // There should be an item
2725 std::string itemname = item->getName();
2727 if(spec.type == ITEM_MATERIAL)
2729 if(itemname != "MaterialItem")
2731 MaterialItem *mitem = (MaterialItem*)item;
2732 if(mitem->getMaterial() != spec.num)
2735 else if(spec.type == ITEM_CRAFT)
2737 if(itemname != "CraftItem")
2739 CraftItem *mitem = (CraftItem*)item;
2740 if(mitem->getSubName() != spec.name)
2743 else if(spec.type == ITEM_TOOL)
2745 // Not supported yet
2748 else if(spec.type == ITEM_MBO)
2750 // Not supported yet
2755 // Not supported yet
2763 void Server::SendInventory(u16 peer_id)
2765 DSTACK(__FUNCTION_NAME);
2767 Player* player = m_env.getPlayer(peer_id);
2770 Calculate crafting stuff
2772 if(g_settings.getBool("creative_mode") == false)
2774 InventoryList *clist = player->inventory.getList("craft");
2775 InventoryList *rlist = player->inventory.getList("craftresult");
2778 rlist->clearItems();
2782 InventoryItem *items[9];
2783 for(u16 i=0; i<9; i++)
2785 items[i] = clist->getItem(i);
2794 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2795 if(checkItemCombination(items, specs))
2797 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2806 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2807 if(checkItemCombination(items, specs))
2809 rlist->addItem(new CraftItem("Stick", 4));
2818 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2819 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2820 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2821 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2822 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2823 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2824 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2825 if(checkItemCombination(items, specs))
2827 rlist->addItem(new MapBlockObjectItem("Sign"));
2836 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2837 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2838 if(checkItemCombination(items, specs))
2840 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2849 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2850 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2851 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2852 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2853 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2854 if(checkItemCombination(items, specs))
2856 rlist->addItem(new ToolItem("WPick", 0));
2865 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2866 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2867 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2868 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2869 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2870 if(checkItemCombination(items, specs))
2872 rlist->addItem(new ToolItem("STPick", 0));
2881 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2882 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2883 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2884 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2885 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2886 if(checkItemCombination(items, specs))
2888 rlist->addItem(new ToolItem("MesePick", 0));
2893 } // if creative_mode == false
2899 std::ostringstream os;
2900 //os.imbue(std::locale("C"));
2902 player->inventory.serialize(os);
2904 std::string s = os.str();
2906 SharedBuffer<u8> data(s.size()+2);
2907 writeU16(&data[0], TOCLIENT_INVENTORY);
2908 memcpy(&data[2], s.c_str(), s.size());
2911 m_con.Send(peer_id, 0, data, true);
2914 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2916 DSTACK(__FUNCTION_NAME);
2918 std::ostringstream os(std::ios_base::binary);
2922 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2923 os.write((char*)buf, 2);
2926 writeU16(buf, message.size());
2927 os.write((char*)buf, 2);
2930 for(u32 i=0; i<message.size(); i++)
2934 os.write((char*)buf, 2);
2938 std::string s = os.str();
2939 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2941 m_con.Send(peer_id, 0, data, true);
2944 void Server::BroadcastChatMessage(const std::wstring &message)
2946 for(core::map<u16, RemoteClient*>::Iterator
2947 i = m_clients.getIterator();
2948 i.atEnd() == false; i++)
2950 // Get client and check that it is valid
2951 RemoteClient *client = i.getNode()->getValue();
2952 assert(client->peer_id == i.getNode()->getKey());
2953 if(client->serialization_version == SER_FMT_VER_INVALID)
2956 SendChatMessage(client->peer_id, message);
2960 void Server::SendBlocks(float dtime)
2962 DSTACK(__FUNCTION_NAME);
2964 JMutexAutoLock envlock(m_env_mutex);
2966 core::array<PrioritySortedBlockTransfer> queue;
2968 s32 total_sending = 0;
2970 for(core::map<u16, RemoteClient*>::Iterator
2971 i = m_clients.getIterator();
2972 i.atEnd() == false; i++)
2974 RemoteClient *client = i.getNode()->getValue();
2975 assert(client->peer_id == i.getNode()->getKey());
2977 total_sending += client->SendingCount();
2979 if(client->serialization_version == SER_FMT_VER_INVALID)
2982 client->GetNextBlocks(this, dtime, queue);
2986 // Lowest priority number comes first.
2987 // Lowest is most important.
2990 JMutexAutoLock conlock(m_con_mutex);
2992 for(u32 i=0; i<queue.size(); i++)
2994 //TODO: Calculate limit dynamically
2995 if(total_sending >= g_settings.getS32
2996 ("max_simultaneous_block_sends_server_total"))
2999 PrioritySortedBlockTransfer q = queue[i];
3001 MapBlock *block = NULL;
3004 block = m_env.getMap().getBlockNoCreate(q.pos);
3006 catch(InvalidPositionException &e)
3011 RemoteClient *client = getClient(q.peer_id);
3013 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3015 client->SentBlock(q.pos);
3022 RemoteClient* Server::getClient(u16 peer_id)
3024 DSTACK(__FUNCTION_NAME);
3025 //JMutexAutoLock lock(m_con_mutex);
3026 core::map<u16, RemoteClient*>::Node *n;
3027 n = m_clients.find(peer_id);
3028 // A client should exist for all peers
3030 return n->getValue();
3033 Player *Server::emergePlayer(const char *name, const char *password)
3036 Try to get an existing player
3038 Player *player = m_env.getPlayer(name);
3049 player = new ServerRemotePlayer();
3050 //player->peer_id = c.peer_id;
3051 player->peer_id = PEER_ID_INEXISTENT;
3052 player->updateName(name);
3058 dstream<<"Server: Finding spawn place for player \""
3059 <<player->getName()<<"\""<<std::endl;
3063 f32 groundheight = 0;
3064 // Try to find a good place a few times
3065 for(s32 i=0; i<500; i++)
3068 // We're going to try to throw the player to this position
3069 nodepos = v2s16(-range + (myrand()%(range*2)),
3070 -range + (myrand()%(range*2)));
3071 v2s16 sectorpos = getNodeSectorPos(nodepos);
3073 m_env.getMap().emergeSector(sectorpos);
3074 // Get ground height at point
3075 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3076 // The sector should have been generated -> groundheight exists
3077 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3078 // Don't go underwater
3079 if(groundheight < WATER_LEVEL)
3081 //dstream<<"-> Underwater"<<std::endl;
3084 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3085 // Get block at point
3087 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3088 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3089 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3090 // Don't go inside ground
3092 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3093 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3094 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3095 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3096 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3097 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3099 dstream<<"-> Inside ground"<<std::endl;
3103 }catch(InvalidPositionException &e)
3105 dstream<<"-> Invalid position"<<std::endl;
3106 // Ignore invalid position
3110 // Found a good place
3111 dstream<<"Searched through "<<i<<" places."<<std::endl;
3116 // If no suitable place was not found, go above water at least.
3117 if(groundheight < WATER_LEVEL)
3118 groundheight = WATER_LEVEL;
3120 player->setPosition(intToFloat(v3s16(
3127 Add player to environment
3130 m_env.addPlayer(player);
3133 Add stuff to inventory
3136 if(g_settings.getBool("creative_mode"))
3138 // Give some good picks
3140 InventoryItem *item = new ToolItem("STPick", 0);
3141 void* r = player->inventory.addItem("main", item);
3145 InventoryItem *item = new ToolItem("MesePick", 0);
3146 void* r = player->inventory.addItem("main", item);
3153 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3156 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3157 player->inventory.addItem("main", item);
3160 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3162 // Skip some materials
3163 if(i == CONTENT_OCEAN || i == CONTENT_TORCH)
3166 InventoryItem *item = new MaterialItem(i, 1);
3167 player->inventory.addItem("main", item);
3171 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3172 void* r = player->inventory.addItem("main", item);
3179 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3180 void* r = player->inventory.addItem("main", item);
3184 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3185 void* r = player->inventory.addItem("main", item);
3189 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3190 void* r = player->inventory.addItem("main", item);
3194 InventoryItem *item = new CraftItem("Stick", 4);
3195 void* r = player->inventory.addItem("main", item);
3199 InventoryItem *item = new ToolItem("WPick", 32000);
3200 void* r = player->inventory.addItem("main", item);
3204 InventoryItem *item = new ToolItem("STPick", 32000);
3205 void* r = player->inventory.addItem("main", item);
3208 /*// Give some lights
3210 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3211 bool r = player->inventory.addItem("main", item);
3215 for(u16 i=0; i<4; i++)
3217 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3218 bool r = player->inventory.addItem("main", item);
3221 /*// Give some other stuff
3223 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3224 bool r = player->inventory.addItem("main", item);
3233 void Server::UpdateBlockWaterPressure(MapBlock *block,
3234 core::map<v3s16, MapBlock*> &modified_blocks)
3236 MapVoxelManipulator v(&m_env.getMap());
3237 v.m_disable_water_climb =
3238 g_settings.getBool("disable_water_climb");
3240 VoxelArea area(block->getPosRelative(),
3241 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3245 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3247 catch(ProcessingLimitException &e)
3249 dstream<<"Processing limit reached (1)"<<std::endl;
3252 v.blitBack(modified_blocks);
3255 void Server::handlePeerChange(PeerChange &c)
3257 JMutexAutoLock envlock(m_env_mutex);
3258 JMutexAutoLock conlock(m_con_mutex);
3260 if(c.type == PEER_ADDED)
3267 core::map<u16, RemoteClient*>::Node *n;
3268 n = m_clients.find(c.peer_id);
3269 // The client shouldn't already exist
3273 RemoteClient *client = new RemoteClient();
3274 client->peer_id = c.peer_id;
3275 m_clients.insert(client->peer_id, client);
3278 else if(c.type == PEER_REMOVED)
3285 core::map<u16, RemoteClient*>::Node *n;
3286 n = m_clients.find(c.peer_id);
3287 // The client should exist
3290 // Collect information about leaving in chat
3291 std::wstring message;
3293 std::wstring name = L"unknown";
3294 Player *player = m_env.getPlayer(c.peer_id);
3296 name = narrow_to_wide(player->getName());
3300 message += L" left game";
3302 message += L" (timed out)";
3307 m_env.removePlayer(c.peer_id);
3310 // Set player client disconnected
3312 Player *player = m_env.getPlayer(c.peer_id);
3313 player->peer_id = 0;
3317 delete m_clients[c.peer_id];
3318 m_clients.remove(c.peer_id);
3320 // Send player info to all remaining clients
3323 // Send leave chat message to all remaining clients
3324 BroadcastChatMessage(message);
3333 void Server::handlePeerChanges()
3335 while(m_peer_change_queue.size() > 0)
3337 PeerChange c = m_peer_change_queue.pop_front();
3339 dout_server<<"Server: Handling peer change: "
3340 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3343 handlePeerChange(c);