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, "", peer_id);
1571 //Player *player = m_env.getPlayer(peer_id);
1573 // If failed, cancel
1576 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1577 <<": failed to emerge player"<<std::endl;
1582 // If a client is already connected to the player, cancel
1583 if(player->peer_id != 0)
1585 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1586 <<" tried to connect to "
1587 "an already connected player (peer_id="
1588 <<player->peer_id<<")"<<std::endl;
1591 // Set client of player
1592 player->peer_id = peer_id;
1595 // Check if player doesn't exist
1597 throw con::InvalidIncomingDataException
1598 ("Server::ProcessData(): INIT: Player doesn't exist");
1600 /*// update name if it was supplied
1601 if(datasize >= 20+3)
1604 player->updateName((const char*)&data[3]);
1607 // Now answer with a TOCLIENT_INIT
1609 SharedBuffer<u8> reply(2+1+6);
1610 writeU16(&reply[0], TOCLIENT_INIT);
1611 writeU8(&reply[2], deployed);
1612 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1614 m_con.Send(peer_id, 0, reply, true);
1618 if(command == TOSERVER_INIT2)
1620 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1621 <<peer->id<<std::endl;
1624 getClient(peer->id)->serialization_version
1625 = getClient(peer->id)->pending_serialization_version;
1628 Send some initialization data
1631 // Send player info to all players
1634 // Send inventory to player
1635 SendInventory(peer->id);
1639 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1640 m_time_of_day.get());
1641 m_con.Send(peer->id, 0, data, true);
1644 // Send information about server to player in chat
1646 std::wostringstream os(std::ios_base::binary);
1649 os<<L"uptime="<<m_uptime.get();
1650 // Information about clients
1652 for(core::map<u16, RemoteClient*>::Iterator
1653 i = m_clients.getIterator();
1654 i.atEnd() == false; i++)
1656 // Get client and check that it is valid
1657 RemoteClient *client = i.getNode()->getValue();
1658 assert(client->peer_id == i.getNode()->getKey());
1659 if(client->serialization_version == SER_FMT_VER_INVALID)
1662 Player *player = m_env.getPlayer(client->peer_id);
1663 // Get name of player
1664 std::wstring name = L"unknown";
1666 name = narrow_to_wide(player->getName());
1667 // Add name to information string
1672 SendChatMessage(peer_id, os.str());
1675 // Send information about joining in chat
1677 std::wstring name = L"unknown";
1678 Player *player = m_env.getPlayer(peer_id);
1680 name = narrow_to_wide(player->getName());
1682 std::wstring message;
1685 message += L" joined game";
1686 BroadcastChatMessage(message);
1692 if(peer_ser_ver == SER_FMT_VER_INVALID)
1694 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1695 " serialization format invalid or not initialized."
1696 " Skipping incoming command="<<command<<std::endl;
1700 Player *player = m_env.getPlayer(peer_id);
1703 derr_server<<"Server::ProcessData(): Cancelling: "
1704 "No player for peer_id="<<peer_id
1708 if(command == TOSERVER_PLAYERPOS)
1710 if(datasize < 2+12+12+4+4)
1714 v3s32 ps = readV3S32(&data[start+2]);
1715 v3s32 ss = readV3S32(&data[start+2+12]);
1716 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1717 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1718 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1719 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1720 pitch = wrapDegrees(pitch);
1721 yaw = wrapDegrees(yaw);
1722 player->setPosition(position);
1723 player->setSpeed(speed);
1724 player->setPitch(pitch);
1725 player->setYaw(yaw);
1727 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1728 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1729 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1731 else if(command == TOSERVER_GOTBLOCKS)
1744 u16 count = data[2];
1745 for(u16 i=0; i<count; i++)
1747 if((s16)datasize < 2+1+(i+1)*6)
1748 throw con::InvalidIncomingDataException
1749 ("GOTBLOCKS length is too short");
1750 v3s16 p = readV3S16(&data[2+1+i*6]);
1751 /*dstream<<"Server: GOTBLOCKS ("
1752 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1753 RemoteClient *client = getClient(peer_id);
1754 client->GotBlock(p);
1757 else if(command == TOSERVER_DELETEDBLOCKS)
1770 u16 count = data[2];
1771 for(u16 i=0; i<count; i++)
1773 if((s16)datasize < 2+1+(i+1)*6)
1774 throw con::InvalidIncomingDataException
1775 ("DELETEDBLOCKS length is too short");
1776 v3s16 p = readV3S16(&data[2+1+i*6]);
1777 /*dstream<<"Server: DELETEDBLOCKS ("
1778 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1779 RemoteClient *client = getClient(peer_id);
1780 client->SetBlockNotSent(p);
1783 else if(command == TOSERVER_CLICK_OBJECT)
1790 [2] u8 button (0=left, 1=right)
1795 u8 button = readU8(&data[2]);
1797 p.X = readS16(&data[3]);
1798 p.Y = readS16(&data[5]);
1799 p.Z = readS16(&data[7]);
1800 s16 id = readS16(&data[9]);
1801 //u16 item_i = readU16(&data[11]);
1803 MapBlock *block = NULL;
1806 block = m_env.getMap().getBlockNoCreate(p);
1808 catch(InvalidPositionException &e)
1810 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1814 MapBlockObject *obj = block->getObject(id);
1818 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1822 //TODO: Check that object is reasonably close
1827 InventoryList *ilist = player->inventory.getList("main");
1828 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1831 // Skip if inventory has no free space
1832 if(ilist->getUsedSlots() == ilist->getSize())
1834 dout_server<<"Player inventory has no free space"<<std::endl;
1839 Create the inventory item
1841 InventoryItem *item = NULL;
1842 // If it is an item-object, take the item from it
1843 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1845 item = ((ItemObject*)obj)->createInventoryItem();
1847 // Else create an item of the object
1850 item = new MapBlockObjectItem
1851 (obj->getInventoryString());
1854 // Add to inventory and send inventory
1855 ilist->addItem(item);
1856 SendInventory(player->peer_id);
1859 // Remove from block
1860 block->removeObject(id);
1863 else if(command == TOSERVER_GROUND_ACTION)
1871 [3] v3s16 nodepos_undersurface
1872 [9] v3s16 nodepos_abovesurface
1877 2: stop digging (all parameters ignored)
1879 u8 action = readU8(&data[2]);
1881 p_under.X = readS16(&data[3]);
1882 p_under.Y = readS16(&data[5]);
1883 p_under.Z = readS16(&data[7]);
1885 p_over.X = readS16(&data[9]);
1886 p_over.Y = readS16(&data[11]);
1887 p_over.Z = readS16(&data[13]);
1888 u16 item_i = readU16(&data[15]);
1890 //TODO: Check that target is reasonably close
1898 NOTE: This can be used in the future to check if
1899 somebody is cheating, by checking the timing.
1906 else if(action == 2)
1909 RemoteClient *client = getClient(peer->id);
1910 JMutexAutoLock digmutex(client->m_dig_mutex);
1911 client->m_dig_tool_item = -1;
1916 3: Digging completed
1918 else if(action == 3)
1920 // Mandatory parameter; actually used for nothing
1921 core::map<v3s16, MapBlock*> modified_blocks;
1927 // Get material at position
1928 material = m_env.getMap().getNode(p_under).d;
1929 // If it's not diggable, do nothing
1930 if(content_diggable(material) == false)
1932 derr_server<<"Server: Not finishing digging: Node not diggable"
1937 catch(InvalidPositionException &e)
1939 derr_server<<"Server: Not finishing digging: Node not found"
1944 //TODO: Send to only other clients
1947 Send the removal to all other clients
1952 SharedBuffer<u8> reply(replysize);
1953 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1954 writeS16(&reply[2], p_under.X);
1955 writeS16(&reply[4], p_under.Y);
1956 writeS16(&reply[6], p_under.Z);
1958 for(core::map<u16, RemoteClient*>::Iterator
1959 i = m_clients.getIterator();
1960 i.atEnd() == false; i++)
1962 // Get client and check that it is valid
1963 RemoteClient *client = i.getNode()->getValue();
1964 assert(client->peer_id == i.getNode()->getKey());
1965 if(client->serialization_version == SER_FMT_VER_INVALID)
1968 // Don't send if it's the same one
1969 if(peer_id == client->peer_id)
1973 m_con.Send(client->peer_id, 0, reply, true);
1977 Update and send inventory
1980 if(g_settings.getBool("creative_mode") == false)
1985 InventoryList *mlist = player->inventory.getList("main");
1988 InventoryItem *item = mlist->getItem(item_i);
1989 if(item && (std::string)item->getName() == "ToolItem")
1991 ToolItem *titem = (ToolItem*)item;
1992 std::string toolname = titem->getToolName();
1994 // Get digging properties for material and tool
1995 DiggingProperties prop =
1996 getDiggingProperties(material, toolname);
1998 if(prop.diggable == false)
2000 derr_server<<"Server: WARNING: Player digged"
2001 <<" with impossible material + tool"
2002 <<" combination"<<std::endl;
2005 bool weared_out = titem->addWear(prop.wear);
2009 mlist->deleteItem(item_i);
2015 Add digged item to inventory
2017 InventoryItem *item = new MaterialItem(material, 1);
2018 player->inventory.addItem("main", item);
2023 SendInventory(player->peer_id);
2028 (this takes some time so it is done after the quick stuff)
2030 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2037 // Update water pressure around modification
2038 // This also adds it to m_flow_active_nodes if appropriate
2040 MapVoxelManipulator v(&m_env.getMap());
2041 v.m_disable_water_climb =
2042 g_settings.getBool("disable_water_climb");
2044 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2048 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2050 catch(ProcessingLimitException &e)
2052 dstream<<"Processing limit reached (1)"<<std::endl;
2055 v.blitBack(modified_blocks);
2062 else if(action == 1)
2065 InventoryList *ilist = player->inventory.getList("main");
2070 InventoryItem *item = ilist->getItem(item_i);
2072 // If there is no item, it is not possible to add it anywhere
2077 Handle material items
2079 if(std::string("MaterialItem") == item->getName())
2082 // Don't add a node if this is not a free space
2083 MapNode n2 = m_env.getMap().getNode(p_over);
2084 if(content_buildable_to(n2.d) == false)
2087 catch(InvalidPositionException &e)
2089 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2094 // Reset build time counter
2095 getClient(peer->id)->m_time_from_building.set(0.0);
2098 MaterialItem *mitem = (MaterialItem*)item;
2100 n.d = mitem->getMaterial();
2101 if(content_directional(n.d))
2102 n.dir = packDir(p_under - p_over);
2106 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2107 SharedBuffer<u8> reply(replysize);
2108 writeU16(&reply[0], TOCLIENT_ADDNODE);
2109 writeS16(&reply[2], p_over.X);
2110 writeS16(&reply[4], p_over.Y);
2111 writeS16(&reply[6], p_over.Z);
2112 n.serialize(&reply[8], peer_ser_ver);
2114 m_con.SendToAll(0, reply, true);
2119 InventoryList *ilist = player->inventory.getList("main");
2120 if(g_settings.getBool("creative_mode") == false && ilist)
2122 // Remove from inventory and send inventory
2123 if(mitem->getCount() == 1)
2124 ilist->deleteItem(item_i);
2128 SendInventory(peer_id);
2134 This takes some time so it is done after the quick stuff
2136 core::map<v3s16, MapBlock*> modified_blocks;
2137 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2143 InventoryList *ilist = player->inventory.getList("main");
2144 if(g_settings.getBool("creative_mode") == false && ilist)
2146 // Remove from inventory and send inventory
2147 if(mitem->getCount() == 1)
2148 ilist->deleteItem(item_i);
2152 SendInventory(peer_id);
2158 This takes some time so it is done after the quick stuff
2160 core::map<v3s16, MapBlock*> modified_blocks;
2161 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2164 Set the modified blocks unsent for all the clients
2167 //JMutexAutoLock lock2(m_con_mutex);
2169 for(core::map<u16, RemoteClient*>::Iterator
2170 i = m_clients.getIterator();
2171 i.atEnd() == false; i++)
2173 RemoteClient *client = i.getNode()->getValue();
2175 if(modified_blocks.size() > 0)
2177 // Remove block from sent history
2178 client->SetBlocksNotSent(modified_blocks);
2188 // Update water pressure around modification
2189 // This also adds it to m_flow_active_nodes if appropriate
2191 MapVoxelManipulator v(&m_env.getMap());
2192 v.m_disable_water_climb =
2193 g_settings.getBool("disable_water_climb");
2195 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2199 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2201 catch(ProcessingLimitException &e)
2203 dstream<<"Processing limit reached (1)"<<std::endl;
2206 v.blitBack(modified_blocks);
2214 v3s16 blockpos = getNodeBlockPos(p_over);
2216 MapBlock *block = NULL;
2219 block = m_env.getMap().getBlockNoCreate(blockpos);
2221 catch(InvalidPositionException &e)
2223 derr_server<<"Error while placing object: "
2224 "block not found"<<std::endl;
2228 v3s16 block_pos_i_on_map = block->getPosRelative();
2229 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2231 v3f pos = intToFloat(p_over);
2232 pos -= block_pos_f_on_map;
2234 /*dout_server<<"pos="
2235 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2238 MapBlockObject *obj = NULL;
2241 Handle block object items
2243 if(std::string("MBOItem") == item->getName())
2245 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2247 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2248 "inventorystring=\""
2249 <<oitem->getInventoryString()
2250 <<"\""<<std::endl;*/
2252 obj = oitem->createObject
2253 (pos, player->getYaw(), player->getPitch());
2260 dout_server<<"Placing a miscellaneous item on map"
2263 Create an ItemObject that contains the item.
2265 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2266 std::ostringstream os(std::ios_base::binary);
2267 item->serialize(os);
2268 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2269 iobj->setItemString(os.str());
2275 derr_server<<"WARNING: item resulted in NULL object, "
2276 <<"not placing onto map"
2281 block->addObject(obj);
2283 dout_server<<"Placed object"<<std::endl;
2285 InventoryList *ilist = player->inventory.getList("main");
2286 if(g_settings.getBool("creative_mode") == false && ilist)
2288 // Remove from inventory and send inventory
2289 ilist->deleteItem(item_i);
2291 SendInventory(peer_id);
2299 Catch invalid actions
2303 derr_server<<"WARNING: Server: Invalid action "
2304 <<action<<std::endl;
2308 else if(command == TOSERVER_RELEASE)
2317 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2320 else if(command == TOSERVER_SIGNTEXT)
2329 std::string datastring((char*)&data[2], datasize-2);
2330 std::istringstream is(datastring, std::ios_base::binary);
2333 is.read((char*)buf, 6);
2334 v3s16 blockpos = readV3S16(buf);
2335 is.read((char*)buf, 2);
2336 s16 id = readS16(buf);
2337 is.read((char*)buf, 2);
2338 u16 textlen = readU16(buf);
2340 for(u16 i=0; i<textlen; i++)
2342 is.read((char*)buf, 1);
2343 text += (char)buf[0];
2346 MapBlock *block = NULL;
2349 block = m_env.getMap().getBlockNoCreate(blockpos);
2351 catch(InvalidPositionException &e)
2353 derr_server<<"Error while setting sign text: "
2354 "block not found"<<std::endl;
2358 MapBlockObject *obj = block->getObject(id);
2361 derr_server<<"Error while setting sign text: "
2362 "object not found"<<std::endl;
2366 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2368 derr_server<<"Error while setting sign text: "
2369 "object is not a sign"<<std::endl;
2373 ((SignObject*)obj)->setText(text);
2375 obj->getBlock()->setChangedFlag();
2377 else if(command == TOSERVER_INVENTORY_ACTION)
2379 /*// Ignore inventory changes if in creative mode
2380 if(g_settings.getBool("creative_mode") == true)
2382 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2386 // Strip command and create a stream
2387 std::string datastring((char*)&data[2], datasize-2);
2388 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2389 std::istringstream is(datastring, std::ios_base::binary);
2391 InventoryAction *a = InventoryAction::deSerialize(is);
2395 Handle craftresult specially if not in creative mode
2397 bool disable_action = false;
2398 if(a->getType() == IACTION_MOVE
2399 && g_settings.getBool("creative_mode") == false)
2401 IMoveAction *ma = (IMoveAction*)a;
2402 // Don't allow moving anything to craftresult
2403 if(ma->to_name == "craftresult")
2406 disable_action = true;
2408 // When something is removed from craftresult
2409 if(ma->from_name == "craftresult")
2411 disable_action = true;
2412 // Remove stuff from craft
2413 InventoryList *clist = player->inventory.getList("craft");
2416 u16 count = ma->count;
2419 clist->decrementMaterials(count);
2422 // Feed action to player inventory
2423 a->apply(&player->inventory);
2426 // If something appeared in craftresult, throw it
2428 InventoryList *rlist = player->inventory.getList("craftresult");
2429 InventoryList *mlist = player->inventory.getList("main");
2430 if(rlist && mlist && rlist->getUsedSlots() == 1)
2432 InventoryItem *item1 = rlist->changeItem(0, NULL);
2433 mlist->addItem(item1);
2437 if(disable_action == false)
2439 // Feed action to player inventory
2440 a->apply(&player->inventory);
2445 SendInventory(player->peer_id);
2449 dstream<<"TOSERVER_INVENTORY_ACTION: "
2450 <<"InventoryAction::deSerialize() returned NULL"
2454 else if(command == TOSERVER_CHAT_MESSAGE)
2462 std::string datastring((char*)&data[2], datasize-2);
2463 std::istringstream is(datastring, std::ios_base::binary);
2466 is.read((char*)buf, 2);
2467 u16 len = readU16(buf);
2469 std::wstring message;
2470 for(u16 i=0; i<len; i++)
2472 is.read((char*)buf, 2);
2473 message += (wchar_t)readU16(buf);
2476 // Get player name of this client
2477 std::wstring name = narrow_to_wide(player->getName());
2479 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2481 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2484 Send the message to all other clients
2486 for(core::map<u16, RemoteClient*>::Iterator
2487 i = m_clients.getIterator();
2488 i.atEnd() == false; i++)
2490 // Get client and check that it is valid
2491 RemoteClient *client = i.getNode()->getValue();
2492 assert(client->peer_id == i.getNode()->getKey());
2493 if(client->serialization_version == SER_FMT_VER_INVALID)
2496 // Don't send if it's the same one
2497 if(peer_id == client->peer_id)
2500 SendChatMessage(client->peer_id, line);
2505 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2506 "unknown command "<<command<<std::endl;
2510 catch(SendFailedException &e)
2512 derr_server<<"Server::ProcessData(): SendFailedException: "
2518 /*void Server::Send(u16 peer_id, u16 channelnum,
2519 SharedBuffer<u8> data, bool reliable)
2521 JMutexAutoLock lock(m_con_mutex);
2522 m_con.Send(peer_id, channelnum, data, reliable);
2525 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2527 DSTACK(__FUNCTION_NAME);
2529 Create a packet with the block in the right format
2532 std::ostringstream os(std::ios_base::binary);
2533 block->serialize(os, ver);
2534 std::string s = os.str();
2535 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2537 u32 replysize = 8 + blockdata.getSize();
2538 SharedBuffer<u8> reply(replysize);
2539 v3s16 p = block->getPos();
2540 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2541 writeS16(&reply[2], p.X);
2542 writeS16(&reply[4], p.Y);
2543 writeS16(&reply[6], p.Z);
2544 memcpy(&reply[8], *blockdata, blockdata.getSize());
2546 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2547 <<": \tpacket size: "<<replysize<<std::endl;*/
2552 m_con.Send(peer_id, 1, reply, true);
2555 core::list<PlayerInfo> Server::getPlayerInfo()
2557 DSTACK(__FUNCTION_NAME);
2558 JMutexAutoLock envlock(m_env_mutex);
2559 JMutexAutoLock conlock(m_con_mutex);
2561 core::list<PlayerInfo> list;
2563 core::list<Player*> players = m_env.getPlayers();
2565 core::list<Player*>::Iterator i;
2566 for(i = players.begin();
2567 i != players.end(); i++)
2571 Player *player = *i;
2574 con::Peer *peer = m_con.GetPeer(player->peer_id);
2575 // Copy info from peer to info struct
2577 info.address = peer->address;
2578 info.avg_rtt = peer->avg_rtt;
2580 catch(con::PeerNotFoundException &e)
2582 // Set dummy peer info
2584 info.address = Address(0,0,0,0,0);
2588 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2589 info.position = player->getPosition();
2591 list.push_back(info);
2597 void Server::peerAdded(con::Peer *peer)
2599 DSTACK(__FUNCTION_NAME);
2600 dout_server<<"Server::peerAdded(): peer->id="
2601 <<peer->id<<std::endl;
2604 c.type = PEER_ADDED;
2605 c.peer_id = peer->id;
2607 m_peer_change_queue.push_back(c);
2610 void Server::deletingPeer(con::Peer *peer, bool timeout)
2612 DSTACK(__FUNCTION_NAME);
2613 dout_server<<"Server::deletingPeer(): peer->id="
2614 <<peer->id<<", timeout="<<timeout<<std::endl;
2617 c.type = PEER_REMOVED;
2618 c.peer_id = peer->id;
2619 c.timeout = timeout;
2620 m_peer_change_queue.push_back(c);
2623 void Server::SendObjectData(float dtime)
2625 DSTACK(__FUNCTION_NAME);
2627 core::map<v3s16, bool> stepped_blocks;
2629 for(core::map<u16, RemoteClient*>::Iterator
2630 i = m_clients.getIterator();
2631 i.atEnd() == false; i++)
2633 u16 peer_id = i.getNode()->getKey();
2634 RemoteClient *client = i.getNode()->getValue();
2635 assert(client->peer_id == peer_id);
2637 if(client->serialization_version == SER_FMT_VER_INVALID)
2640 client->SendObjectData(this, dtime, stepped_blocks);
2644 void Server::SendPlayerInfos()
2646 DSTACK(__FUNCTION_NAME);
2648 //JMutexAutoLock envlock(m_env_mutex);
2650 // Get connected players
2651 core::list<Player*> players = m_env.getPlayers(true);
2653 u32 player_count = players.getSize();
2654 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2656 SharedBuffer<u8> data(datasize);
2657 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2660 core::list<Player*>::Iterator i;
2661 for(i = players.begin();
2662 i != players.end(); i++)
2664 Player *player = *i;
2666 /*dstream<<"Server sending player info for player with "
2667 "peer_id="<<player->peer_id<<std::endl;*/
2669 writeU16(&data[start], player->peer_id);
2670 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2671 start += 2+PLAYERNAME_SIZE;
2674 //JMutexAutoLock conlock(m_con_mutex);
2677 m_con.SendToAll(0, data, true);
2695 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2701 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2707 enum ItemSpecType type;
2708 // Only other one of these is used
2714 items: a pointer to an array of 9 pointers to items
2715 specs: a pointer to an array of 9 ItemSpecs
2717 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2719 u16 items_min_x = 100;
2720 u16 items_max_x = 100;
2721 u16 items_min_y = 100;
2722 u16 items_max_y = 100;
2723 for(u16 y=0; y<3; y++)
2724 for(u16 x=0; x<3; x++)
2726 if(items[y*3 + x] == NULL)
2728 if(items_min_x == 100 || x < items_min_x)
2730 if(items_min_y == 100 || y < items_min_y)
2732 if(items_max_x == 100 || x > items_max_x)
2734 if(items_max_y == 100 || y > items_max_y)
2737 // No items at all, just return false
2738 if(items_min_x == 100)
2741 u16 items_w = items_max_x - items_min_x + 1;
2742 u16 items_h = items_max_y - items_min_y + 1;
2744 u16 specs_min_x = 100;
2745 u16 specs_max_x = 100;
2746 u16 specs_min_y = 100;
2747 u16 specs_max_y = 100;
2748 for(u16 y=0; y<3; y++)
2749 for(u16 x=0; x<3; x++)
2751 if(specs[y*3 + x].type == ITEM_NONE)
2753 if(specs_min_x == 100 || x < specs_min_x)
2755 if(specs_min_y == 100 || y < specs_min_y)
2757 if(specs_max_x == 100 || x > specs_max_x)
2759 if(specs_max_y == 100 || y > specs_max_y)
2762 // No specs at all, just return false
2763 if(specs_min_x == 100)
2766 u16 specs_w = specs_max_x - specs_min_x + 1;
2767 u16 specs_h = specs_max_y - specs_min_y + 1;
2770 if(items_w != specs_w || items_h != specs_h)
2773 for(u16 y=0; y<specs_h; y++)
2774 for(u16 x=0; x<specs_w; x++)
2776 u16 items_x = items_min_x + x;
2777 u16 items_y = items_min_y + y;
2778 u16 specs_x = specs_min_x + x;
2779 u16 specs_y = specs_min_y + y;
2780 InventoryItem *item = items[items_y * 3 + items_x];
2781 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2783 if(spec.type == ITEM_NONE)
2785 // Has to be no item
2791 // There should be an item
2795 std::string itemname = item->getName();
2797 if(spec.type == ITEM_MATERIAL)
2799 if(itemname != "MaterialItem")
2801 MaterialItem *mitem = (MaterialItem*)item;
2802 if(mitem->getMaterial() != spec.num)
2805 else if(spec.type == ITEM_CRAFT)
2807 if(itemname != "CraftItem")
2809 CraftItem *mitem = (CraftItem*)item;
2810 if(mitem->getSubName() != spec.name)
2813 else if(spec.type == ITEM_TOOL)
2815 // Not supported yet
2818 else if(spec.type == ITEM_MBO)
2820 // Not supported yet
2825 // Not supported yet
2833 void Server::SendInventory(u16 peer_id)
2835 DSTACK(__FUNCTION_NAME);
2837 Player* player = m_env.getPlayer(peer_id);
2840 Calculate crafting stuff
2842 if(g_settings.getBool("creative_mode") == false)
2844 InventoryList *clist = player->inventory.getList("craft");
2845 InventoryList *rlist = player->inventory.getList("craftresult");
2848 rlist->clearItems();
2852 InventoryItem *items[9];
2853 for(u16 i=0; i<9; i++)
2855 items[i] = clist->getItem(i);
2864 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2865 if(checkItemCombination(items, specs))
2867 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2876 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2877 if(checkItemCombination(items, specs))
2879 rlist->addItem(new CraftItem("Stick", 4));
2888 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2889 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2890 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2891 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2892 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2893 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2894 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2895 if(checkItemCombination(items, specs))
2897 rlist->addItem(new MapBlockObjectItem("Sign"));
2906 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2907 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2908 if(checkItemCombination(items, specs))
2910 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2919 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2920 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2921 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2922 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2923 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2924 if(checkItemCombination(items, specs))
2926 rlist->addItem(new ToolItem("WPick", 0));
2935 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2936 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2937 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2938 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2939 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2940 if(checkItemCombination(items, specs))
2942 rlist->addItem(new ToolItem("STPick", 0));
2951 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2952 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2953 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2954 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2955 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2956 if(checkItemCombination(items, specs))
2958 rlist->addItem(new ToolItem("MesePick", 0));
2963 } // if creative_mode == false
2969 std::ostringstream os;
2970 //os.imbue(std::locale("C"));
2972 player->inventory.serialize(os);
2974 std::string s = os.str();
2976 SharedBuffer<u8> data(s.size()+2);
2977 writeU16(&data[0], TOCLIENT_INVENTORY);
2978 memcpy(&data[2], s.c_str(), s.size());
2981 m_con.Send(peer_id, 0, data, true);
2984 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2986 DSTACK(__FUNCTION_NAME);
2988 std::ostringstream os(std::ios_base::binary);
2992 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2993 os.write((char*)buf, 2);
2996 writeU16(buf, message.size());
2997 os.write((char*)buf, 2);
3000 for(u32 i=0; i<message.size(); i++)
3004 os.write((char*)buf, 2);
3008 std::string s = os.str();
3009 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3011 m_con.Send(peer_id, 0, data, true);
3014 void Server::BroadcastChatMessage(const std::wstring &message)
3016 for(core::map<u16, RemoteClient*>::Iterator
3017 i = m_clients.getIterator();
3018 i.atEnd() == false; i++)
3020 // Get client and check that it is valid
3021 RemoteClient *client = i.getNode()->getValue();
3022 assert(client->peer_id == i.getNode()->getKey());
3023 if(client->serialization_version == SER_FMT_VER_INVALID)
3026 SendChatMessage(client->peer_id, message);
3030 void Server::SendBlocks(float dtime)
3032 DSTACK(__FUNCTION_NAME);
3034 JMutexAutoLock envlock(m_env_mutex);
3036 core::array<PrioritySortedBlockTransfer> queue;
3038 s32 total_sending = 0;
3040 for(core::map<u16, RemoteClient*>::Iterator
3041 i = m_clients.getIterator();
3042 i.atEnd() == false; i++)
3044 RemoteClient *client = i.getNode()->getValue();
3045 assert(client->peer_id == i.getNode()->getKey());
3047 total_sending += client->SendingCount();
3049 if(client->serialization_version == SER_FMT_VER_INVALID)
3052 client->GetNextBlocks(this, dtime, queue);
3056 // Lowest priority number comes first.
3057 // Lowest is most important.
3060 JMutexAutoLock conlock(m_con_mutex);
3062 for(u32 i=0; i<queue.size(); i++)
3064 //TODO: Calculate limit dynamically
3065 if(total_sending >= g_settings.getS32
3066 ("max_simultaneous_block_sends_server_total"))
3069 PrioritySortedBlockTransfer q = queue[i];
3071 MapBlock *block = NULL;
3074 block = m_env.getMap().getBlockNoCreate(q.pos);
3076 catch(InvalidPositionException &e)
3081 RemoteClient *client = getClient(q.peer_id);
3083 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3085 client->SentBlock(q.pos);
3092 RemoteClient* Server::getClient(u16 peer_id)
3094 DSTACK(__FUNCTION_NAME);
3095 //JMutexAutoLock lock(m_con_mutex);
3096 core::map<u16, RemoteClient*>::Node *n;
3097 n = m_clients.find(peer_id);
3098 // A client should exist for all peers
3100 return n->getValue();
3103 Player *Server::emergePlayer(const char *name, const char *password,
3107 Try to get an existing player
3109 Player *player = m_env.getPlayer(name);
3112 // If player is already connected, cancel
3113 if(player->peer_id != 0)
3115 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3123 If player with the wanted peer_id already exists, cancel.
3125 if(m_env.getPlayer(peer_id) != NULL)
3127 dstream<<"emergePlayer(): Player with wrong name but same"
3128 " peer_id already exists"<<std::endl;
3136 player = new ServerRemotePlayer();
3137 //player->peer_id = c.peer_id;
3138 //player->peer_id = PEER_ID_INEXISTENT;
3139 player->peer_id = peer_id;
3140 player->updateName(name);
3146 dstream<<"Server: Finding spawn place for player \""
3147 <<player->getName()<<"\""<<std::endl;
3151 f32 groundheight = 0;
3152 // Try to find a good place a few times
3153 for(s32 i=0; i<500; i++)
3156 // We're going to try to throw the player to this position
3157 nodepos = v2s16(-range + (myrand()%(range*2)),
3158 -range + (myrand()%(range*2)));
3159 v2s16 sectorpos = getNodeSectorPos(nodepos);
3161 m_env.getMap().emergeSector(sectorpos);
3162 // Get ground height at point
3163 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3164 // The sector should have been generated -> groundheight exists
3165 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3166 // Don't go underwater
3167 if(groundheight < WATER_LEVEL)
3169 //dstream<<"-> Underwater"<<std::endl;
3172 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3173 // Get block at point
3175 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3176 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3177 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3178 // Don't go inside ground
3180 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3181 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3182 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3183 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3184 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3185 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3187 dstream<<"-> Inside ground"<<std::endl;
3191 }catch(InvalidPositionException &e)
3193 dstream<<"-> Invalid position"<<std::endl;
3194 // Ignore invalid position
3198 // Found a good place
3199 dstream<<"Searched through "<<i<<" places."<<std::endl;
3204 // If no suitable place was not found, go above water at least.
3205 if(groundheight < WATER_LEVEL)
3206 groundheight = WATER_LEVEL;
3208 player->setPosition(intToFloat(v3s16(
3215 Add player to environment
3218 m_env.addPlayer(player);
3221 Add stuff to inventory
3224 if(g_settings.getBool("creative_mode"))
3226 // Give some good picks
3228 InventoryItem *item = new ToolItem("STPick", 0);
3229 void* r = player->inventory.addItem("main", item);
3233 InventoryItem *item = new ToolItem("MesePick", 0);
3234 void* r = player->inventory.addItem("main", item);
3241 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3244 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3245 player->inventory.addItem("main", item);
3248 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3250 // Skip some materials
3251 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3254 InventoryItem *item = new MaterialItem(i, 1);
3255 player->inventory.addItem("main", item);
3259 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3260 void* r = player->inventory.addItem("main", item);
3267 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3268 void* r = player->inventory.addItem("main", item);
3272 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3273 void* r = player->inventory.addItem("main", item);
3277 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3278 void* r = player->inventory.addItem("main", item);
3282 InventoryItem *item = new CraftItem("Stick", 4);
3283 void* r = player->inventory.addItem("main", item);
3287 InventoryItem *item = new ToolItem("WPick", 32000);
3288 void* r = player->inventory.addItem("main", item);
3292 InventoryItem *item = new ToolItem("STPick", 32000);
3293 void* r = player->inventory.addItem("main", item);
3296 /*// Give some lights
3298 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3299 bool r = player->inventory.addItem("main", item);
3303 for(u16 i=0; i<4; i++)
3305 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3306 bool r = player->inventory.addItem("main", item);
3309 /*// Give some other stuff
3311 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3312 bool r = player->inventory.addItem("main", item);
3319 } // create new player
3323 void Server::UpdateBlockWaterPressure(MapBlock *block,
3324 core::map<v3s16, MapBlock*> &modified_blocks)
3326 MapVoxelManipulator v(&m_env.getMap());
3327 v.m_disable_water_climb =
3328 g_settings.getBool("disable_water_climb");
3330 VoxelArea area(block->getPosRelative(),
3331 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3335 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3337 catch(ProcessingLimitException &e)
3339 dstream<<"Processing limit reached (1)"<<std::endl;
3342 v.blitBack(modified_blocks);
3346 void Server::handlePeerChange(PeerChange &c)
3348 JMutexAutoLock envlock(m_env_mutex);
3349 JMutexAutoLock conlock(m_con_mutex);
3351 if(c.type == PEER_ADDED)
3358 core::map<u16, RemoteClient*>::Node *n;
3359 n = m_clients.find(c.peer_id);
3360 // The client shouldn't already exist
3364 RemoteClient *client = new RemoteClient();
3365 client->peer_id = c.peer_id;
3366 m_clients.insert(client->peer_id, client);
3369 else if(c.type == PEER_REMOVED)
3376 core::map<u16, RemoteClient*>::Node *n;
3377 n = m_clients.find(c.peer_id);
3378 // The client should exist
3381 // Collect information about leaving in chat
3382 std::wstring message;
3384 std::wstring name = L"unknown";
3385 Player *player = m_env.getPlayer(c.peer_id);
3387 name = narrow_to_wide(player->getName());
3391 message += L" left game";
3393 message += L" (timed out)";
3398 m_env.removePlayer(c.peer_id);
3401 // Set player client disconnected
3403 Player *player = m_env.getPlayer(c.peer_id);
3404 player->peer_id = 0;
3408 delete m_clients[c.peer_id];
3409 m_clients.remove(c.peer_id);
3411 // Send player info to all remaining clients
3414 // Send leave chat message to all remaining clients
3415 BroadcastChatMessage(message);
3424 void Server::handlePeerChanges()
3426 while(m_peer_change_queue.size() > 0)
3428 PeerChange c = m_peer_change_queue.pop_front();
3430 dout_server<<"Server: Handling peer change: "
3431 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3434 handlePeerChange(c);