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);
169 EXPERIMENTAL: Create a few other blocks too
176 lighting_invalidated_blocks);
182 lighting_invalidated_blocks);
186 // If it is a dummy, block was not found on disk
189 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
193 catch(InvalidPositionException &e)
196 // This happens when position is over limit.
202 if(debug && changed_blocks.size() > 0)
204 dout_server<<DTIME<<"Got changed_blocks: ";
205 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
206 i.atEnd() == false; i++)
208 MapBlock *block = i.getNode()->getValue();
209 v3s16 p = block->getPos();
210 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
212 dout_server<<std::endl;
217 Update water pressure
220 m_server->UpdateBlockWaterPressure(block, modified_blocks);
222 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
223 i.atEnd() == false; i++)
225 MapBlock *block = i.getNode()->getValue();
226 m_server->UpdateBlockWaterPressure(block, modified_blocks);
227 //v3s16 p = i.getNode()->getKey();
228 //m_server->UpdateBlockWaterPressure(p, modified_blocks);
233 Collect a list of blocks that have been modified in
234 addition to the fetched one.
237 // Add all the "changed blocks" to modified_blocks
238 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
239 i.atEnd() == false; i++)
241 MapBlock *block = i.getNode()->getValue();
242 modified_blocks.insert(block->getPos(), block);
245 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
246 <<" blocks"<<std::endl;*/
248 //TimeTaker timer("** updateLighting", g_device);
250 // Update lighting without locking the environment mutex,
251 // add modified blocks to changed blocks
252 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
254 // If we got no block, there should be no invalidated blocks
257 assert(lighting_invalidated_blocks.size() == 0);
263 Set sent status of modified blocks on clients
266 // NOTE: Server's clients are also behind the connection mutex
267 JMutexAutoLock lock(m_server->m_con_mutex);
270 Add the originally fetched block to the modified list
274 modified_blocks.insert(p, block);
278 Set the modified blocks unsent for all the clients
281 for(core::map<u16, RemoteClient*>::Iterator
282 i = m_server->m_clients.getIterator();
283 i.atEnd() == false; i++)
285 RemoteClient *client = i.getNode()->getValue();
287 if(modified_blocks.size() > 0)
289 // Remove block from sent history
290 client->SetBlocksNotSent(modified_blocks);
296 END_DEBUG_EXCEPTION_HANDLER
301 void RemoteClient::GetNextBlocks(Server *server, float dtime,
302 core::array<PrioritySortedBlockTransfer> &dest)
304 DSTACK(__FUNCTION_NAME);
308 JMutexAutoLock lock(m_blocks_sent_mutex);
309 m_nearest_unsent_reset_timer += dtime;
312 // Won't send anything if already sending
314 JMutexAutoLock lock(m_blocks_sending_mutex);
316 if(m_blocks_sending.size() >= g_settings.getU16
317 ("max_simultaneous_block_sends_per_client"))
319 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
324 bool haxmode = g_settings.getBool("haxmode");
326 Player *player = server->m_env.getPlayer(peer_id);
328 assert(player != NULL);
330 v3f playerpos = player->getPosition();
331 v3f playerspeed = player->getSpeed();
333 v3s16 center_nodepos = floatToInt(playerpos);
335 v3s16 center = getNodeBlockPos(center_nodepos);
337 // Camera position and direction
339 playerpos + v3f(0, BS+BS/2, 0);
340 v3f camera_dir = v3f(0,0,1);
341 camera_dir.rotateYZBy(player->getPitch());
342 camera_dir.rotateXZBy(player->getYaw());
345 Get the starting value of the block finder radius.
347 s16 last_nearest_unsent_d;
350 JMutexAutoLock lock(m_blocks_sent_mutex);
352 if(m_last_center != center)
354 m_nearest_unsent_d = 0;
355 m_last_center = center;
358 /*dstream<<"m_nearest_unsent_reset_timer="
359 <<m_nearest_unsent_reset_timer<<std::endl;*/
360 if(m_nearest_unsent_reset_timer > 5.0)
362 m_nearest_unsent_reset_timer = 0;
363 m_nearest_unsent_d = 0;
364 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
367 last_nearest_unsent_d = m_nearest_unsent_d;
369 d_start = m_nearest_unsent_d;
372 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
373 ("max_simultaneous_block_sends_per_client");
374 u16 maximum_simultaneous_block_sends =
375 maximum_simultaneous_block_sends_setting;
378 Check the time from last addNode/removeNode.
380 Decrease send rate if player is building stuff.
383 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
384 m_time_from_building.m_value += dtime;
385 /*if(m_time_from_building.m_value
386 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
387 if(m_time_from_building.m_value < g_settings.getFloat(
388 "full_block_send_enable_min_time_from_building"))
390 maximum_simultaneous_block_sends
391 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
395 u32 num_blocks_selected;
397 JMutexAutoLock lock(m_blocks_sending_mutex);
398 num_blocks_selected = m_blocks_sending.size();
402 next time d will be continued from the d from which the nearest
403 unsent block was found this time.
405 This is because not necessarily any of the blocks found this
406 time are actually sent.
408 s32 new_nearest_unsent_d = -1;
410 // Serialization version used
411 //u8 ser_version = serialization_version;
413 //bool has_incomplete_blocks = false;
415 s16 d_max = g_settings.getS16("max_block_send_distance");
416 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
418 //dstream<<"Starting from "<<d_start<<std::endl;
420 for(s16 d = d_start; d <= d_max; d++)
422 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
424 //if(has_incomplete_blocks == false)
426 JMutexAutoLock lock(m_blocks_sent_mutex);
428 If m_nearest_unsent_d was changed by the EmergeThread
429 (it can change it to 0 through SetBlockNotSent),
431 Else update m_nearest_unsent_d
433 if(m_nearest_unsent_d != last_nearest_unsent_d)
435 d = m_nearest_unsent_d;
436 last_nearest_unsent_d = m_nearest_unsent_d;
441 Get the border/face dot coordinates of a "d-radiused"
444 core::list<v3s16> list;
445 getFacePositions(list, d);
447 core::list<v3s16>::Iterator li;
448 for(li=list.begin(); li!=list.end(); li++)
450 v3s16 p = *li + center;
454 - Don't allow too many simultaneous transfers
455 - EXCEPT when the blocks are very close
457 Also, don't send blocks that are already flying.
460 u16 maximum_simultaneous_block_sends_now =
461 maximum_simultaneous_block_sends;
463 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
465 maximum_simultaneous_block_sends_now =
466 maximum_simultaneous_block_sends_setting;
470 JMutexAutoLock lock(m_blocks_sending_mutex);
472 // Limit is dynamically lowered when building
473 if(num_blocks_selected
474 >= maximum_simultaneous_block_sends_now)
476 /*dstream<<"Not sending more blocks. Queue full. "
477 <<m_blocks_sending.size()
482 if(m_blocks_sending.find(p) != NULL)
489 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
490 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
491 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
492 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
493 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
494 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
497 bool generate = d <= d_max_gen;
501 // Don't generate above player
507 // Limit the generating area vertically to 2/3
508 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
513 Don't draw if not in sight
516 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
522 Don't send already sent blocks
525 JMutexAutoLock lock(m_blocks_sent_mutex);
527 if(m_blocks_sent.find(p) != NULL)
534 Ignore block if it is not at ground surface
535 but don't ignore water surface blocks
537 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
538 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
539 f32 y = server->m_env.getMap().getGroundHeight(p2d);
540 // The sector might not exist yet, thus no heightmap
541 if(y > GROUNDHEIGHT_VALID_MINVALUE)
543 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
544 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
545 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
551 Check if map has this block
553 MapBlock *block = NULL;
556 block = server->m_env.getMap().getBlockNoCreate(p);
558 catch(InvalidPositionException &e)
562 bool surely_not_found_on_disk = false;
565 /*if(block->isIncomplete())
567 has_incomplete_blocks = true;
573 surely_not_found_on_disk = true;
578 If block has been marked to not exist on disk (dummy)
579 and generating new ones is not wanted, skip block.
581 if(generate == false && surely_not_found_on_disk == true)
588 Record the lowest d from which a a block has been
589 found being not sent and possibly to exist
591 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
593 new_nearest_unsent_d = d;
597 Add inexistent block to emerge queue.
599 if(block == NULL || surely_not_found_on_disk)
601 /*SharedPtr<JMutexAutoLock> lock
602 (m_num_blocks_in_emerge_queue.getLock());*/
604 //TODO: Get value from somewhere
605 // Allow only one block in emerge queue
606 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
608 // Add it to the emerge queue and trigger the thread
611 if(generate == false)
612 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
614 server->m_emerge_queue.addBlock(peer_id, p, flags);
615 server->m_emergethread.trigger();
626 PrioritySortedBlockTransfer q((float)d, p, peer_id);
630 num_blocks_selected += 1;
635 if(new_nearest_unsent_d != -1)
637 JMutexAutoLock lock(m_blocks_sent_mutex);
638 m_nearest_unsent_d = new_nearest_unsent_d;
642 void RemoteClient::SendObjectData(
645 core::map<v3s16, bool> &stepped_blocks
648 DSTACK(__FUNCTION_NAME);
650 // Can't send anything without knowing version
651 if(serialization_version == SER_FMT_VER_INVALID)
653 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
659 Send a TOCLIENT_OBJECTDATA packet.
663 u16 number of player positions
674 std::ostringstream os(std::ios_base::binary);
678 writeU16(buf, TOCLIENT_OBJECTDATA);
679 os.write((char*)buf, 2);
682 Get and write player data
685 // Get connected players
686 core::list<Player*> players = server->m_env.getPlayers(true);
688 // Write player count
689 u16 playercount = players.size();
690 writeU16(buf, playercount);
691 os.write((char*)buf, 2);
693 core::list<Player*>::Iterator i;
694 for(i = players.begin();
695 i != players.end(); i++)
699 v3f pf = player->getPosition();
700 v3f sf = player->getSpeed();
702 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
703 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
704 s32 pitch_i (player->getPitch() * 100);
705 s32 yaw_i (player->getYaw() * 100);
707 writeU16(buf, player->peer_id);
708 os.write((char*)buf, 2);
709 writeV3S32(buf, position_i);
710 os.write((char*)buf, 12);
711 writeV3S32(buf, speed_i);
712 os.write((char*)buf, 12);
713 writeS32(buf, pitch_i);
714 os.write((char*)buf, 4);
715 writeS32(buf, yaw_i);
716 os.write((char*)buf, 4);
720 Get and write object data
726 For making players to be able to build to their nearby
727 environment (building is not possible on blocks that are not
730 - Add blocks to emerge queue if they are not found
732 SUGGESTION: These could be ignored from the backside of the player
735 Player *player = server->m_env.getPlayer(peer_id);
739 v3f playerpos = player->getPosition();
740 v3f playerspeed = player->getSpeed();
742 v3s16 center_nodepos = floatToInt(playerpos);
743 v3s16 center = getNodeBlockPos(center_nodepos);
745 s16 d_max = g_settings.getS16("active_object_range");
747 // Number of blocks whose objects were written to bos
750 std::ostringstream bos(std::ios_base::binary);
752 for(s16 d = 0; d <= d_max; d++)
754 core::list<v3s16> list;
755 getFacePositions(list, d);
757 core::list<v3s16>::Iterator li;
758 for(li=list.begin(); li!=list.end(); li++)
760 v3s16 p = *li + center;
763 Ignore blocks that haven't been sent to the client
766 JMutexAutoLock sentlock(m_blocks_sent_mutex);
767 if(m_blocks_sent.find(p) == NULL)
771 // Try stepping block and add it to a send queue
776 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
779 Step block if not in stepped_blocks and add to stepped_blocks.
781 if(stepped_blocks.find(p) == NULL)
783 block->stepObjects(dtime, true, server->getDayNightRatio());
784 stepped_blocks.insert(p, true);
785 block->setChangedFlag();
788 // Skip block if there are no objects
789 if(block->getObjectCount() == 0)
798 bos.write((char*)buf, 6);
801 block->serializeObjects(bos, serialization_version);
806 Stop collecting objects if data is already too big
808 // Sum of player and object data sizes
809 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
810 // break out if data too big
811 if(sum > MAX_OBJECTDATA_SIZE)
813 goto skip_subsequent;
817 catch(InvalidPositionException &e)
820 // Add it to the emerge queue and trigger the thread.
821 // Fetch the block only if it is on disk.
823 // Grab and increment counter
824 /*SharedPtr<JMutexAutoLock> lock
825 (m_num_blocks_in_emerge_queue.getLock());
826 m_num_blocks_in_emerge_queue.m_value++;*/
828 // Add to queue as an anonymous fetch from disk
829 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
830 server->m_emerge_queue.addBlock(0, p, flags);
831 server->m_emergethread.trigger();
839 writeU16(buf, blockcount);
840 os.write((char*)buf, 2);
842 // Write block objects
849 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
852 std::string s = os.str();
853 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
854 // Send as unreliable
855 server->m_con.Send(peer_id, 0, data, false);
858 void RemoteClient::GotBlock(v3s16 p)
860 JMutexAutoLock lock(m_blocks_sending_mutex);
861 JMutexAutoLock lock2(m_blocks_sent_mutex);
862 if(m_blocks_sending.find(p) != NULL)
863 m_blocks_sending.remove(p);
866 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
867 " m_blocks_sending"<<std::endl;*/
868 m_excess_gotblocks++;
870 m_blocks_sent.insert(p, true);
873 void RemoteClient::SentBlock(v3s16 p)
875 JMutexAutoLock lock(m_blocks_sending_mutex);
876 /*if(m_blocks_sending.size() > 15)
878 dstream<<"RemoteClient::SentBlock(): "
879 <<"m_blocks_sending.size()="
880 <<m_blocks_sending.size()<<std::endl;
882 if(m_blocks_sending.find(p) == NULL)
883 m_blocks_sending.insert(p, 0.0);
885 dstream<<"RemoteClient::SentBlock(): Sent block"
886 " already in m_blocks_sending"<<std::endl;
889 void RemoteClient::SetBlockNotSent(v3s16 p)
891 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
892 JMutexAutoLock sentlock(m_blocks_sent_mutex);
894 m_nearest_unsent_d = 0;
896 if(m_blocks_sending.find(p) != NULL)
897 m_blocks_sending.remove(p);
898 if(m_blocks_sent.find(p) != NULL)
899 m_blocks_sent.remove(p);
902 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
904 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
905 JMutexAutoLock sentlock(m_blocks_sent_mutex);
907 m_nearest_unsent_d = 0;
909 for(core::map<v3s16, MapBlock*>::Iterator
910 i = blocks.getIterator();
911 i.atEnd()==false; i++)
913 v3s16 p = i.getNode()->getKey();
915 if(m_blocks_sending.find(p) != NULL)
916 m_blocks_sending.remove(p);
917 if(m_blocks_sent.find(p) != NULL)
918 m_blocks_sent.remove(p);
926 PlayerInfo::PlayerInfo()
931 void PlayerInfo::PrintLine(std::ostream *s)
934 (*s)<<"\""<<name<<"\" ("
935 <<position.X<<","<<position.Y
936 <<","<<position.Z<<") ";
938 (*s)<<" avg_rtt="<<avg_rtt;
942 u32 PIChecksum(core::list<PlayerInfo> &l)
944 core::list<PlayerInfo>::Iterator i;
947 for(i=l.begin(); i!=l.end(); i++)
949 checksum += a * (i->id+1);
950 checksum ^= 0x435aafcd;
961 std::string mapsavedir,
965 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
966 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
968 m_emergethread(this),
971 m_time_of_day_send_timer(0),
974 //m_flowwater_timer = 0.0;
975 m_liquid_transform_timer = 0.0;
976 m_print_info_timer = 0.0;
977 m_objectdata_timer = 0.0;
978 m_emergethread_trigger_timer = 0.0;
979 m_savemap_timer = 0.0;
983 m_step_dtime_mutex.Init();
992 JMutexAutoLock clientslock(m_con_mutex);
994 for(core::map<u16, RemoteClient*>::Iterator
995 i = m_clients.getIterator();
996 i.atEnd() == false; i++)
999 // NOTE: These are removed by env destructor
1001 u16 peer_id = i.getNode()->getKey();
1002 JMutexAutoLock envlock(m_env_mutex);
1003 m_env.removePlayer(peer_id);
1007 delete i.getNode()->getValue();
1011 void Server::start(unsigned short port)
1013 DSTACK(__FUNCTION_NAME);
1014 // Stop thread if already running
1017 // Initialize connection
1018 m_con.setTimeoutMs(30);
1022 m_thread.setRun(true);
1025 dout_server<<"Server started on port "<<port<<std::endl;
1030 DSTACK(__FUNCTION_NAME);
1031 // Stop threads (set run=false first so both start stopping)
1032 m_thread.setRun(false);
1033 m_emergethread.setRun(false);
1035 m_emergethread.stop();
1037 dout_server<<"Server threads stopped"<<std::endl;
1040 void Server::step(float dtime)
1042 DSTACK(__FUNCTION_NAME);
1047 JMutexAutoLock lock(m_step_dtime_mutex);
1048 m_step_dtime += dtime;
1052 void Server::AsyncRunStep()
1054 DSTACK(__FUNCTION_NAME);
1058 JMutexAutoLock lock1(m_step_dtime_mutex);
1059 dtime = m_step_dtime;
1062 // Send blocks to clients
1068 //dstream<<"Server steps "<<dtime<<std::endl;
1069 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1072 JMutexAutoLock lock1(m_step_dtime_mutex);
1073 m_step_dtime -= dtime;
1080 m_uptime.set(m_uptime.get() + dtime);
1084 Update m_time_of_day
1087 m_time_counter += dtime;
1088 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1089 u32 units = (u32)(m_time_counter*speed);
1090 m_time_counter -= (f32)units / speed;
1091 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1093 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1096 Send to clients at constant intervals
1099 m_time_of_day_send_timer -= dtime;
1100 if(m_time_of_day_send_timer < 0.0)
1102 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1104 //JMutexAutoLock envlock(m_env_mutex);
1105 JMutexAutoLock conlock(m_con_mutex);
1107 for(core::map<u16, RemoteClient*>::Iterator
1108 i = m_clients.getIterator();
1109 i.atEnd() == false; i++)
1111 RemoteClient *client = i.getNode()->getValue();
1112 //Player *player = m_env.getPlayer(client->peer_id);
1114 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1115 m_time_of_day.get());
1117 m_con.Send(client->peer_id, 0, data, true);
1123 // Process connection's timeouts
1124 JMutexAutoLock lock2(m_con_mutex);
1125 m_con.RunTimeouts(dtime);
1129 // This has to be called so that the client list gets synced
1130 // with the peer list of the connection
1131 handlePeerChanges();
1136 // This also runs Map's timers
1137 JMutexAutoLock lock(m_env_mutex);
1148 m_liquid_transform_timer += dtime;
1149 if(m_liquid_transform_timer >= 1.00)
1151 m_liquid_transform_timer -= 1.00;
1153 JMutexAutoLock lock(m_env_mutex);
1155 core::map<v3s16, MapBlock*> modified_blocks;
1156 m_env.getMap().transformLiquids(modified_blocks);
1161 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1162 ServerMap &map = ((ServerMap&)m_env.getMap());
1163 map.updateLighting(modified_blocks, lighting_modified_blocks);
1165 // Add blocks modified by lighting to modified_blocks
1166 for(core::map<v3s16, MapBlock*>::Iterator
1167 i = lighting_modified_blocks.getIterator();
1168 i.atEnd() == false; i++)
1170 MapBlock *block = i.getNode()->getValue();
1171 modified_blocks.insert(block->getPos(), block);
1175 Set the modified blocks unsent for all the clients
1178 JMutexAutoLock lock2(m_con_mutex);
1180 for(core::map<u16, RemoteClient*>::Iterator
1181 i = m_clients.getIterator();
1182 i.atEnd() == false; i++)
1184 RemoteClient *client = i.getNode()->getValue();
1186 if(modified_blocks.size() > 0)
1188 // Remove block from sent history
1189 client->SetBlocksNotSent(modified_blocks);
1198 if(g_settings.getBool("water_moves") == true)
1202 if(g_settings.getBool("endless_water") == false)
1207 float &counter = m_flowwater_timer;
1209 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1214 core::map<v3s16, MapBlock*> modified_blocks;
1218 JMutexAutoLock envlock(m_env_mutex);
1220 MapVoxelManipulator v(&m_env.getMap());
1221 v.m_disable_water_climb =
1222 g_settings.getBool("disable_water_climb");
1224 if(g_settings.getBool("endless_water") == false)
1225 v.flowWater(m_flow_active_nodes, 0, false, 250);
1227 v.flowWater(m_flow_active_nodes, 0, false, 50);
1229 v.blitBack(modified_blocks);
1231 ServerMap &map = ((ServerMap&)m_env.getMap());
1234 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1235 map.updateLighting(modified_blocks, lighting_modified_blocks);
1237 // Add blocks modified by lighting to modified_blocks
1238 for(core::map<v3s16, MapBlock*>::Iterator
1239 i = lighting_modified_blocks.getIterator();
1240 i.atEnd() == false; i++)
1242 MapBlock *block = i.getNode()->getValue();
1243 modified_blocks.insert(block->getPos(), block);
1248 Set the modified blocks unsent for all the clients
1251 JMutexAutoLock lock2(m_con_mutex);
1253 for(core::map<u16, RemoteClient*>::Iterator
1254 i = m_clients.getIterator();
1255 i.atEnd() == false; i++)
1257 RemoteClient *client = i.getNode()->getValue();
1259 if(modified_blocks.size() > 0)
1261 // Remove block from sent history
1262 client->SetBlocksNotSent(modified_blocks);
1266 } // interval counter
1270 // Periodically print some info
1272 float &counter = m_print_info_timer;
1278 JMutexAutoLock lock2(m_con_mutex);
1280 for(core::map<u16, RemoteClient*>::Iterator
1281 i = m_clients.getIterator();
1282 i.atEnd() == false; i++)
1284 //u16 peer_id = i.getNode()->getKey();
1285 RemoteClient *client = i.getNode()->getValue();
1286 client->PrintInfo(std::cout);
1294 NOTE: Some of this could be moved to RemoteClient
1298 JMutexAutoLock envlock(m_env_mutex);
1299 JMutexAutoLock conlock(m_con_mutex);
1301 for(core::map<u16, RemoteClient*>::Iterator
1302 i = m_clients.getIterator();
1303 i.atEnd() == false; i++)
1305 RemoteClient *client = i.getNode()->getValue();
1306 Player *player = m_env.getPlayer(client->peer_id);
1308 JMutexAutoLock digmutex(client->m_dig_mutex);
1310 if(client->m_dig_tool_item == -1)
1313 client->m_dig_time_remaining -= dtime;
1315 if(client->m_dig_time_remaining > 0)
1317 client->m_time_from_building.set(0.0);
1321 v3s16 p_under = client->m_dig_position;
1323 // Mandatory parameter; actually used for nothing
1324 core::map<v3s16, MapBlock*> modified_blocks;
1330 // Get material at position
1331 material = m_env.getMap().getNode(p_under).d;
1332 // If it's not diggable, do nothing
1333 if(content_diggable(material) == false)
1335 derr_server<<"Server: Not finishing digging: Node not diggable"
1337 client->m_dig_tool_item = -1;
1341 catch(InvalidPositionException &e)
1343 derr_server<<"Server: Not finishing digging: Node not found"
1345 client->m_dig_tool_item = -1;
1351 SharedBuffer<u8> reply(replysize);
1352 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1353 writeS16(&reply[2], p_under.X);
1354 writeS16(&reply[4], p_under.Y);
1355 writeS16(&reply[6], p_under.Z);
1357 m_con.SendToAll(0, reply, true);
1359 if(g_settings.getBool("creative_mode") == false)
1361 // Add to inventory and send inventory
1362 InventoryItem *item = new MaterialItem(material, 1);
1363 player->inventory.addItem("main", item);
1364 SendInventory(player->peer_id);
1369 (this takes some time so it is done after the quick stuff)
1371 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1377 // Update water pressure around modification
1378 // This also adds it to m_flow_active_nodes if appropriate
1380 MapVoxelManipulator v(&m_env.getMap());
1381 v.m_disable_water_climb =
1382 g_settings.getBool("disable_water_climb");
1384 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1388 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1390 catch(ProcessingLimitException &e)
1392 dstream<<"Processing limit reached (1)"<<std::endl;
1395 v.blitBack(modified_blocks);
1400 // Send object positions
1402 float &counter = m_objectdata_timer;
1404 if(counter >= g_settings.getFloat("objectdata_interval"))
1406 JMutexAutoLock lock1(m_env_mutex);
1407 JMutexAutoLock lock2(m_con_mutex);
1408 SendObjectData(counter);
1414 // Trigger emergethread (it gets somehow gets to a
1415 // non-triggered but bysy state sometimes)
1417 float &counter = m_emergethread_trigger_timer;
1423 m_emergethread.trigger();
1429 float &counter = m_savemap_timer;
1431 if(counter >= g_settings.getFloat("server_map_save_interval"))
1435 JMutexAutoLock lock(m_env_mutex);
1437 // Save only changed parts
1438 m_env.getMap().save(true);
1440 // Delete unused sectors
1441 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1442 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1443 if(deleted_count > 0)
1445 dout_server<<"Server: Unloaded "<<deleted_count
1446 <<" sectors from memory"<<std::endl;
1452 void Server::Receive()
1454 DSTACK(__FUNCTION_NAME);
1455 u32 data_maxsize = 10000;
1456 Buffer<u8> data(data_maxsize);
1461 JMutexAutoLock conlock(m_con_mutex);
1462 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1465 // This has to be called so that the client list gets synced
1466 // with the peer list of the connection
1467 handlePeerChanges();
1469 ProcessData(*data, datasize, peer_id);
1471 catch(con::InvalidIncomingDataException &e)
1473 derr_server<<"Server::Receive(): "
1474 "InvalidIncomingDataException: what()="
1475 <<e.what()<<std::endl;
1477 catch(con::PeerNotFoundException &e)
1479 //NOTE: This is not needed anymore
1481 // The peer has been disconnected.
1482 // Find the associated player and remove it.
1484 /*JMutexAutoLock envlock(m_env_mutex);
1486 dout_server<<"ServerThread: peer_id="<<peer_id
1487 <<" has apparently closed connection. "
1488 <<"Removing player."<<std::endl;
1490 m_env.removePlayer(peer_id);*/
1494 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1496 DSTACK(__FUNCTION_NAME);
1497 // Environment is locked first.
1498 JMutexAutoLock envlock(m_env_mutex);
1499 JMutexAutoLock conlock(m_con_mutex);
1503 peer = m_con.GetPeer(peer_id);
1505 catch(con::PeerNotFoundException &e)
1507 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1508 <<peer_id<<" not found"<<std::endl;
1512 //u8 peer_ser_ver = peer->serialization_version;
1513 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1521 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1523 if(command == TOSERVER_INIT)
1525 // [0] u16 TOSERVER_INIT
1526 // [2] u8 SER_FMT_VER_HIGHEST
1527 // [3] u8[20] player_name
1532 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1533 <<peer->id<<std::endl;
1535 // First byte after command is maximum supported
1536 // serialization version
1537 u8 client_max = data[2];
1538 u8 our_max = SER_FMT_VER_HIGHEST;
1539 // Use the highest version supported by both
1540 u8 deployed = core::min_(client_max, our_max);
1541 // If it's lower than the lowest supported, give up.
1542 if(deployed < SER_FMT_VER_LOWEST)
1543 deployed = SER_FMT_VER_INVALID;
1545 //peer->serialization_version = deployed;
1546 getClient(peer->id)->pending_serialization_version = deployed;
1548 if(deployed == SER_FMT_VER_INVALID)
1550 derr_server<<DTIME<<"Server: Cannot negotiate "
1551 "serialization version with peer "
1552 <<peer_id<<std::endl;
1561 const u32 playername_size = 20;
1562 char playername[playername_size];
1563 for(u32 i=0; i<playername_size-1; i++)
1565 playername[i] = data[3+i];
1567 playername[playername_size-1] = 0;
1570 Player *player = emergePlayer(playername, "");
1571 //Player *player = m_env.getPlayer(peer_id);
1573 // If a client is already connected to the player, cancel
1574 if(player->peer_id != 0)
1576 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1577 <<" tried to connect to "
1578 "an already connected player (peer_id="
1579 <<player->peer_id<<")"<<std::endl;
1583 // Set client of player
1584 player->peer_id = peer_id;
1586 // Check if player doesn't exist
1588 throw con::InvalidIncomingDataException
1589 ("Server::ProcessData(): INIT: Player doesn't exist");
1591 /*// update name if it was supplied
1592 if(datasize >= 20+3)
1595 player->updateName((const char*)&data[3]);
1598 // Now answer with a TOCLIENT_INIT
1600 SharedBuffer<u8> reply(2+1+6);
1601 writeU16(&reply[0], TOCLIENT_INIT);
1602 writeU8(&reply[2], deployed);
1603 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1605 m_con.Send(peer_id, 0, reply, true);
1609 if(command == TOSERVER_INIT2)
1611 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1612 <<peer->id<<std::endl;
1615 getClient(peer->id)->serialization_version
1616 = getClient(peer->id)->pending_serialization_version;
1619 Send some initialization data
1622 // Send player info to all players
1625 // Send inventory to player
1626 SendInventory(peer->id);
1630 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1631 m_time_of_day.get());
1632 m_con.Send(peer->id, 0, data, true);
1635 // Send information about server to player in chat
1637 std::wostringstream os(std::ios_base::binary);
1640 os<<L"uptime="<<m_uptime.get();
1641 // Information about clients
1643 for(core::map<u16, RemoteClient*>::Iterator
1644 i = m_clients.getIterator();
1645 i.atEnd() == false; i++)
1647 // Get client and check that it is valid
1648 RemoteClient *client = i.getNode()->getValue();
1649 assert(client->peer_id == i.getNode()->getKey());
1650 if(client->serialization_version == SER_FMT_VER_INVALID)
1653 Player *player = m_env.getPlayer(client->peer_id);
1654 // Get name of player
1655 std::wstring name = L"unknown";
1657 name = narrow_to_wide(player->getName());
1658 // Add name to information string
1663 SendChatMessage(peer_id, os.str());
1666 // Send information about joining in chat
1668 std::wstring name = L"unknown";
1669 Player *player = m_env.getPlayer(peer_id);
1671 name = narrow_to_wide(player->getName());
1673 std::wstring message;
1676 message += L" joined game";
1677 BroadcastChatMessage(message);
1683 if(peer_ser_ver == SER_FMT_VER_INVALID)
1685 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1686 " serialization format invalid or not initialized."
1687 " Skipping incoming command="<<command<<std::endl;
1691 Player *player = m_env.getPlayer(peer_id);
1694 derr_server<<"Server::ProcessData(): Cancelling: "
1695 "No player for peer_id="<<peer_id
1699 if(command == TOSERVER_PLAYERPOS)
1701 if(datasize < 2+12+12+4+4)
1705 v3s32 ps = readV3S32(&data[start+2]);
1706 v3s32 ss = readV3S32(&data[start+2+12]);
1707 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1708 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1709 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1710 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1711 pitch = wrapDegrees(pitch);
1712 yaw = wrapDegrees(yaw);
1713 player->setPosition(position);
1714 player->setSpeed(speed);
1715 player->setPitch(pitch);
1716 player->setYaw(yaw);
1718 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1719 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1720 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1722 else if(command == TOSERVER_GOTBLOCKS)
1735 u16 count = data[2];
1736 for(u16 i=0; i<count; i++)
1738 if((s16)datasize < 2+1+(i+1)*6)
1739 throw con::InvalidIncomingDataException
1740 ("GOTBLOCKS length is too short");
1741 v3s16 p = readV3S16(&data[2+1+i*6]);
1742 /*dstream<<"Server: GOTBLOCKS ("
1743 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1744 RemoteClient *client = getClient(peer_id);
1745 client->GotBlock(p);
1748 else if(command == TOSERVER_DELETEDBLOCKS)
1761 u16 count = data[2];
1762 for(u16 i=0; i<count; i++)
1764 if((s16)datasize < 2+1+(i+1)*6)
1765 throw con::InvalidIncomingDataException
1766 ("DELETEDBLOCKS length is too short");
1767 v3s16 p = readV3S16(&data[2+1+i*6]);
1768 /*dstream<<"Server: DELETEDBLOCKS ("
1769 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1770 RemoteClient *client = getClient(peer_id);
1771 client->SetBlockNotSent(p);
1774 else if(command == TOSERVER_CLICK_OBJECT)
1781 [2] u8 button (0=left, 1=right)
1786 u8 button = readU8(&data[2]);
1788 p.X = readS16(&data[3]);
1789 p.Y = readS16(&data[5]);
1790 p.Z = readS16(&data[7]);
1791 s16 id = readS16(&data[9]);
1792 //u16 item_i = readU16(&data[11]);
1794 MapBlock *block = NULL;
1797 block = m_env.getMap().getBlockNoCreate(p);
1799 catch(InvalidPositionException &e)
1801 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1805 MapBlockObject *obj = block->getObject(id);
1809 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1813 //TODO: Check that object is reasonably close
1818 InventoryList *ilist = player->inventory.getList("main");
1819 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1822 // Skip if inventory has no free space
1823 if(ilist->getUsedSlots() == ilist->getSize())
1825 dout_server<<"Player inventory has no free space"<<std::endl;
1830 Create the inventory item
1832 InventoryItem *item = NULL;
1833 // If it is an item-object, take the item from it
1834 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1836 item = ((ItemObject*)obj)->createInventoryItem();
1838 // Else create an item of the object
1841 item = new MapBlockObjectItem
1842 (obj->getInventoryString());
1845 // Add to inventory and send inventory
1846 ilist->addItem(item);
1847 SendInventory(player->peer_id);
1850 // Remove from block
1851 block->removeObject(id);
1854 else if(command == TOSERVER_GROUND_ACTION)
1862 [3] v3s16 nodepos_undersurface
1863 [9] v3s16 nodepos_abovesurface
1868 2: stop digging (all parameters ignored)
1870 u8 action = readU8(&data[2]);
1872 p_under.X = readS16(&data[3]);
1873 p_under.Y = readS16(&data[5]);
1874 p_under.Z = readS16(&data[7]);
1876 p_over.X = readS16(&data[9]);
1877 p_over.Y = readS16(&data[11]);
1878 p_over.Z = readS16(&data[13]);
1879 u16 item_i = readU16(&data[15]);
1881 //TODO: Check that target is reasonably close
1889 NOTE: This can be used in the future to check if
1890 somebody is cheating, by checking the timing.
1897 else if(action == 2)
1900 RemoteClient *client = getClient(peer->id);
1901 JMutexAutoLock digmutex(client->m_dig_mutex);
1902 client->m_dig_tool_item = -1;
1907 3: Digging completed
1909 else if(action == 3)
1911 // Mandatory parameter; actually used for nothing
1912 core::map<v3s16, MapBlock*> modified_blocks;
1918 // Get material at position
1919 material = m_env.getMap().getNode(p_under).d;
1920 // If it's not diggable, do nothing
1921 if(content_diggable(material) == false)
1923 derr_server<<"Server: Not finishing digging: Node not diggable"
1928 catch(InvalidPositionException &e)
1930 derr_server<<"Server: Not finishing digging: Node not found"
1935 //TODO: Send to only other clients
1938 Send the removal to all other clients
1943 SharedBuffer<u8> reply(replysize);
1944 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1945 writeS16(&reply[2], p_under.X);
1946 writeS16(&reply[4], p_under.Y);
1947 writeS16(&reply[6], p_under.Z);
1949 for(core::map<u16, RemoteClient*>::Iterator
1950 i = m_clients.getIterator();
1951 i.atEnd() == false; i++)
1953 // Get client and check that it is valid
1954 RemoteClient *client = i.getNode()->getValue();
1955 assert(client->peer_id == i.getNode()->getKey());
1956 if(client->serialization_version == SER_FMT_VER_INVALID)
1959 // Don't send if it's the same one
1960 if(peer_id == client->peer_id)
1964 m_con.Send(client->peer_id, 0, reply, true);
1968 Update and send inventory
1971 if(g_settings.getBool("creative_mode") == false)
1976 InventoryList *mlist = player->inventory.getList("main");
1979 InventoryItem *item = mlist->getItem(item_i);
1980 if(item && (std::string)item->getName() == "ToolItem")
1982 ToolItem *titem = (ToolItem*)item;
1983 std::string toolname = titem->getToolName();
1985 // Get digging properties for material and tool
1986 DiggingProperties prop =
1987 getDiggingProperties(material, toolname);
1989 if(prop.diggable == false)
1991 derr_server<<"Server: WARNING: Player digged"
1992 <<" with impossible material + tool"
1993 <<" combination"<<std::endl;
1996 bool weared_out = titem->addWear(prop.wear);
2000 mlist->deleteItem(item_i);
2006 Add digged item to inventory
2008 InventoryItem *item = new MaterialItem(material, 1);
2009 player->inventory.addItem("main", item);
2014 SendInventory(player->peer_id);
2019 (this takes some time so it is done after the quick stuff)
2021 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2028 // Update water pressure around modification
2029 // This also adds it to m_flow_active_nodes if appropriate
2031 MapVoxelManipulator v(&m_env.getMap());
2032 v.m_disable_water_climb =
2033 g_settings.getBool("disable_water_climb");
2035 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2039 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2041 catch(ProcessingLimitException &e)
2043 dstream<<"Processing limit reached (1)"<<std::endl;
2046 v.blitBack(modified_blocks);
2053 else if(action == 1)
2056 InventoryList *ilist = player->inventory.getList("main");
2061 InventoryItem *item = ilist->getItem(item_i);
2063 // If there is no item, it is not possible to add it anywhere
2068 Handle material items
2070 if(std::string("MaterialItem") == item->getName())
2073 // Don't add a node if this is not a free space
2074 MapNode n2 = m_env.getMap().getNode(p_over);
2075 if(content_buildable_to(n2.d) == false)
2078 catch(InvalidPositionException &e)
2080 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2085 // Reset build time counter
2086 getClient(peer->id)->m_time_from_building.set(0.0);
2089 MaterialItem *mitem = (MaterialItem*)item;
2091 n.d = mitem->getMaterial();
2092 if(content_directional(n.d))
2093 n.dir = packDir(p_under - p_over);
2097 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2098 SharedBuffer<u8> reply(replysize);
2099 writeU16(&reply[0], TOCLIENT_ADDNODE);
2100 writeS16(&reply[2], p_over.X);
2101 writeS16(&reply[4], p_over.Y);
2102 writeS16(&reply[6], p_over.Z);
2103 n.serialize(&reply[8], peer_ser_ver);
2105 m_con.SendToAll(0, reply, true);
2110 InventoryList *ilist = player->inventory.getList("main");
2111 if(g_settings.getBool("creative_mode") == false && ilist)
2113 // Remove from inventory and send inventory
2114 if(mitem->getCount() == 1)
2115 ilist->deleteItem(item_i);
2119 SendInventory(peer_id);
2125 This takes some time so it is done after the quick stuff
2127 core::map<v3s16, MapBlock*> modified_blocks;
2128 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2134 InventoryList *ilist = player->inventory.getList("main");
2135 if(g_settings.getBool("creative_mode") == false && ilist)
2137 // Remove from inventory and send inventory
2138 if(mitem->getCount() == 1)
2139 ilist->deleteItem(item_i);
2143 SendInventory(peer_id);
2149 This takes some time so it is done after the quick stuff
2151 core::map<v3s16, MapBlock*> modified_blocks;
2152 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2155 Set the modified blocks unsent for all the clients
2158 //JMutexAutoLock lock2(m_con_mutex);
2160 for(core::map<u16, RemoteClient*>::Iterator
2161 i = m_clients.getIterator();
2162 i.atEnd() == false; i++)
2164 RemoteClient *client = i.getNode()->getValue();
2166 if(modified_blocks.size() > 0)
2168 // Remove block from sent history
2169 client->SetBlocksNotSent(modified_blocks);
2179 // Update water pressure around modification
2180 // This also adds it to m_flow_active_nodes if appropriate
2182 MapVoxelManipulator v(&m_env.getMap());
2183 v.m_disable_water_climb =
2184 g_settings.getBool("disable_water_climb");
2186 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2190 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2192 catch(ProcessingLimitException &e)
2194 dstream<<"Processing limit reached (1)"<<std::endl;
2197 v.blitBack(modified_blocks);
2205 v3s16 blockpos = getNodeBlockPos(p_over);
2207 MapBlock *block = NULL;
2210 block = m_env.getMap().getBlockNoCreate(blockpos);
2212 catch(InvalidPositionException &e)
2214 derr_server<<"Error while placing object: "
2215 "block not found"<<std::endl;
2219 v3s16 block_pos_i_on_map = block->getPosRelative();
2220 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2222 v3f pos = intToFloat(p_over);
2223 pos -= block_pos_f_on_map;
2225 /*dout_server<<"pos="
2226 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2229 MapBlockObject *obj = NULL;
2232 Handle block object items
2234 if(std::string("MBOItem") == item->getName())
2236 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2238 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2239 "inventorystring=\""
2240 <<oitem->getInventoryString()
2241 <<"\""<<std::endl;*/
2243 obj = oitem->createObject
2244 (pos, player->getYaw(), player->getPitch());
2251 dout_server<<"Placing a miscellaneous item on map"
2254 Create an ItemObject that contains the item.
2256 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2257 std::ostringstream os(std::ios_base::binary);
2258 item->serialize(os);
2259 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2260 iobj->setItemString(os.str());
2266 derr_server<<"WARNING: item resulted in NULL object, "
2267 <<"not placing onto map"
2272 block->addObject(obj);
2274 dout_server<<"Placed object"<<std::endl;
2276 InventoryList *ilist = player->inventory.getList("main");
2277 if(g_settings.getBool("creative_mode") == false && ilist)
2279 // Remove from inventory and send inventory
2280 ilist->deleteItem(item_i);
2282 SendInventory(peer_id);
2290 Catch invalid actions
2294 derr_server<<"WARNING: Server: Invalid action "
2295 <<action<<std::endl;
2299 else if(command == TOSERVER_RELEASE)
2308 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2311 else if(command == TOSERVER_SIGNTEXT)
2320 std::string datastring((char*)&data[2], datasize-2);
2321 std::istringstream is(datastring, std::ios_base::binary);
2324 is.read((char*)buf, 6);
2325 v3s16 blockpos = readV3S16(buf);
2326 is.read((char*)buf, 2);
2327 s16 id = readS16(buf);
2328 is.read((char*)buf, 2);
2329 u16 textlen = readU16(buf);
2331 for(u16 i=0; i<textlen; i++)
2333 is.read((char*)buf, 1);
2334 text += (char)buf[0];
2337 MapBlock *block = NULL;
2340 block = m_env.getMap().getBlockNoCreate(blockpos);
2342 catch(InvalidPositionException &e)
2344 derr_server<<"Error while setting sign text: "
2345 "block not found"<<std::endl;
2349 MapBlockObject *obj = block->getObject(id);
2352 derr_server<<"Error while setting sign text: "
2353 "object not found"<<std::endl;
2357 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2359 derr_server<<"Error while setting sign text: "
2360 "object is not a sign"<<std::endl;
2364 ((SignObject*)obj)->setText(text);
2366 obj->getBlock()->setChangedFlag();
2368 else if(command == TOSERVER_INVENTORY_ACTION)
2370 /*// Ignore inventory changes if in creative mode
2371 if(g_settings.getBool("creative_mode") == true)
2373 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2377 // Strip command and create a stream
2378 std::string datastring((char*)&data[2], datasize-2);
2379 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2380 std::istringstream is(datastring, std::ios_base::binary);
2382 InventoryAction *a = InventoryAction::deSerialize(is);
2386 Handle craftresult specially if not in creative mode
2388 bool disable_action = false;
2389 if(a->getType() == IACTION_MOVE
2390 && g_settings.getBool("creative_mode") == false)
2392 IMoveAction *ma = (IMoveAction*)a;
2393 // Don't allow moving anything to craftresult
2394 if(ma->to_name == "craftresult")
2397 disable_action = true;
2399 // When something is removed from craftresult
2400 if(ma->from_name == "craftresult")
2402 disable_action = true;
2403 // Remove stuff from craft
2404 InventoryList *clist = player->inventory.getList("craft");
2407 u16 count = ma->count;
2410 clist->decrementMaterials(count);
2413 // Feed action to player inventory
2414 a->apply(&player->inventory);
2417 // If something appeared in craftresult, throw it
2419 InventoryList *rlist = player->inventory.getList("craftresult");
2420 InventoryList *mlist = player->inventory.getList("main");
2421 if(rlist && mlist && rlist->getUsedSlots() == 1)
2423 InventoryItem *item1 = rlist->changeItem(0, NULL);
2424 mlist->addItem(item1);
2428 if(disable_action == false)
2430 // Feed action to player inventory
2431 a->apply(&player->inventory);
2436 SendInventory(player->peer_id);
2440 dstream<<"TOSERVER_INVENTORY_ACTION: "
2441 <<"InventoryAction::deSerialize() returned NULL"
2445 else if(command == TOSERVER_CHAT_MESSAGE)
2453 std::string datastring((char*)&data[2], datasize-2);
2454 std::istringstream is(datastring, std::ios_base::binary);
2457 is.read((char*)buf, 2);
2458 u16 len = readU16(buf);
2460 std::wstring message;
2461 for(u16 i=0; i<len; i++)
2463 is.read((char*)buf, 2);
2464 message += (wchar_t)readU16(buf);
2467 // Get player name of this client
2468 std::wstring name = narrow_to_wide(player->getName());
2470 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2472 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2475 Send the message to all other clients
2477 for(core::map<u16, RemoteClient*>::Iterator
2478 i = m_clients.getIterator();
2479 i.atEnd() == false; i++)
2481 // Get client and check that it is valid
2482 RemoteClient *client = i.getNode()->getValue();
2483 assert(client->peer_id == i.getNode()->getKey());
2484 if(client->serialization_version == SER_FMT_VER_INVALID)
2487 // Don't send if it's the same one
2488 if(peer_id == client->peer_id)
2491 SendChatMessage(client->peer_id, line);
2496 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2497 "unknown command "<<command<<std::endl;
2501 catch(SendFailedException &e)
2503 derr_server<<"Server::ProcessData(): SendFailedException: "
2509 /*void Server::Send(u16 peer_id, u16 channelnum,
2510 SharedBuffer<u8> data, bool reliable)
2512 JMutexAutoLock lock(m_con_mutex);
2513 m_con.Send(peer_id, channelnum, data, reliable);
2516 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2518 DSTACK(__FUNCTION_NAME);
2520 Create a packet with the block in the right format
2523 std::ostringstream os(std::ios_base::binary);
2524 block->serialize(os, ver);
2525 std::string s = os.str();
2526 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2528 u32 replysize = 8 + blockdata.getSize();
2529 SharedBuffer<u8> reply(replysize);
2530 v3s16 p = block->getPos();
2531 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2532 writeS16(&reply[2], p.X);
2533 writeS16(&reply[4], p.Y);
2534 writeS16(&reply[6], p.Z);
2535 memcpy(&reply[8], *blockdata, blockdata.getSize());
2537 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2538 <<": \tpacket size: "<<replysize<<std::endl;*/
2543 m_con.Send(peer_id, 1, reply, true);
2546 core::list<PlayerInfo> Server::getPlayerInfo()
2548 DSTACK(__FUNCTION_NAME);
2549 JMutexAutoLock envlock(m_env_mutex);
2550 JMutexAutoLock conlock(m_con_mutex);
2552 core::list<PlayerInfo> list;
2554 core::list<Player*> players = m_env.getPlayers();
2556 core::list<Player*>::Iterator i;
2557 for(i = players.begin();
2558 i != players.end(); i++)
2562 Player *player = *i;
2565 con::Peer *peer = m_con.GetPeer(player->peer_id);
2566 // Copy info from peer to info struct
2568 info.address = peer->address;
2569 info.avg_rtt = peer->avg_rtt;
2571 catch(con::PeerNotFoundException &e)
2573 // Set dummy peer info
2575 info.address = Address(0,0,0,0,0);
2579 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2580 info.position = player->getPosition();
2582 list.push_back(info);
2588 void Server::peerAdded(con::Peer *peer)
2590 DSTACK(__FUNCTION_NAME);
2591 dout_server<<"Server::peerAdded(): peer->id="
2592 <<peer->id<<std::endl;
2595 c.type = PEER_ADDED;
2596 c.peer_id = peer->id;
2598 m_peer_change_queue.push_back(c);
2601 void Server::deletingPeer(con::Peer *peer, bool timeout)
2603 DSTACK(__FUNCTION_NAME);
2604 dout_server<<"Server::deletingPeer(): peer->id="
2605 <<peer->id<<", timeout="<<timeout<<std::endl;
2608 c.type = PEER_REMOVED;
2609 c.peer_id = peer->id;
2610 c.timeout = timeout;
2611 m_peer_change_queue.push_back(c);
2614 void Server::SendObjectData(float dtime)
2616 DSTACK(__FUNCTION_NAME);
2618 core::map<v3s16, bool> stepped_blocks;
2620 for(core::map<u16, RemoteClient*>::Iterator
2621 i = m_clients.getIterator();
2622 i.atEnd() == false; i++)
2624 u16 peer_id = i.getNode()->getKey();
2625 RemoteClient *client = i.getNode()->getValue();
2626 assert(client->peer_id == peer_id);
2628 if(client->serialization_version == SER_FMT_VER_INVALID)
2631 client->SendObjectData(this, dtime, stepped_blocks);
2635 void Server::SendPlayerInfos()
2637 DSTACK(__FUNCTION_NAME);
2639 //JMutexAutoLock envlock(m_env_mutex);
2641 // Get connected players
2642 core::list<Player*> players = m_env.getPlayers(true);
2644 u32 player_count = players.getSize();
2645 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2647 SharedBuffer<u8> data(datasize);
2648 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2651 core::list<Player*>::Iterator i;
2652 for(i = players.begin();
2653 i != players.end(); i++)
2655 Player *player = *i;
2657 /*dstream<<"Server sending player info for player with "
2658 "peer_id="<<player->peer_id<<std::endl;*/
2660 writeU16(&data[start], player->peer_id);
2661 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2662 start += 2+PLAYERNAME_SIZE;
2665 //JMutexAutoLock conlock(m_con_mutex);
2668 m_con.SendToAll(0, data, true);
2686 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2692 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2698 enum ItemSpecType type;
2699 // Only other one of these is used
2705 items: a pointer to an array of 9 pointers to items
2706 specs: a pointer to an array of 9 ItemSpecs
2708 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2710 u16 items_min_x = 100;
2711 u16 items_max_x = 100;
2712 u16 items_min_y = 100;
2713 u16 items_max_y = 100;
2714 for(u16 y=0; y<3; y++)
2715 for(u16 x=0; x<3; x++)
2717 if(items[y*3 + x] == NULL)
2719 if(items_min_x == 100 || x < items_min_x)
2721 if(items_min_y == 100 || y < items_min_y)
2723 if(items_max_x == 100 || x > items_max_x)
2725 if(items_max_y == 100 || y > items_max_y)
2728 // No items at all, just return false
2729 if(items_min_x == 100)
2732 u16 items_w = items_max_x - items_min_x + 1;
2733 u16 items_h = items_max_y - items_min_y + 1;
2735 u16 specs_min_x = 100;
2736 u16 specs_max_x = 100;
2737 u16 specs_min_y = 100;
2738 u16 specs_max_y = 100;
2739 for(u16 y=0; y<3; y++)
2740 for(u16 x=0; x<3; x++)
2742 if(specs[y*3 + x].type == ITEM_NONE)
2744 if(specs_min_x == 100 || x < specs_min_x)
2746 if(specs_min_y == 100 || y < specs_min_y)
2748 if(specs_max_x == 100 || x > specs_max_x)
2750 if(specs_max_y == 100 || y > specs_max_y)
2753 // No specs at all, just return false
2754 if(specs_min_x == 100)
2757 u16 specs_w = specs_max_x - specs_min_x + 1;
2758 u16 specs_h = specs_max_y - specs_min_y + 1;
2761 if(items_w != specs_w || items_h != specs_h)
2764 for(u16 y=0; y<specs_h; y++)
2765 for(u16 x=0; x<specs_w; x++)
2767 u16 items_x = items_min_x + x;
2768 u16 items_y = items_min_y + y;
2769 u16 specs_x = specs_min_x + x;
2770 u16 specs_y = specs_min_y + y;
2771 InventoryItem *item = items[items_y * 3 + items_x];
2772 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2774 if(spec.type == ITEM_NONE)
2776 // Has to be no item
2782 // There should be an item
2786 std::string itemname = item->getName();
2788 if(spec.type == ITEM_MATERIAL)
2790 if(itemname != "MaterialItem")
2792 MaterialItem *mitem = (MaterialItem*)item;
2793 if(mitem->getMaterial() != spec.num)
2796 else if(spec.type == ITEM_CRAFT)
2798 if(itemname != "CraftItem")
2800 CraftItem *mitem = (CraftItem*)item;
2801 if(mitem->getSubName() != spec.name)
2804 else if(spec.type == ITEM_TOOL)
2806 // Not supported yet
2809 else if(spec.type == ITEM_MBO)
2811 // Not supported yet
2816 // Not supported yet
2824 void Server::SendInventory(u16 peer_id)
2826 DSTACK(__FUNCTION_NAME);
2828 Player* player = m_env.getPlayer(peer_id);
2831 Calculate crafting stuff
2833 if(g_settings.getBool("creative_mode") == false)
2835 InventoryList *clist = player->inventory.getList("craft");
2836 InventoryList *rlist = player->inventory.getList("craftresult");
2839 rlist->clearItems();
2843 InventoryItem *items[9];
2844 for(u16 i=0; i<9; i++)
2846 items[i] = clist->getItem(i);
2855 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2856 if(checkItemCombination(items, specs))
2858 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2867 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2868 if(checkItemCombination(items, specs))
2870 rlist->addItem(new CraftItem("Stick", 4));
2879 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2880 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2881 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2882 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2883 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2884 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2885 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2886 if(checkItemCombination(items, specs))
2888 rlist->addItem(new MapBlockObjectItem("Sign"));
2897 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2898 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2899 if(checkItemCombination(items, specs))
2901 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2910 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2911 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2912 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2913 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2914 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2915 if(checkItemCombination(items, specs))
2917 rlist->addItem(new ToolItem("WPick", 0));
2926 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2927 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2928 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2929 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2930 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2931 if(checkItemCombination(items, specs))
2933 rlist->addItem(new ToolItem("STPick", 0));
2942 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2943 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2944 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2945 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2946 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2947 if(checkItemCombination(items, specs))
2949 rlist->addItem(new ToolItem("MesePick", 0));
2954 } // if creative_mode == false
2960 std::ostringstream os;
2961 //os.imbue(std::locale("C"));
2963 player->inventory.serialize(os);
2965 std::string s = os.str();
2967 SharedBuffer<u8> data(s.size()+2);
2968 writeU16(&data[0], TOCLIENT_INVENTORY);
2969 memcpy(&data[2], s.c_str(), s.size());
2972 m_con.Send(peer_id, 0, data, true);
2975 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2977 DSTACK(__FUNCTION_NAME);
2979 std::ostringstream os(std::ios_base::binary);
2983 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2984 os.write((char*)buf, 2);
2987 writeU16(buf, message.size());
2988 os.write((char*)buf, 2);
2991 for(u32 i=0; i<message.size(); i++)
2995 os.write((char*)buf, 2);
2999 std::string s = os.str();
3000 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3002 m_con.Send(peer_id, 0, data, true);
3005 void Server::BroadcastChatMessage(const std::wstring &message)
3007 for(core::map<u16, RemoteClient*>::Iterator
3008 i = m_clients.getIterator();
3009 i.atEnd() == false; i++)
3011 // Get client and check that it is valid
3012 RemoteClient *client = i.getNode()->getValue();
3013 assert(client->peer_id == i.getNode()->getKey());
3014 if(client->serialization_version == SER_FMT_VER_INVALID)
3017 SendChatMessage(client->peer_id, message);
3021 void Server::SendBlocks(float dtime)
3023 DSTACK(__FUNCTION_NAME);
3025 JMutexAutoLock envlock(m_env_mutex);
3027 core::array<PrioritySortedBlockTransfer> queue;
3029 s32 total_sending = 0;
3031 for(core::map<u16, RemoteClient*>::Iterator
3032 i = m_clients.getIterator();
3033 i.atEnd() == false; i++)
3035 RemoteClient *client = i.getNode()->getValue();
3036 assert(client->peer_id == i.getNode()->getKey());
3038 total_sending += client->SendingCount();
3040 if(client->serialization_version == SER_FMT_VER_INVALID)
3043 client->GetNextBlocks(this, dtime, queue);
3047 // Lowest priority number comes first.
3048 // Lowest is most important.
3051 JMutexAutoLock conlock(m_con_mutex);
3053 for(u32 i=0; i<queue.size(); i++)
3055 //TODO: Calculate limit dynamically
3056 if(total_sending >= g_settings.getS32
3057 ("max_simultaneous_block_sends_server_total"))
3060 PrioritySortedBlockTransfer q = queue[i];
3062 MapBlock *block = NULL;
3065 block = m_env.getMap().getBlockNoCreate(q.pos);
3067 catch(InvalidPositionException &e)
3072 RemoteClient *client = getClient(q.peer_id);
3074 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3076 client->SentBlock(q.pos);
3083 RemoteClient* Server::getClient(u16 peer_id)
3085 DSTACK(__FUNCTION_NAME);
3086 //JMutexAutoLock lock(m_con_mutex);
3087 core::map<u16, RemoteClient*>::Node *n;
3088 n = m_clients.find(peer_id);
3089 // A client should exist for all peers
3091 return n->getValue();
3094 Player *Server::emergePlayer(const char *name, const char *password)
3097 Try to get an existing player
3099 Player *player = m_env.getPlayer(name);
3110 player = new ServerRemotePlayer();
3111 //player->peer_id = c.peer_id;
3112 player->peer_id = PEER_ID_INEXISTENT;
3113 player->updateName(name);
3119 dstream<<"Server: Finding spawn place for player \""
3120 <<player->getName()<<"\""<<std::endl;
3124 f32 groundheight = 0;
3125 // Try to find a good place a few times
3126 for(s32 i=0; i<500; i++)
3129 // We're going to try to throw the player to this position
3130 nodepos = v2s16(-range + (myrand()%(range*2)),
3131 -range + (myrand()%(range*2)));
3132 v2s16 sectorpos = getNodeSectorPos(nodepos);
3134 m_env.getMap().emergeSector(sectorpos);
3135 // Get ground height at point
3136 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3137 // The sector should have been generated -> groundheight exists
3138 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3139 // Don't go underwater
3140 if(groundheight < WATER_LEVEL)
3142 //dstream<<"-> Underwater"<<std::endl;
3145 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3146 // Get block at point
3148 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3149 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3150 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3151 // Don't go inside ground
3153 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3154 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3155 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3156 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3157 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3158 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3160 dstream<<"-> Inside ground"<<std::endl;
3164 }catch(InvalidPositionException &e)
3166 dstream<<"-> Invalid position"<<std::endl;
3167 // Ignore invalid position
3171 // Found a good place
3172 dstream<<"Searched through "<<i<<" places."<<std::endl;
3177 // If no suitable place was not found, go above water at least.
3178 if(groundheight < WATER_LEVEL)
3179 groundheight = WATER_LEVEL;
3181 player->setPosition(intToFloat(v3s16(
3188 Add player to environment
3191 m_env.addPlayer(player);
3194 Add stuff to inventory
3197 if(g_settings.getBool("creative_mode"))
3199 // Give some good picks
3201 InventoryItem *item = new ToolItem("STPick", 0);
3202 void* r = player->inventory.addItem("main", item);
3206 InventoryItem *item = new ToolItem("MesePick", 0);
3207 void* r = player->inventory.addItem("main", item);
3214 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3217 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3218 player->inventory.addItem("main", item);
3221 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3223 // Skip some materials
3224 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3227 InventoryItem *item = new MaterialItem(i, 1);
3228 player->inventory.addItem("main", item);
3232 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3233 void* r = player->inventory.addItem("main", item);
3240 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3241 void* r = player->inventory.addItem("main", item);
3245 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3246 void* r = player->inventory.addItem("main", item);
3250 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3251 void* r = player->inventory.addItem("main", item);
3255 InventoryItem *item = new CraftItem("Stick", 4);
3256 void* r = player->inventory.addItem("main", item);
3260 InventoryItem *item = new ToolItem("WPick", 32000);
3261 void* r = player->inventory.addItem("main", item);
3265 InventoryItem *item = new ToolItem("STPick", 32000);
3266 void* r = player->inventory.addItem("main", item);
3269 /*// Give some lights
3271 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3272 bool r = player->inventory.addItem("main", item);
3276 for(u16 i=0; i<4; i++)
3278 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3279 bool r = player->inventory.addItem("main", item);
3282 /*// Give some other stuff
3284 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3285 bool r = player->inventory.addItem("main", item);
3295 void Server::UpdateBlockWaterPressure(MapBlock *block,
3296 core::map<v3s16, MapBlock*> &modified_blocks)
3298 MapVoxelManipulator v(&m_env.getMap());
3299 v.m_disable_water_climb =
3300 g_settings.getBool("disable_water_climb");
3302 VoxelArea area(block->getPosRelative(),
3303 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3307 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3309 catch(ProcessingLimitException &e)
3311 dstream<<"Processing limit reached (1)"<<std::endl;
3314 v.blitBack(modified_blocks);
3318 void Server::handlePeerChange(PeerChange &c)
3320 JMutexAutoLock envlock(m_env_mutex);
3321 JMutexAutoLock conlock(m_con_mutex);
3323 if(c.type == PEER_ADDED)
3330 core::map<u16, RemoteClient*>::Node *n;
3331 n = m_clients.find(c.peer_id);
3332 // The client shouldn't already exist
3336 RemoteClient *client = new RemoteClient();
3337 client->peer_id = c.peer_id;
3338 m_clients.insert(client->peer_id, client);
3341 else if(c.type == PEER_REMOVED)
3348 core::map<u16, RemoteClient*>::Node *n;
3349 n = m_clients.find(c.peer_id);
3350 // The client should exist
3353 // Collect information about leaving in chat
3354 std::wstring message;
3356 std::wstring name = L"unknown";
3357 Player *player = m_env.getPlayer(c.peer_id);
3359 name = narrow_to_wide(player->getName());
3363 message += L" left game";
3365 message += L" (timed out)";
3370 m_env.removePlayer(c.peer_id);
3373 // Set player client disconnected
3375 Player *player = m_env.getPlayer(c.peer_id);
3376 player->peer_id = 0;
3380 delete m_clients[c.peer_id];
3381 m_clients.remove(c.peer_id);
3383 // Send player info to all remaining clients
3386 // Send leave chat message to all remaining clients
3387 BroadcastChatMessage(message);
3396 void Server::handlePeerChanges()
3398 while(m_peer_change_queue.size() > 0)
3400 PeerChange c = m_peer_change_queue.pop_front();
3402 dout_server<<"Server: Handling peer change: "
3403 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3406 handlePeerChange(c);