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)
56 catch(con::PeerNotFoundException &e)
58 dout_server<<"Server: PeerNotFoundException"<<std::endl;
62 END_DEBUG_EXCEPTION_HANDLER
67 void * EmergeThread::Thread()
71 DSTACK(__FUNCTION_NAME);
75 BEGIN_DEBUG_EXCEPTION_HANDLER
78 Get block info from queue, emerge them and send them
81 After queue is empty, exit.
85 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
89 SharedPtr<QueuedBlockEmerge> q(qptr);
93 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
95 //TimeTaker timer("block emerge");
98 Try to emerge it from somewhere.
100 If it is only wanted as optional, only loading from disk
105 Check if any peer wants it as non-optional. In that case it
108 Also decrement the emerge queue count in clients.
111 bool optional = true;
114 core::map<u16, u8>::Iterator i;
115 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
117 //u16 peer_id = i.getNode()->getKey();
120 u8 flags = i.getNode()->getValue();
121 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
127 /*dstream<<"EmergeThread: p="
128 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
129 <<"optional="<<optional<<std::endl;*/
131 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
133 core::map<v3s16, MapBlock*> changed_blocks;
134 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
136 MapBlock *block = NULL;
137 bool got_block = true;
138 core::map<v3s16, MapBlock*> modified_blocks;
142 //TimeTaker envlockwaittimer("block emerge envlock wait time");
145 JMutexAutoLock envlock(m_server->m_env_mutex);
147 //envlockwaittimer.stop();
149 //TimeTaker timer("block emerge (while env locked)");
152 bool only_from_disk = false;
155 only_from_disk = true;
157 // First check if the block already exists
160 block = map.getBlockNoCreate(p);
165 block = map.emergeBlock(
169 lighting_invalidated_blocks);
173 EXPERIMENTAL: Create a few other blocks too
180 lighting_invalidated_blocks);
186 lighting_invalidated_blocks);
192 lighting_invalidated_blocks);
198 lighting_invalidated_blocks);
203 // If it is a dummy, block was not found on disk
206 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
210 catch(InvalidPositionException &e)
213 // This happens when position is over limit.
219 if(debug && changed_blocks.size() > 0)
221 dout_server<<DTIME<<"Got changed_blocks: ";
222 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
223 i.atEnd() == false; i++)
225 MapBlock *block = i.getNode()->getValue();
226 v3s16 p = block->getPos();
227 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
229 dout_server<<std::endl;
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");
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 // If this is true, inexistent block will be made from scratch
498 bool generate = d <= d_max_gen;
502 // Don't generate above player
508 // Limit the generating area vertically to 2/3
509 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
514 If block is far away, don't generate it unless it is
520 MapSector *sector = NULL;
523 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
525 catch(InvalidPositionException &e)
531 // Get center ground height in nodes
532 f32 gh = sector->getGroundHeight(
533 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
534 // Block center y in nodes
535 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
536 // If differs a lot, don't generate
537 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
543 Don't draw if not in sight
546 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
552 Don't send already sent blocks
555 JMutexAutoLock lock(m_blocks_sent_mutex);
557 if(m_blocks_sent.find(p) != NULL)
564 Ignore block if it is not at ground surface
565 but don't ignore water surface blocks
567 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
568 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
569 f32 y = server->m_env.getMap().getGroundHeight(p2d);
570 // The sector might not exist yet, thus no heightmap
571 if(y > GROUNDHEIGHT_VALID_MINVALUE)
573 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
574 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
575 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
581 Check if map has this block
583 MapBlock *block = NULL;
586 block = server->m_env.getMap().getBlockNoCreate(p);
588 catch(InvalidPositionException &e)
592 bool surely_not_found_on_disk = false;
595 /*if(block->isIncomplete())
597 has_incomplete_blocks = true;
603 surely_not_found_on_disk = true;
608 If block has been marked to not exist on disk (dummy)
609 and generating new ones is not wanted, skip block.
611 if(generate == false && surely_not_found_on_disk == true)
618 Record the lowest d from which a a block has been
619 found being not sent and possibly to exist
621 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
623 new_nearest_unsent_d = d;
627 Add inexistent block to emerge queue.
629 if(block == NULL || surely_not_found_on_disk)
631 /*SharedPtr<JMutexAutoLock> lock
632 (m_num_blocks_in_emerge_queue.getLock());*/
634 //TODO: Get value from somewhere
635 // Allow only one block in emerge queue
636 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
638 // Add it to the emerge queue and trigger the thread
641 if(generate == false)
642 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
644 server->m_emerge_queue.addBlock(peer_id, p, flags);
645 server->m_emergethread.trigger();
656 PrioritySortedBlockTransfer q((float)d, p, peer_id);
660 num_blocks_selected += 1;
665 if(new_nearest_unsent_d != -1)
667 JMutexAutoLock lock(m_blocks_sent_mutex);
668 m_nearest_unsent_d = new_nearest_unsent_d;
672 void RemoteClient::SendObjectData(
675 core::map<v3s16, bool> &stepped_blocks
678 DSTACK(__FUNCTION_NAME);
680 // Can't send anything without knowing version
681 if(serialization_version == SER_FMT_VER_INVALID)
683 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
689 Send a TOCLIENT_OBJECTDATA packet.
693 u16 number of player positions
704 std::ostringstream os(std::ios_base::binary);
708 writeU16(buf, TOCLIENT_OBJECTDATA);
709 os.write((char*)buf, 2);
712 Get and write player data
715 // Get connected players
716 core::list<Player*> players = server->m_env.getPlayers(true);
718 // Write player count
719 u16 playercount = players.size();
720 writeU16(buf, playercount);
721 os.write((char*)buf, 2);
723 core::list<Player*>::Iterator i;
724 for(i = players.begin();
725 i != players.end(); i++)
729 v3f pf = player->getPosition();
730 v3f sf = player->getSpeed();
732 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
733 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
734 s32 pitch_i (player->getPitch() * 100);
735 s32 yaw_i (player->getYaw() * 100);
737 writeU16(buf, player->peer_id);
738 os.write((char*)buf, 2);
739 writeV3S32(buf, position_i);
740 os.write((char*)buf, 12);
741 writeV3S32(buf, speed_i);
742 os.write((char*)buf, 12);
743 writeS32(buf, pitch_i);
744 os.write((char*)buf, 4);
745 writeS32(buf, yaw_i);
746 os.write((char*)buf, 4);
750 Get and write object data
756 For making players to be able to build to their nearby
757 environment (building is not possible on blocks that are not
760 - Add blocks to emerge queue if they are not found
762 SUGGESTION: These could be ignored from the backside of the player
765 Player *player = server->m_env.getPlayer(peer_id);
769 v3f playerpos = player->getPosition();
770 v3f playerspeed = player->getSpeed();
772 v3s16 center_nodepos = floatToInt(playerpos);
773 v3s16 center = getNodeBlockPos(center_nodepos);
775 s16 d_max = g_settings.getS16("active_object_range");
777 // Number of blocks whose objects were written to bos
780 std::ostringstream bos(std::ios_base::binary);
782 for(s16 d = 0; d <= d_max; d++)
784 core::list<v3s16> list;
785 getFacePositions(list, d);
787 core::list<v3s16>::Iterator li;
788 for(li=list.begin(); li!=list.end(); li++)
790 v3s16 p = *li + center;
793 Ignore blocks that haven't been sent to the client
796 JMutexAutoLock sentlock(m_blocks_sent_mutex);
797 if(m_blocks_sent.find(p) == NULL)
801 // Try stepping block and add it to a send queue
806 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
809 Step block if not in stepped_blocks and add to stepped_blocks.
811 if(stepped_blocks.find(p) == NULL)
813 block->stepObjects(dtime, true, server->getDayNightRatio());
814 stepped_blocks.insert(p, true);
815 block->setChangedFlag();
818 // Skip block if there are no objects
819 if(block->getObjectCount() == 0)
828 bos.write((char*)buf, 6);
831 block->serializeObjects(bos, serialization_version);
836 Stop collecting objects if data is already too big
838 // Sum of player and object data sizes
839 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
840 // break out if data too big
841 if(sum > MAX_OBJECTDATA_SIZE)
843 goto skip_subsequent;
847 catch(InvalidPositionException &e)
850 // Add it to the emerge queue and trigger the thread.
851 // Fetch the block only if it is on disk.
853 // Grab and increment counter
854 /*SharedPtr<JMutexAutoLock> lock
855 (m_num_blocks_in_emerge_queue.getLock());
856 m_num_blocks_in_emerge_queue.m_value++;*/
858 // Add to queue as an anonymous fetch from disk
859 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
860 server->m_emerge_queue.addBlock(0, p, flags);
861 server->m_emergethread.trigger();
869 writeU16(buf, blockcount);
870 os.write((char*)buf, 2);
872 // Write block objects
879 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
882 std::string s = os.str();
883 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
884 // Send as unreliable
885 server->m_con.Send(peer_id, 0, data, false);
888 void RemoteClient::GotBlock(v3s16 p)
890 JMutexAutoLock lock(m_blocks_sending_mutex);
891 JMutexAutoLock lock2(m_blocks_sent_mutex);
892 if(m_blocks_sending.find(p) != NULL)
893 m_blocks_sending.remove(p);
896 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
897 " m_blocks_sending"<<std::endl;*/
898 m_excess_gotblocks++;
900 m_blocks_sent.insert(p, true);
903 void RemoteClient::SentBlock(v3s16 p)
905 JMutexAutoLock lock(m_blocks_sending_mutex);
906 /*if(m_blocks_sending.size() > 15)
908 dstream<<"RemoteClient::SentBlock(): "
909 <<"m_blocks_sending.size()="
910 <<m_blocks_sending.size()<<std::endl;
912 if(m_blocks_sending.find(p) == NULL)
913 m_blocks_sending.insert(p, 0.0);
915 dstream<<"RemoteClient::SentBlock(): Sent block"
916 " already in m_blocks_sending"<<std::endl;
919 void RemoteClient::SetBlockNotSent(v3s16 p)
921 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
922 JMutexAutoLock sentlock(m_blocks_sent_mutex);
924 m_nearest_unsent_d = 0;
926 if(m_blocks_sending.find(p) != NULL)
927 m_blocks_sending.remove(p);
928 if(m_blocks_sent.find(p) != NULL)
929 m_blocks_sent.remove(p);
932 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
934 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
935 JMutexAutoLock sentlock(m_blocks_sent_mutex);
937 m_nearest_unsent_d = 0;
939 for(core::map<v3s16, MapBlock*>::Iterator
940 i = blocks.getIterator();
941 i.atEnd()==false; i++)
943 v3s16 p = i.getNode()->getKey();
945 if(m_blocks_sending.find(p) != NULL)
946 m_blocks_sending.remove(p);
947 if(m_blocks_sent.find(p) != NULL)
948 m_blocks_sent.remove(p);
956 PlayerInfo::PlayerInfo()
961 void PlayerInfo::PrintLine(std::ostream *s)
964 (*s)<<"\""<<name<<"\" ("
965 <<position.X<<","<<position.Y
966 <<","<<position.Z<<") ";
968 (*s)<<" avg_rtt="<<avg_rtt;
972 u32 PIChecksum(core::list<PlayerInfo> &l)
974 core::list<PlayerInfo>::Iterator i;
977 for(i=l.begin(); i!=l.end(); i++)
979 checksum += a * (i->id+1);
980 checksum ^= 0x435aafcd;
991 std::string mapsavedir,
995 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
996 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
998 m_emergethread(this),
1001 m_time_of_day_send_timer(0),
1004 //m_flowwater_timer = 0.0;
1005 m_liquid_transform_timer = 0.0;
1006 m_print_info_timer = 0.0;
1007 m_objectdata_timer = 0.0;
1008 m_emergethread_trigger_timer = 0.0;
1009 m_savemap_timer = 0.0;
1013 m_step_dtime_mutex.Init();
1022 JMutexAutoLock clientslock(m_con_mutex);
1024 for(core::map<u16, RemoteClient*>::Iterator
1025 i = m_clients.getIterator();
1026 i.atEnd() == false; i++)
1029 // NOTE: These are removed by env destructor
1031 u16 peer_id = i.getNode()->getKey();
1032 JMutexAutoLock envlock(m_env_mutex);
1033 m_env.removePlayer(peer_id);
1037 delete i.getNode()->getValue();
1041 void Server::start(unsigned short port)
1043 DSTACK(__FUNCTION_NAME);
1044 // Stop thread if already running
1047 // Initialize connection
1048 m_con.setTimeoutMs(30);
1052 m_thread.setRun(true);
1055 dout_server<<"Server started on port "<<port<<std::endl;
1060 DSTACK(__FUNCTION_NAME);
1061 // Stop threads (set run=false first so both start stopping)
1062 m_thread.setRun(false);
1063 m_emergethread.setRun(false);
1065 m_emergethread.stop();
1067 dout_server<<"Server threads stopped"<<std::endl;
1070 void Server::step(float dtime)
1072 DSTACK(__FUNCTION_NAME);
1077 JMutexAutoLock lock(m_step_dtime_mutex);
1078 m_step_dtime += dtime;
1082 void Server::AsyncRunStep()
1084 DSTACK(__FUNCTION_NAME);
1088 JMutexAutoLock lock1(m_step_dtime_mutex);
1089 dtime = m_step_dtime;
1092 // Send blocks to clients
1098 //dstream<<"Server steps "<<dtime<<std::endl;
1099 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1102 JMutexAutoLock lock1(m_step_dtime_mutex);
1103 m_step_dtime -= dtime;
1110 m_uptime.set(m_uptime.get() + dtime);
1114 Update m_time_of_day
1117 m_time_counter += dtime;
1118 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1119 u32 units = (u32)(m_time_counter*speed);
1120 m_time_counter -= (f32)units / speed;
1121 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1123 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1126 Send to clients at constant intervals
1129 m_time_of_day_send_timer -= dtime;
1130 if(m_time_of_day_send_timer < 0.0)
1132 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1134 //JMutexAutoLock envlock(m_env_mutex);
1135 JMutexAutoLock conlock(m_con_mutex);
1137 for(core::map<u16, RemoteClient*>::Iterator
1138 i = m_clients.getIterator();
1139 i.atEnd() == false; i++)
1141 RemoteClient *client = i.getNode()->getValue();
1142 //Player *player = m_env.getPlayer(client->peer_id);
1144 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1145 m_time_of_day.get());
1147 m_con.Send(client->peer_id, 0, data, true);
1153 // Process connection's timeouts
1154 JMutexAutoLock lock2(m_con_mutex);
1155 m_con.RunTimeouts(dtime);
1159 // This has to be called so that the client list gets synced
1160 // with the peer list of the connection
1161 handlePeerChanges();
1166 // This also runs Map's timers
1167 JMutexAutoLock lock(m_env_mutex);
1178 m_liquid_transform_timer += dtime;
1179 if(m_liquid_transform_timer >= 1.00)
1181 m_liquid_transform_timer -= 1.00;
1183 JMutexAutoLock lock(m_env_mutex);
1185 core::map<v3s16, MapBlock*> modified_blocks;
1186 m_env.getMap().transformLiquids(modified_blocks);
1191 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1192 ServerMap &map = ((ServerMap&)m_env.getMap());
1193 map.updateLighting(modified_blocks, lighting_modified_blocks);
1195 // Add blocks modified by lighting to modified_blocks
1196 for(core::map<v3s16, MapBlock*>::Iterator
1197 i = lighting_modified_blocks.getIterator();
1198 i.atEnd() == false; i++)
1200 MapBlock *block = i.getNode()->getValue();
1201 modified_blocks.insert(block->getPos(), block);
1205 Set the modified blocks unsent for all the clients
1208 JMutexAutoLock lock2(m_con_mutex);
1210 for(core::map<u16, RemoteClient*>::Iterator
1211 i = m_clients.getIterator();
1212 i.atEnd() == false; i++)
1214 RemoteClient *client = i.getNode()->getValue();
1216 if(modified_blocks.size() > 0)
1218 // Remove block from sent history
1219 client->SetBlocksNotSent(modified_blocks);
1228 if(g_settings.getBool("water_moves") == true)
1232 if(g_settings.getBool("endless_water") == false)
1237 float &counter = m_flowwater_timer;
1239 if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
1244 core::map<v3s16, MapBlock*> modified_blocks;
1248 JMutexAutoLock envlock(m_env_mutex);
1250 MapVoxelManipulator v(&m_env.getMap());
1251 v.m_disable_water_climb =
1252 g_settings.getBool("disable_water_climb");
1254 if(g_settings.getBool("endless_water") == false)
1255 v.flowWater(m_flow_active_nodes, 0, false, 250);
1257 v.flowWater(m_flow_active_nodes, 0, false, 50);
1259 v.blitBack(modified_blocks);
1261 ServerMap &map = ((ServerMap&)m_env.getMap());
1264 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1265 map.updateLighting(modified_blocks, lighting_modified_blocks);
1267 // Add blocks modified by lighting to modified_blocks
1268 for(core::map<v3s16, MapBlock*>::Iterator
1269 i = lighting_modified_blocks.getIterator();
1270 i.atEnd() == false; i++)
1272 MapBlock *block = i.getNode()->getValue();
1273 modified_blocks.insert(block->getPos(), block);
1278 Set the modified blocks unsent for all the clients
1281 JMutexAutoLock lock2(m_con_mutex);
1283 for(core::map<u16, RemoteClient*>::Iterator
1284 i = m_clients.getIterator();
1285 i.atEnd() == false; i++)
1287 RemoteClient *client = i.getNode()->getValue();
1289 if(modified_blocks.size() > 0)
1291 // Remove block from sent history
1292 client->SetBlocksNotSent(modified_blocks);
1296 } // interval counter
1300 // Periodically print some info
1302 float &counter = m_print_info_timer;
1308 JMutexAutoLock lock2(m_con_mutex);
1310 for(core::map<u16, RemoteClient*>::Iterator
1311 i = m_clients.getIterator();
1312 i.atEnd() == false; i++)
1314 //u16 peer_id = i.getNode()->getKey();
1315 RemoteClient *client = i.getNode()->getValue();
1316 client->PrintInfo(std::cout);
1324 NOTE: Some of this could be moved to RemoteClient
1328 JMutexAutoLock envlock(m_env_mutex);
1329 JMutexAutoLock conlock(m_con_mutex);
1331 for(core::map<u16, RemoteClient*>::Iterator
1332 i = m_clients.getIterator();
1333 i.atEnd() == false; i++)
1335 RemoteClient *client = i.getNode()->getValue();
1336 Player *player = m_env.getPlayer(client->peer_id);
1338 JMutexAutoLock digmutex(client->m_dig_mutex);
1340 if(client->m_dig_tool_item == -1)
1343 client->m_dig_time_remaining -= dtime;
1345 if(client->m_dig_time_remaining > 0)
1347 client->m_time_from_building.set(0.0);
1351 v3s16 p_under = client->m_dig_position;
1353 // Mandatory parameter; actually used for nothing
1354 core::map<v3s16, MapBlock*> modified_blocks;
1360 // Get material at position
1361 material = m_env.getMap().getNode(p_under).d;
1362 // If it's not diggable, do nothing
1363 if(content_diggable(material) == false)
1365 derr_server<<"Server: Not finishing digging: Node not diggable"
1367 client->m_dig_tool_item = -1;
1371 catch(InvalidPositionException &e)
1373 derr_server<<"Server: Not finishing digging: Node not found"
1375 client->m_dig_tool_item = -1;
1381 SharedBuffer<u8> reply(replysize);
1382 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1383 writeS16(&reply[2], p_under.X);
1384 writeS16(&reply[4], p_under.Y);
1385 writeS16(&reply[6], p_under.Z);
1387 m_con.SendToAll(0, reply, true);
1389 if(g_settings.getBool("creative_mode") == false)
1391 // Add to inventory and send inventory
1392 InventoryItem *item = new MaterialItem(material, 1);
1393 player->inventory.addItem("main", item);
1394 SendInventory(player->peer_id);
1399 (this takes some time so it is done after the quick stuff)
1401 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1407 // Update water pressure around modification
1408 // This also adds it to m_flow_active_nodes if appropriate
1410 MapVoxelManipulator v(&m_env.getMap());
1411 v.m_disable_water_climb =
1412 g_settings.getBool("disable_water_climb");
1414 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1418 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1420 catch(ProcessingLimitException &e)
1422 dstream<<"Processing limit reached (1)"<<std::endl;
1425 v.blitBack(modified_blocks);
1430 // Send object positions
1432 float &counter = m_objectdata_timer;
1434 if(counter >= g_settings.getFloat("objectdata_interval"))
1436 JMutexAutoLock lock1(m_env_mutex);
1437 JMutexAutoLock lock2(m_con_mutex);
1438 SendObjectData(counter);
1444 // Trigger emergethread (it gets somehow gets to a
1445 // non-triggered but bysy state sometimes)
1447 float &counter = m_emergethread_trigger_timer;
1453 m_emergethread.trigger();
1459 float &counter = m_savemap_timer;
1461 if(counter >= g_settings.getFloat("server_map_save_interval"))
1465 JMutexAutoLock lock(m_env_mutex);
1467 // Save only changed parts
1468 m_env.getMap().save(true);
1470 // Delete unused sectors
1471 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1472 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1473 if(deleted_count > 0)
1475 dout_server<<"Server: Unloaded "<<deleted_count
1476 <<" sectors from memory"<<std::endl;
1482 void Server::Receive()
1484 DSTACK(__FUNCTION_NAME);
1485 u32 data_maxsize = 10000;
1486 Buffer<u8> data(data_maxsize);
1491 JMutexAutoLock conlock(m_con_mutex);
1492 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1495 // This has to be called so that the client list gets synced
1496 // with the peer list of the connection
1497 handlePeerChanges();
1499 ProcessData(*data, datasize, peer_id);
1501 catch(con::InvalidIncomingDataException &e)
1503 derr_server<<"Server::Receive(): "
1504 "InvalidIncomingDataException: what()="
1505 <<e.what()<<std::endl;
1507 catch(con::PeerNotFoundException &e)
1509 //NOTE: This is not needed anymore
1511 // The peer has been disconnected.
1512 // Find the associated player and remove it.
1514 /*JMutexAutoLock envlock(m_env_mutex);
1516 dout_server<<"ServerThread: peer_id="<<peer_id
1517 <<" has apparently closed connection. "
1518 <<"Removing player."<<std::endl;
1520 m_env.removePlayer(peer_id);*/
1524 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1526 DSTACK(__FUNCTION_NAME);
1527 // Environment is locked first.
1528 JMutexAutoLock envlock(m_env_mutex);
1529 JMutexAutoLock conlock(m_con_mutex);
1533 peer = m_con.GetPeer(peer_id);
1535 catch(con::PeerNotFoundException &e)
1537 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1538 <<peer_id<<" not found"<<std::endl;
1542 //u8 peer_ser_ver = peer->serialization_version;
1543 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1551 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1553 if(command == TOSERVER_INIT)
1555 // [0] u16 TOSERVER_INIT
1556 // [2] u8 SER_FMT_VER_HIGHEST
1557 // [3] u8[20] player_name
1562 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1563 <<peer->id<<std::endl;
1565 // First byte after command is maximum supported
1566 // serialization version
1567 u8 client_max = data[2];
1568 u8 our_max = SER_FMT_VER_HIGHEST;
1569 // Use the highest version supported by both
1570 u8 deployed = core::min_(client_max, our_max);
1571 // If it's lower than the lowest supported, give up.
1572 if(deployed < SER_FMT_VER_LOWEST)
1573 deployed = SER_FMT_VER_INVALID;
1575 //peer->serialization_version = deployed;
1576 getClient(peer->id)->pending_serialization_version = deployed;
1578 if(deployed == SER_FMT_VER_INVALID)
1580 derr_server<<DTIME<<"Server: Cannot negotiate "
1581 "serialization version with peer "
1582 <<peer_id<<std::endl;
1591 const u32 playername_size = 20;
1592 char playername[playername_size];
1593 for(u32 i=0; i<playername_size-1; i++)
1595 playername[i] = data[3+i];
1597 playername[playername_size-1] = 0;
1600 Player *player = emergePlayer(playername, "", peer_id);
1601 //Player *player = m_env.getPlayer(peer_id);
1603 // If failed, cancel
1606 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1607 <<": failed to emerge player"<<std::endl;
1612 // If a client is already connected to the player, cancel
1613 if(player->peer_id != 0)
1615 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1616 <<" tried to connect to "
1617 "an already connected player (peer_id="
1618 <<player->peer_id<<")"<<std::endl;
1621 // Set client of player
1622 player->peer_id = peer_id;
1625 // Check if player doesn't exist
1627 throw con::InvalidIncomingDataException
1628 ("Server::ProcessData(): INIT: Player doesn't exist");
1630 /*// update name if it was supplied
1631 if(datasize >= 20+3)
1634 player->updateName((const char*)&data[3]);
1637 // Now answer with a TOCLIENT_INIT
1639 SharedBuffer<u8> reply(2+1+6);
1640 writeU16(&reply[0], TOCLIENT_INIT);
1641 writeU8(&reply[2], deployed);
1642 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1644 m_con.Send(peer_id, 0, reply, true);
1648 if(command == TOSERVER_INIT2)
1650 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1651 <<peer->id<<std::endl;
1654 getClient(peer->id)->serialization_version
1655 = getClient(peer->id)->pending_serialization_version;
1658 Send some initialization data
1661 // Send player info to all players
1664 // Send inventory to player
1665 SendInventory(peer->id);
1669 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1670 m_time_of_day.get());
1671 m_con.Send(peer->id, 0, data, true);
1674 // Send information about server to player in chat
1676 std::wostringstream os(std::ios_base::binary);
1679 os<<L"uptime="<<m_uptime.get();
1680 // Information about clients
1682 for(core::map<u16, RemoteClient*>::Iterator
1683 i = m_clients.getIterator();
1684 i.atEnd() == false; i++)
1686 // Get client and check that it is valid
1687 RemoteClient *client = i.getNode()->getValue();
1688 assert(client->peer_id == i.getNode()->getKey());
1689 if(client->serialization_version == SER_FMT_VER_INVALID)
1692 Player *player = m_env.getPlayer(client->peer_id);
1693 // Get name of player
1694 std::wstring name = L"unknown";
1696 name = narrow_to_wide(player->getName());
1697 // Add name to information string
1702 SendChatMessage(peer_id, os.str());
1705 // Send information about joining in chat
1707 std::wstring name = L"unknown";
1708 Player *player = m_env.getPlayer(peer_id);
1710 name = narrow_to_wide(player->getName());
1712 std::wstring message;
1715 message += L" joined game";
1716 BroadcastChatMessage(message);
1722 if(peer_ser_ver == SER_FMT_VER_INVALID)
1724 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1725 " serialization format invalid or not initialized."
1726 " Skipping incoming command="<<command<<std::endl;
1730 Player *player = m_env.getPlayer(peer_id);
1733 derr_server<<"Server::ProcessData(): Cancelling: "
1734 "No player for peer_id="<<peer_id
1738 if(command == TOSERVER_PLAYERPOS)
1740 if(datasize < 2+12+12+4+4)
1744 v3s32 ps = readV3S32(&data[start+2]);
1745 v3s32 ss = readV3S32(&data[start+2+12]);
1746 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1747 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1748 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1749 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1750 pitch = wrapDegrees(pitch);
1751 yaw = wrapDegrees(yaw);
1752 player->setPosition(position);
1753 player->setSpeed(speed);
1754 player->setPitch(pitch);
1755 player->setYaw(yaw);
1757 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1758 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1759 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1761 else if(command == TOSERVER_GOTBLOCKS)
1774 u16 count = data[2];
1775 for(u16 i=0; i<count; i++)
1777 if((s16)datasize < 2+1+(i+1)*6)
1778 throw con::InvalidIncomingDataException
1779 ("GOTBLOCKS length is too short");
1780 v3s16 p = readV3S16(&data[2+1+i*6]);
1781 /*dstream<<"Server: GOTBLOCKS ("
1782 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1783 RemoteClient *client = getClient(peer_id);
1784 client->GotBlock(p);
1787 else if(command == TOSERVER_DELETEDBLOCKS)
1800 u16 count = data[2];
1801 for(u16 i=0; i<count; i++)
1803 if((s16)datasize < 2+1+(i+1)*6)
1804 throw con::InvalidIncomingDataException
1805 ("DELETEDBLOCKS length is too short");
1806 v3s16 p = readV3S16(&data[2+1+i*6]);
1807 /*dstream<<"Server: DELETEDBLOCKS ("
1808 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1809 RemoteClient *client = getClient(peer_id);
1810 client->SetBlockNotSent(p);
1813 else if(command == TOSERVER_CLICK_OBJECT)
1820 [2] u8 button (0=left, 1=right)
1825 u8 button = readU8(&data[2]);
1827 p.X = readS16(&data[3]);
1828 p.Y = readS16(&data[5]);
1829 p.Z = readS16(&data[7]);
1830 s16 id = readS16(&data[9]);
1831 //u16 item_i = readU16(&data[11]);
1833 MapBlock *block = NULL;
1836 block = m_env.getMap().getBlockNoCreate(p);
1838 catch(InvalidPositionException &e)
1840 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1844 MapBlockObject *obj = block->getObject(id);
1848 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1852 //TODO: Check that object is reasonably close
1857 InventoryList *ilist = player->inventory.getList("main");
1858 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1861 // Skip if inventory has no free space
1862 if(ilist->getUsedSlots() == ilist->getSize())
1864 dout_server<<"Player inventory has no free space"<<std::endl;
1869 Create the inventory item
1871 InventoryItem *item = NULL;
1872 // If it is an item-object, take the item from it
1873 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1875 item = ((ItemObject*)obj)->createInventoryItem();
1877 // Else create an item of the object
1880 item = new MapBlockObjectItem
1881 (obj->getInventoryString());
1884 // Add to inventory and send inventory
1885 ilist->addItem(item);
1886 SendInventory(player->peer_id);
1889 // Remove from block
1890 block->removeObject(id);
1893 else if(command == TOSERVER_GROUND_ACTION)
1901 [3] v3s16 nodepos_undersurface
1902 [9] v3s16 nodepos_abovesurface
1907 2: stop digging (all parameters ignored)
1909 u8 action = readU8(&data[2]);
1911 p_under.X = readS16(&data[3]);
1912 p_under.Y = readS16(&data[5]);
1913 p_under.Z = readS16(&data[7]);
1915 p_over.X = readS16(&data[9]);
1916 p_over.Y = readS16(&data[11]);
1917 p_over.Z = readS16(&data[13]);
1918 u16 item_i = readU16(&data[15]);
1920 //TODO: Check that target is reasonably close
1928 NOTE: This can be used in the future to check if
1929 somebody is cheating, by checking the timing.
1936 else if(action == 2)
1939 RemoteClient *client = getClient(peer->id);
1940 JMutexAutoLock digmutex(client->m_dig_mutex);
1941 client->m_dig_tool_item = -1;
1946 3: Digging completed
1948 else if(action == 3)
1950 // Mandatory parameter; actually used for nothing
1951 core::map<v3s16, MapBlock*> modified_blocks;
1957 // Get material at position
1958 material = m_env.getMap().getNode(p_under).d;
1959 // If it's not diggable, do nothing
1960 if(content_diggable(material) == false)
1962 derr_server<<"Server: Not finishing digging: Node not diggable"
1967 catch(InvalidPositionException &e)
1969 derr_server<<"Server: Not finishing digging: Node not found"
1974 //TODO: Send to only other clients
1977 Send the removal to all other clients
1982 SharedBuffer<u8> reply(replysize);
1983 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1984 writeS16(&reply[2], p_under.X);
1985 writeS16(&reply[4], p_under.Y);
1986 writeS16(&reply[6], p_under.Z);
1988 for(core::map<u16, RemoteClient*>::Iterator
1989 i = m_clients.getIterator();
1990 i.atEnd() == false; i++)
1992 // Get client and check that it is valid
1993 RemoteClient *client = i.getNode()->getValue();
1994 assert(client->peer_id == i.getNode()->getKey());
1995 if(client->serialization_version == SER_FMT_VER_INVALID)
1998 // Don't send if it's the same one
1999 if(peer_id == client->peer_id)
2003 m_con.Send(client->peer_id, 0, reply, true);
2007 Update and send inventory
2010 if(g_settings.getBool("creative_mode") == false)
2015 InventoryList *mlist = player->inventory.getList("main");
2018 InventoryItem *item = mlist->getItem(item_i);
2019 if(item && (std::string)item->getName() == "ToolItem")
2021 ToolItem *titem = (ToolItem*)item;
2022 std::string toolname = titem->getToolName();
2024 // Get digging properties for material and tool
2025 DiggingProperties prop =
2026 getDiggingProperties(material, toolname);
2028 if(prop.diggable == false)
2030 derr_server<<"Server: WARNING: Player digged"
2031 <<" with impossible material + tool"
2032 <<" combination"<<std::endl;
2035 bool weared_out = titem->addWear(prop.wear);
2039 mlist->deleteItem(item_i);
2045 Add digged item to inventory
2047 InventoryItem *item = new MaterialItem(material, 1);
2048 player->inventory.addItem("main", item);
2053 SendInventory(player->peer_id);
2058 (this takes some time so it is done after the quick stuff)
2060 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2067 // Update water pressure around modification
2068 // This also adds it to m_flow_active_nodes if appropriate
2070 MapVoxelManipulator v(&m_env.getMap());
2071 v.m_disable_water_climb =
2072 g_settings.getBool("disable_water_climb");
2074 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2078 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2080 catch(ProcessingLimitException &e)
2082 dstream<<"Processing limit reached (1)"<<std::endl;
2085 v.blitBack(modified_blocks);
2092 else if(action == 1)
2095 InventoryList *ilist = player->inventory.getList("main");
2100 InventoryItem *item = ilist->getItem(item_i);
2102 // If there is no item, it is not possible to add it anywhere
2107 Handle material items
2109 if(std::string("MaterialItem") == item->getName())
2112 // Don't add a node if this is not a free space
2113 MapNode n2 = m_env.getMap().getNode(p_over);
2114 if(content_buildable_to(n2.d) == false)
2117 catch(InvalidPositionException &e)
2119 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2124 // Reset build time counter
2125 getClient(peer->id)->m_time_from_building.set(0.0);
2128 MaterialItem *mitem = (MaterialItem*)item;
2130 n.d = mitem->getMaterial();
2131 if(content_directional(n.d))
2132 n.dir = packDir(p_under - p_over);
2136 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2137 SharedBuffer<u8> reply(replysize);
2138 writeU16(&reply[0], TOCLIENT_ADDNODE);
2139 writeS16(&reply[2], p_over.X);
2140 writeS16(&reply[4], p_over.Y);
2141 writeS16(&reply[6], p_over.Z);
2142 n.serialize(&reply[8], peer_ser_ver);
2144 m_con.SendToAll(0, reply, true);
2149 InventoryList *ilist = player->inventory.getList("main");
2150 if(g_settings.getBool("creative_mode") == false && ilist)
2152 // Remove from inventory and send inventory
2153 if(mitem->getCount() == 1)
2154 ilist->deleteItem(item_i);
2158 SendInventory(peer_id);
2164 This takes some time so it is done after the quick stuff
2166 core::map<v3s16, MapBlock*> modified_blocks;
2167 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2173 InventoryList *ilist = player->inventory.getList("main");
2174 if(g_settings.getBool("creative_mode") == false && ilist)
2176 // Remove from inventory and send inventory
2177 if(mitem->getCount() == 1)
2178 ilist->deleteItem(item_i);
2182 SendInventory(peer_id);
2188 This takes some time so it is done after the quick stuff
2190 core::map<v3s16, MapBlock*> modified_blocks;
2191 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2194 Set the modified blocks unsent for all the clients
2197 //JMutexAutoLock lock2(m_con_mutex);
2199 for(core::map<u16, RemoteClient*>::Iterator
2200 i = m_clients.getIterator();
2201 i.atEnd() == false; i++)
2203 RemoteClient *client = i.getNode()->getValue();
2205 if(modified_blocks.size() > 0)
2207 // Remove block from sent history
2208 client->SetBlocksNotSent(modified_blocks);
2218 // Update water pressure around modification
2219 // This also adds it to m_flow_active_nodes if appropriate
2221 MapVoxelManipulator v(&m_env.getMap());
2222 v.m_disable_water_climb =
2223 g_settings.getBool("disable_water_climb");
2225 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2229 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2231 catch(ProcessingLimitException &e)
2233 dstream<<"Processing limit reached (1)"<<std::endl;
2236 v.blitBack(modified_blocks);
2244 v3s16 blockpos = getNodeBlockPos(p_over);
2246 MapBlock *block = NULL;
2249 block = m_env.getMap().getBlockNoCreate(blockpos);
2251 catch(InvalidPositionException &e)
2253 derr_server<<"Error while placing object: "
2254 "block not found"<<std::endl;
2258 v3s16 block_pos_i_on_map = block->getPosRelative();
2259 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2261 v3f pos = intToFloat(p_over);
2262 pos -= block_pos_f_on_map;
2264 /*dout_server<<"pos="
2265 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2268 MapBlockObject *obj = NULL;
2271 Handle block object items
2273 if(std::string("MBOItem") == item->getName())
2275 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2277 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2278 "inventorystring=\""
2279 <<oitem->getInventoryString()
2280 <<"\""<<std::endl;*/
2282 obj = oitem->createObject
2283 (pos, player->getYaw(), player->getPitch());
2290 dout_server<<"Placing a miscellaneous item on map"
2293 Create an ItemObject that contains the item.
2295 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2296 std::ostringstream os(std::ios_base::binary);
2297 item->serialize(os);
2298 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2299 iobj->setItemString(os.str());
2305 derr_server<<"WARNING: item resulted in NULL object, "
2306 <<"not placing onto map"
2311 block->addObject(obj);
2313 dout_server<<"Placed object"<<std::endl;
2315 InventoryList *ilist = player->inventory.getList("main");
2316 if(g_settings.getBool("creative_mode") == false && ilist)
2318 // Remove from inventory and send inventory
2319 ilist->deleteItem(item_i);
2321 SendInventory(peer_id);
2329 Catch invalid actions
2333 derr_server<<"WARNING: Server: Invalid action "
2334 <<action<<std::endl;
2338 else if(command == TOSERVER_RELEASE)
2347 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2350 else if(command == TOSERVER_SIGNTEXT)
2359 std::string datastring((char*)&data[2], datasize-2);
2360 std::istringstream is(datastring, std::ios_base::binary);
2363 is.read((char*)buf, 6);
2364 v3s16 blockpos = readV3S16(buf);
2365 is.read((char*)buf, 2);
2366 s16 id = readS16(buf);
2367 is.read((char*)buf, 2);
2368 u16 textlen = readU16(buf);
2370 for(u16 i=0; i<textlen; i++)
2372 is.read((char*)buf, 1);
2373 text += (char)buf[0];
2376 MapBlock *block = NULL;
2379 block = m_env.getMap().getBlockNoCreate(blockpos);
2381 catch(InvalidPositionException &e)
2383 derr_server<<"Error while setting sign text: "
2384 "block not found"<<std::endl;
2388 MapBlockObject *obj = block->getObject(id);
2391 derr_server<<"Error while setting sign text: "
2392 "object not found"<<std::endl;
2396 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2398 derr_server<<"Error while setting sign text: "
2399 "object is not a sign"<<std::endl;
2403 ((SignObject*)obj)->setText(text);
2405 obj->getBlock()->setChangedFlag();
2407 else if(command == TOSERVER_INVENTORY_ACTION)
2409 /*// Ignore inventory changes if in creative mode
2410 if(g_settings.getBool("creative_mode") == true)
2412 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2416 // Strip command and create a stream
2417 std::string datastring((char*)&data[2], datasize-2);
2418 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2419 std::istringstream is(datastring, std::ios_base::binary);
2421 InventoryAction *a = InventoryAction::deSerialize(is);
2425 Handle craftresult specially if not in creative mode
2427 bool disable_action = false;
2428 if(a->getType() == IACTION_MOVE
2429 && g_settings.getBool("creative_mode") == false)
2431 IMoveAction *ma = (IMoveAction*)a;
2432 // Don't allow moving anything to craftresult
2433 if(ma->to_name == "craftresult")
2436 disable_action = true;
2438 // When something is removed from craftresult
2439 if(ma->from_name == "craftresult")
2441 disable_action = true;
2442 // Remove stuff from craft
2443 InventoryList *clist = player->inventory.getList("craft");
2446 u16 count = ma->count;
2449 clist->decrementMaterials(count);
2452 // Feed action to player inventory
2453 a->apply(&player->inventory);
2456 // If something appeared in craftresult, throw it
2458 InventoryList *rlist = player->inventory.getList("craftresult");
2459 InventoryList *mlist = player->inventory.getList("main");
2460 if(rlist && mlist && rlist->getUsedSlots() == 1)
2462 InventoryItem *item1 = rlist->changeItem(0, NULL);
2463 mlist->addItem(item1);
2467 if(disable_action == false)
2469 // Feed action to player inventory
2470 a->apply(&player->inventory);
2475 SendInventory(player->peer_id);
2479 dstream<<"TOSERVER_INVENTORY_ACTION: "
2480 <<"InventoryAction::deSerialize() returned NULL"
2484 else if(command == TOSERVER_CHAT_MESSAGE)
2492 std::string datastring((char*)&data[2], datasize-2);
2493 std::istringstream is(datastring, std::ios_base::binary);
2496 is.read((char*)buf, 2);
2497 u16 len = readU16(buf);
2499 std::wstring message;
2500 for(u16 i=0; i<len; i++)
2502 is.read((char*)buf, 2);
2503 message += (wchar_t)readU16(buf);
2506 // Get player name of this client
2507 std::wstring name = narrow_to_wide(player->getName());
2509 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2511 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2514 Send the message to all other clients
2516 for(core::map<u16, RemoteClient*>::Iterator
2517 i = m_clients.getIterator();
2518 i.atEnd() == false; i++)
2520 // Get client and check that it is valid
2521 RemoteClient *client = i.getNode()->getValue();
2522 assert(client->peer_id == i.getNode()->getKey());
2523 if(client->serialization_version == SER_FMT_VER_INVALID)
2526 // Don't send if it's the same one
2527 if(peer_id == client->peer_id)
2530 SendChatMessage(client->peer_id, line);
2535 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2536 "unknown command "<<command<<std::endl;
2540 catch(SendFailedException &e)
2542 derr_server<<"Server::ProcessData(): SendFailedException: "
2548 /*void Server::Send(u16 peer_id, u16 channelnum,
2549 SharedBuffer<u8> data, bool reliable)
2551 JMutexAutoLock lock(m_con_mutex);
2552 m_con.Send(peer_id, channelnum, data, reliable);
2555 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2557 DSTACK(__FUNCTION_NAME);
2559 Create a packet with the block in the right format
2562 std::ostringstream os(std::ios_base::binary);
2563 block->serialize(os, ver);
2564 std::string s = os.str();
2565 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2567 u32 replysize = 8 + blockdata.getSize();
2568 SharedBuffer<u8> reply(replysize);
2569 v3s16 p = block->getPos();
2570 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2571 writeS16(&reply[2], p.X);
2572 writeS16(&reply[4], p.Y);
2573 writeS16(&reply[6], p.Z);
2574 memcpy(&reply[8], *blockdata, blockdata.getSize());
2576 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2577 <<": \tpacket size: "<<replysize<<std::endl;*/
2582 m_con.Send(peer_id, 1, reply, true);
2585 core::list<PlayerInfo> Server::getPlayerInfo()
2587 DSTACK(__FUNCTION_NAME);
2588 JMutexAutoLock envlock(m_env_mutex);
2589 JMutexAutoLock conlock(m_con_mutex);
2591 core::list<PlayerInfo> list;
2593 core::list<Player*> players = m_env.getPlayers();
2595 core::list<Player*>::Iterator i;
2596 for(i = players.begin();
2597 i != players.end(); i++)
2601 Player *player = *i;
2604 con::Peer *peer = m_con.GetPeer(player->peer_id);
2605 // Copy info from peer to info struct
2607 info.address = peer->address;
2608 info.avg_rtt = peer->avg_rtt;
2610 catch(con::PeerNotFoundException &e)
2612 // Set dummy peer info
2614 info.address = Address(0,0,0,0,0);
2618 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2619 info.position = player->getPosition();
2621 list.push_back(info);
2627 void Server::peerAdded(con::Peer *peer)
2629 DSTACK(__FUNCTION_NAME);
2630 dout_server<<"Server::peerAdded(): peer->id="
2631 <<peer->id<<std::endl;
2634 c.type = PEER_ADDED;
2635 c.peer_id = peer->id;
2637 m_peer_change_queue.push_back(c);
2640 void Server::deletingPeer(con::Peer *peer, bool timeout)
2642 DSTACK(__FUNCTION_NAME);
2643 dout_server<<"Server::deletingPeer(): peer->id="
2644 <<peer->id<<", timeout="<<timeout<<std::endl;
2647 c.type = PEER_REMOVED;
2648 c.peer_id = peer->id;
2649 c.timeout = timeout;
2650 m_peer_change_queue.push_back(c);
2653 void Server::SendObjectData(float dtime)
2655 DSTACK(__FUNCTION_NAME);
2657 core::map<v3s16, bool> stepped_blocks;
2659 for(core::map<u16, RemoteClient*>::Iterator
2660 i = m_clients.getIterator();
2661 i.atEnd() == false; i++)
2663 u16 peer_id = i.getNode()->getKey();
2664 RemoteClient *client = i.getNode()->getValue();
2665 assert(client->peer_id == peer_id);
2667 if(client->serialization_version == SER_FMT_VER_INVALID)
2670 client->SendObjectData(this, dtime, stepped_blocks);
2674 void Server::SendPlayerInfos()
2676 DSTACK(__FUNCTION_NAME);
2678 //JMutexAutoLock envlock(m_env_mutex);
2680 // Get connected players
2681 core::list<Player*> players = m_env.getPlayers(true);
2683 u32 player_count = players.getSize();
2684 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2686 SharedBuffer<u8> data(datasize);
2687 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2690 core::list<Player*>::Iterator i;
2691 for(i = players.begin();
2692 i != players.end(); i++)
2694 Player *player = *i;
2696 /*dstream<<"Server sending player info for player with "
2697 "peer_id="<<player->peer_id<<std::endl;*/
2699 writeU16(&data[start], player->peer_id);
2700 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2701 start += 2+PLAYERNAME_SIZE;
2704 //JMutexAutoLock conlock(m_con_mutex);
2707 m_con.SendToAll(0, data, true);
2725 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2731 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2737 enum ItemSpecType type;
2738 // Only other one of these is used
2744 items: a pointer to an array of 9 pointers to items
2745 specs: a pointer to an array of 9 ItemSpecs
2747 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2749 u16 items_min_x = 100;
2750 u16 items_max_x = 100;
2751 u16 items_min_y = 100;
2752 u16 items_max_y = 100;
2753 for(u16 y=0; y<3; y++)
2754 for(u16 x=0; x<3; x++)
2756 if(items[y*3 + x] == NULL)
2758 if(items_min_x == 100 || x < items_min_x)
2760 if(items_min_y == 100 || y < items_min_y)
2762 if(items_max_x == 100 || x > items_max_x)
2764 if(items_max_y == 100 || y > items_max_y)
2767 // No items at all, just return false
2768 if(items_min_x == 100)
2771 u16 items_w = items_max_x - items_min_x + 1;
2772 u16 items_h = items_max_y - items_min_y + 1;
2774 u16 specs_min_x = 100;
2775 u16 specs_max_x = 100;
2776 u16 specs_min_y = 100;
2777 u16 specs_max_y = 100;
2778 for(u16 y=0; y<3; y++)
2779 for(u16 x=0; x<3; x++)
2781 if(specs[y*3 + x].type == ITEM_NONE)
2783 if(specs_min_x == 100 || x < specs_min_x)
2785 if(specs_min_y == 100 || y < specs_min_y)
2787 if(specs_max_x == 100 || x > specs_max_x)
2789 if(specs_max_y == 100 || y > specs_max_y)
2792 // No specs at all, just return false
2793 if(specs_min_x == 100)
2796 u16 specs_w = specs_max_x - specs_min_x + 1;
2797 u16 specs_h = specs_max_y - specs_min_y + 1;
2800 if(items_w != specs_w || items_h != specs_h)
2803 for(u16 y=0; y<specs_h; y++)
2804 for(u16 x=0; x<specs_w; x++)
2806 u16 items_x = items_min_x + x;
2807 u16 items_y = items_min_y + y;
2808 u16 specs_x = specs_min_x + x;
2809 u16 specs_y = specs_min_y + y;
2810 InventoryItem *item = items[items_y * 3 + items_x];
2811 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2813 if(spec.type == ITEM_NONE)
2815 // Has to be no item
2821 // There should be an item
2825 std::string itemname = item->getName();
2827 if(spec.type == ITEM_MATERIAL)
2829 if(itemname != "MaterialItem")
2831 MaterialItem *mitem = (MaterialItem*)item;
2832 if(mitem->getMaterial() != spec.num)
2835 else if(spec.type == ITEM_CRAFT)
2837 if(itemname != "CraftItem")
2839 CraftItem *mitem = (CraftItem*)item;
2840 if(mitem->getSubName() != spec.name)
2843 else if(spec.type == ITEM_TOOL)
2845 // Not supported yet
2848 else if(spec.type == ITEM_MBO)
2850 // Not supported yet
2855 // Not supported yet
2863 void Server::SendInventory(u16 peer_id)
2865 DSTACK(__FUNCTION_NAME);
2867 Player* player = m_env.getPlayer(peer_id);
2870 Calculate crafting stuff
2872 if(g_settings.getBool("creative_mode") == false)
2874 InventoryList *clist = player->inventory.getList("craft");
2875 InventoryList *rlist = player->inventory.getList("craftresult");
2878 rlist->clearItems();
2882 InventoryItem *items[9];
2883 for(u16 i=0; i<9; i++)
2885 items[i] = clist->getItem(i);
2894 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2895 if(checkItemCombination(items, specs))
2897 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2906 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2907 if(checkItemCombination(items, specs))
2909 rlist->addItem(new CraftItem("Stick", 4));
2918 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2919 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2920 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2921 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2922 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2923 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2925 if(checkItemCombination(items, specs))
2927 rlist->addItem(new MapBlockObjectItem("Sign"));
2936 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2937 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2938 if(checkItemCombination(items, specs))
2940 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2949 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2950 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2951 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2952 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2953 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2954 if(checkItemCombination(items, specs))
2956 rlist->addItem(new ToolItem("WPick", 0));
2965 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2966 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2967 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2968 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2969 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2970 if(checkItemCombination(items, specs))
2972 rlist->addItem(new ToolItem("STPick", 0));
2981 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2982 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2983 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2984 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2985 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2986 if(checkItemCombination(items, specs))
2988 rlist->addItem(new ToolItem("MesePick", 0));
2993 } // if creative_mode == false
2999 std::ostringstream os;
3000 //os.imbue(std::locale("C"));
3002 player->inventory.serialize(os);
3004 std::string s = os.str();
3006 SharedBuffer<u8> data(s.size()+2);
3007 writeU16(&data[0], TOCLIENT_INVENTORY);
3008 memcpy(&data[2], s.c_str(), s.size());
3011 m_con.Send(peer_id, 0, data, true);
3014 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3016 DSTACK(__FUNCTION_NAME);
3018 std::ostringstream os(std::ios_base::binary);
3022 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3023 os.write((char*)buf, 2);
3026 writeU16(buf, message.size());
3027 os.write((char*)buf, 2);
3030 for(u32 i=0; i<message.size(); i++)
3034 os.write((char*)buf, 2);
3038 std::string s = os.str();
3039 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3041 m_con.Send(peer_id, 0, data, true);
3044 void Server::BroadcastChatMessage(const std::wstring &message)
3046 for(core::map<u16, RemoteClient*>::Iterator
3047 i = m_clients.getIterator();
3048 i.atEnd() == false; i++)
3050 // Get client and check that it is valid
3051 RemoteClient *client = i.getNode()->getValue();
3052 assert(client->peer_id == i.getNode()->getKey());
3053 if(client->serialization_version == SER_FMT_VER_INVALID)
3056 SendChatMessage(client->peer_id, message);
3060 void Server::SendBlocks(float dtime)
3062 DSTACK(__FUNCTION_NAME);
3064 JMutexAutoLock envlock(m_env_mutex);
3066 core::array<PrioritySortedBlockTransfer> queue;
3068 s32 total_sending = 0;
3070 for(core::map<u16, RemoteClient*>::Iterator
3071 i = m_clients.getIterator();
3072 i.atEnd() == false; i++)
3074 RemoteClient *client = i.getNode()->getValue();
3075 assert(client->peer_id == i.getNode()->getKey());
3077 total_sending += client->SendingCount();
3079 if(client->serialization_version == SER_FMT_VER_INVALID)
3082 client->GetNextBlocks(this, dtime, queue);
3086 // Lowest priority number comes first.
3087 // Lowest is most important.
3090 JMutexAutoLock conlock(m_con_mutex);
3092 for(u32 i=0; i<queue.size(); i++)
3094 //TODO: Calculate limit dynamically
3095 if(total_sending >= g_settings.getS32
3096 ("max_simultaneous_block_sends_server_total"))
3099 PrioritySortedBlockTransfer q = queue[i];
3101 MapBlock *block = NULL;
3104 block = m_env.getMap().getBlockNoCreate(q.pos);
3106 catch(InvalidPositionException &e)
3111 RemoteClient *client = getClient(q.peer_id);
3113 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3115 client->SentBlock(q.pos);
3122 RemoteClient* Server::getClient(u16 peer_id)
3124 DSTACK(__FUNCTION_NAME);
3125 //JMutexAutoLock lock(m_con_mutex);
3126 core::map<u16, RemoteClient*>::Node *n;
3127 n = m_clients.find(peer_id);
3128 // A client should exist for all peers
3130 return n->getValue();
3133 Player *Server::emergePlayer(const char *name, const char *password,
3137 Try to get an existing player
3139 Player *player = m_env.getPlayer(name);
3142 // If player is already connected, cancel
3143 if(player->peer_id != 0)
3145 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3149 player->peer_id = peer_id;
3154 If player with the wanted peer_id already exists, cancel.
3156 if(m_env.getPlayer(peer_id) != NULL)
3158 dstream<<"emergePlayer(): Player with wrong name but same"
3159 " peer_id already exists"<<std::endl;
3167 player = new ServerRemotePlayer();
3168 //player->peer_id = c.peer_id;
3169 //player->peer_id = PEER_ID_INEXISTENT;
3170 player->peer_id = peer_id;
3171 player->updateName(name);
3177 dstream<<"Server: Finding spawn place for player \""
3178 <<player->getName()<<"\""<<std::endl;
3182 f32 groundheight = 0;
3183 // Try to find a good place a few times
3184 for(s32 i=0; i<500; i++)
3187 // We're going to try to throw the player to this position
3188 nodepos = v2s16(-range + (myrand()%(range*2)),
3189 -range + (myrand()%(range*2)));
3190 v2s16 sectorpos = getNodeSectorPos(nodepos);
3192 m_env.getMap().emergeSector(sectorpos);
3193 // Get ground height at point
3194 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3195 // The sector should have been generated -> groundheight exists
3196 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3197 // Don't go underwater
3198 if(groundheight < WATER_LEVEL)
3200 //dstream<<"-> Underwater"<<std::endl;
3203 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3204 // Get block at point
3206 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3207 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3208 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3209 // Don't go inside ground
3211 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3212 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3213 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3214 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3215 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3216 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3218 dstream<<"-> Inside ground"<<std::endl;
3222 }catch(InvalidPositionException &e)
3224 dstream<<"-> Invalid position"<<std::endl;
3225 // Ignore invalid position
3229 // Found a good place
3230 dstream<<"Searched through "<<i<<" places."<<std::endl;
3235 // If no suitable place was not found, go above water at least.
3236 if(groundheight < WATER_LEVEL)
3237 groundheight = WATER_LEVEL;
3239 player->setPosition(intToFloat(v3s16(
3246 Add player to environment
3249 m_env.addPlayer(player);
3252 Add stuff to inventory
3255 if(g_settings.getBool("creative_mode"))
3257 // Give some good picks
3259 InventoryItem *item = new ToolItem("STPick", 0);
3260 void* r = player->inventory.addItem("main", item);
3264 InventoryItem *item = new ToolItem("MesePick", 0);
3265 void* r = player->inventory.addItem("main", item);
3272 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3275 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3276 player->inventory.addItem("main", item);
3279 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3281 // Skip some materials
3282 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3285 InventoryItem *item = new MaterialItem(i, 1);
3286 player->inventory.addItem("main", item);
3290 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3291 void* r = player->inventory.addItem("main", item);
3298 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3299 void* r = player->inventory.addItem("main", item);
3303 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3304 void* r = player->inventory.addItem("main", item);
3308 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3309 void* r = player->inventory.addItem("main", item);
3313 InventoryItem *item = new CraftItem("Stick", 4);
3314 void* r = player->inventory.addItem("main", item);
3318 InventoryItem *item = new ToolItem("WPick", 32000);
3319 void* r = player->inventory.addItem("main", item);
3323 InventoryItem *item = new ToolItem("STPick", 32000);
3324 void* r = player->inventory.addItem("main", item);
3327 /*// Give some lights
3329 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3330 bool r = player->inventory.addItem("main", item);
3334 for(u16 i=0; i<4; i++)
3336 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3337 bool r = player->inventory.addItem("main", item);
3340 /*// Give some other stuff
3342 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3343 bool r = player->inventory.addItem("main", item);
3350 } // create new player
3354 void Server::UpdateBlockWaterPressure(MapBlock *block,
3355 core::map<v3s16, MapBlock*> &modified_blocks)
3357 MapVoxelManipulator v(&m_env.getMap());
3358 v.m_disable_water_climb =
3359 g_settings.getBool("disable_water_climb");
3361 VoxelArea area(block->getPosRelative(),
3362 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3366 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3368 catch(ProcessingLimitException &e)
3370 dstream<<"Processing limit reached (1)"<<std::endl;
3373 v.blitBack(modified_blocks);
3377 void Server::handlePeerChange(PeerChange &c)
3379 JMutexAutoLock envlock(m_env_mutex);
3380 JMutexAutoLock conlock(m_con_mutex);
3382 if(c.type == PEER_ADDED)
3389 core::map<u16, RemoteClient*>::Node *n;
3390 n = m_clients.find(c.peer_id);
3391 // The client shouldn't already exist
3395 RemoteClient *client = new RemoteClient();
3396 client->peer_id = c.peer_id;
3397 m_clients.insert(client->peer_id, client);
3400 else if(c.type == PEER_REMOVED)
3407 core::map<u16, RemoteClient*>::Node *n;
3408 n = m_clients.find(c.peer_id);
3409 // The client should exist
3412 // Collect information about leaving in chat
3413 std::wstring message;
3415 std::wstring name = L"unknown";
3416 Player *player = m_env.getPlayer(c.peer_id);
3418 name = narrow_to_wide(player->getName());
3422 message += L" left game";
3424 message += L" (timed out)";
3429 m_env.removePlayer(c.peer_id);
3432 // Set player client disconnected
3434 Player *player = m_env.getPlayer(c.peer_id);
3436 player->peer_id = 0;
3440 delete m_clients[c.peer_id];
3441 m_clients.remove(c.peer_id);
3443 // Send player info to all remaining clients
3446 // Send leave chat message to all remaining clients
3447 BroadcastChatMessage(message);
3456 void Server::handlePeerChanges()
3458 while(m_peer_change_queue.size() > 0)
3460 PeerChange c = m_peer_change_queue.pop_front();
3462 dout_server<<"Server: Handling peer change: "
3463 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3466 handlePeerChange(c);
3470 void dedicated_server_loop(Server &server)
3472 DSTACK(__FUNCTION_NAME);
3474 std::cout<<std::endl;
3475 std::cout<<"========================"<<std::endl;
3476 std::cout<<"Running dedicated server"<<std::endl;
3477 std::cout<<"========================"<<std::endl;
3478 std::cout<<std::endl;
3482 // This is kind of a hack but can be done like this
3483 // because server.step() is very light
3487 static int counter = 0;
3493 core::list<PlayerInfo> list = server.getPlayerInfo();
3494 core::list<PlayerInfo>::Iterator i;
3495 static u32 sum_old = 0;
3496 u32 sum = PIChecksum(list);
3499 std::cout<<DTIME<<"Player info:"<<std::endl;
3500 for(i=list.begin(); i!=list.end(); i++)
3502 i->PrintLine(&std::cout);