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."
1970 <<" Adding block to emerge queue."
1972 m_emerge_queue.addBlock(peer_id,
1973 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1977 //TODO: Send to only other clients
1980 Send the removal to all other clients
1985 SharedBuffer<u8> reply(replysize);
1986 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1987 writeS16(&reply[2], p_under.X);
1988 writeS16(&reply[4], p_under.Y);
1989 writeS16(&reply[6], p_under.Z);
1991 for(core::map<u16, RemoteClient*>::Iterator
1992 i = m_clients.getIterator();
1993 i.atEnd() == false; i++)
1995 // Get client and check that it is valid
1996 RemoteClient *client = i.getNode()->getValue();
1997 assert(client->peer_id == i.getNode()->getKey());
1998 if(client->serialization_version == SER_FMT_VER_INVALID)
2001 // Don't send if it's the same one
2002 if(peer_id == client->peer_id)
2006 m_con.Send(client->peer_id, 0, reply, true);
2010 Update and send inventory
2013 if(g_settings.getBool("creative_mode") == false)
2018 InventoryList *mlist = player->inventory.getList("main");
2021 InventoryItem *item = mlist->getItem(item_i);
2022 if(item && (std::string)item->getName() == "ToolItem")
2024 ToolItem *titem = (ToolItem*)item;
2025 std::string toolname = titem->getToolName();
2027 // Get digging properties for material and tool
2028 DiggingProperties prop =
2029 getDiggingProperties(material, toolname);
2031 if(prop.diggable == false)
2033 derr_server<<"Server: WARNING: Player digged"
2034 <<" with impossible material + tool"
2035 <<" combination"<<std::endl;
2038 bool weared_out = titem->addWear(prop.wear);
2042 mlist->deleteItem(item_i);
2048 Add digged item to inventory
2050 InventoryItem *item = new MaterialItem(material, 1);
2051 player->inventory.addItem("main", item);
2056 SendInventory(player->peer_id);
2061 (this takes some time so it is done after the quick stuff)
2063 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2070 // Update water pressure around modification
2071 // This also adds it to m_flow_active_nodes if appropriate
2073 MapVoxelManipulator v(&m_env.getMap());
2074 v.m_disable_water_climb =
2075 g_settings.getBool("disable_water_climb");
2077 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2081 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2083 catch(ProcessingLimitException &e)
2085 dstream<<"Processing limit reached (1)"<<std::endl;
2088 v.blitBack(modified_blocks);
2095 else if(action == 1)
2098 InventoryList *ilist = player->inventory.getList("main");
2103 InventoryItem *item = ilist->getItem(item_i);
2105 // If there is no item, it is not possible to add it anywhere
2110 Handle material items
2112 if(std::string("MaterialItem") == item->getName())
2115 // Don't add a node if this is not a free space
2116 MapNode n2 = m_env.getMap().getNode(p_over);
2117 if(content_buildable_to(n2.d) == false)
2120 catch(InvalidPositionException &e)
2122 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2123 <<" Adding block to emerge queue."
2125 m_emerge_queue.addBlock(peer_id,
2126 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2130 // Reset build time counter
2131 getClient(peer->id)->m_time_from_building.set(0.0);
2134 MaterialItem *mitem = (MaterialItem*)item;
2136 n.d = mitem->getMaterial();
2137 if(content_directional(n.d))
2138 n.dir = packDir(p_under - p_over);
2142 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2143 SharedBuffer<u8> reply(replysize);
2144 writeU16(&reply[0], TOCLIENT_ADDNODE);
2145 writeS16(&reply[2], p_over.X);
2146 writeS16(&reply[4], p_over.Y);
2147 writeS16(&reply[6], p_over.Z);
2148 n.serialize(&reply[8], peer_ser_ver);
2150 m_con.SendToAll(0, reply, true);
2155 InventoryList *ilist = player->inventory.getList("main");
2156 if(g_settings.getBool("creative_mode") == false && ilist)
2158 // Remove from inventory and send inventory
2159 if(mitem->getCount() == 1)
2160 ilist->deleteItem(item_i);
2164 SendInventory(peer_id);
2170 This takes some time so it is done after the quick stuff
2172 core::map<v3s16, MapBlock*> modified_blocks;
2173 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2179 InventoryList *ilist = player->inventory.getList("main");
2180 if(g_settings.getBool("creative_mode") == false && ilist)
2182 // Remove from inventory and send inventory
2183 if(mitem->getCount() == 1)
2184 ilist->deleteItem(item_i);
2188 SendInventory(peer_id);
2194 This takes some time so it is done after the quick stuff
2196 core::map<v3s16, MapBlock*> modified_blocks;
2197 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2200 Set the modified blocks unsent for all the clients
2203 //JMutexAutoLock lock2(m_con_mutex);
2205 for(core::map<u16, RemoteClient*>::Iterator
2206 i = m_clients.getIterator();
2207 i.atEnd() == false; i++)
2209 RemoteClient *client = i.getNode()->getValue();
2211 if(modified_blocks.size() > 0)
2213 // Remove block from sent history
2214 client->SetBlocksNotSent(modified_blocks);
2224 // Update water pressure around modification
2225 // This also adds it to m_flow_active_nodes if appropriate
2227 MapVoxelManipulator v(&m_env.getMap());
2228 v.m_disable_water_climb =
2229 g_settings.getBool("disable_water_climb");
2231 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2235 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2237 catch(ProcessingLimitException &e)
2239 dstream<<"Processing limit reached (1)"<<std::endl;
2242 v.blitBack(modified_blocks);
2250 v3s16 blockpos = getNodeBlockPos(p_over);
2252 MapBlock *block = NULL;
2255 block = m_env.getMap().getBlockNoCreate(blockpos);
2257 catch(InvalidPositionException &e)
2259 derr_server<<"Error while placing object: "
2260 "block not found"<<std::endl;
2264 v3s16 block_pos_i_on_map = block->getPosRelative();
2265 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2267 v3f pos = intToFloat(p_over);
2268 pos -= block_pos_f_on_map;
2270 /*dout_server<<"pos="
2271 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2274 MapBlockObject *obj = NULL;
2277 Handle block object items
2279 if(std::string("MBOItem") == item->getName())
2281 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2283 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2284 "inventorystring=\""
2285 <<oitem->getInventoryString()
2286 <<"\""<<std::endl;*/
2288 obj = oitem->createObject
2289 (pos, player->getYaw(), player->getPitch());
2296 dout_server<<"Placing a miscellaneous item on map"
2299 Create an ItemObject that contains the item.
2301 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2302 std::ostringstream os(std::ios_base::binary);
2303 item->serialize(os);
2304 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2305 iobj->setItemString(os.str());
2311 derr_server<<"WARNING: item resulted in NULL object, "
2312 <<"not placing onto map"
2317 block->addObject(obj);
2319 dout_server<<"Placed object"<<std::endl;
2321 InventoryList *ilist = player->inventory.getList("main");
2322 if(g_settings.getBool("creative_mode") == false && ilist)
2324 // Remove from inventory and send inventory
2325 ilist->deleteItem(item_i);
2327 SendInventory(peer_id);
2335 Catch invalid actions
2339 derr_server<<"WARNING: Server: Invalid action "
2340 <<action<<std::endl;
2344 else if(command == TOSERVER_RELEASE)
2353 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2356 else if(command == TOSERVER_SIGNTEXT)
2365 std::string datastring((char*)&data[2], datasize-2);
2366 std::istringstream is(datastring, std::ios_base::binary);
2369 is.read((char*)buf, 6);
2370 v3s16 blockpos = readV3S16(buf);
2371 is.read((char*)buf, 2);
2372 s16 id = readS16(buf);
2373 is.read((char*)buf, 2);
2374 u16 textlen = readU16(buf);
2376 for(u16 i=0; i<textlen; i++)
2378 is.read((char*)buf, 1);
2379 text += (char)buf[0];
2382 MapBlock *block = NULL;
2385 block = m_env.getMap().getBlockNoCreate(blockpos);
2387 catch(InvalidPositionException &e)
2389 derr_server<<"Error while setting sign text: "
2390 "block not found"<<std::endl;
2394 MapBlockObject *obj = block->getObject(id);
2397 derr_server<<"Error while setting sign text: "
2398 "object not found"<<std::endl;
2402 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2404 derr_server<<"Error while setting sign text: "
2405 "object is not a sign"<<std::endl;
2409 ((SignObject*)obj)->setText(text);
2411 obj->getBlock()->setChangedFlag();
2413 else if(command == TOSERVER_INVENTORY_ACTION)
2415 /*// Ignore inventory changes if in creative mode
2416 if(g_settings.getBool("creative_mode") == true)
2418 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2422 // Strip command and create a stream
2423 std::string datastring((char*)&data[2], datasize-2);
2424 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2425 std::istringstream is(datastring, std::ios_base::binary);
2427 InventoryAction *a = InventoryAction::deSerialize(is);
2431 Handle craftresult specially if not in creative mode
2433 bool disable_action = false;
2434 if(a->getType() == IACTION_MOVE
2435 && g_settings.getBool("creative_mode") == false)
2437 IMoveAction *ma = (IMoveAction*)a;
2438 // Don't allow moving anything to craftresult
2439 if(ma->to_name == "craftresult")
2442 disable_action = true;
2444 // When something is removed from craftresult
2445 if(ma->from_name == "craftresult")
2447 disable_action = true;
2448 // Remove stuff from craft
2449 InventoryList *clist = player->inventory.getList("craft");
2452 u16 count = ma->count;
2455 clist->decrementMaterials(count);
2458 // Feed action to player inventory
2459 a->apply(&player->inventory);
2462 // If something appeared in craftresult, throw it
2464 InventoryList *rlist = player->inventory.getList("craftresult");
2465 InventoryList *mlist = player->inventory.getList("main");
2466 if(rlist && mlist && rlist->getUsedSlots() == 1)
2468 InventoryItem *item1 = rlist->changeItem(0, NULL);
2469 mlist->addItem(item1);
2473 if(disable_action == false)
2475 // Feed action to player inventory
2476 a->apply(&player->inventory);
2481 SendInventory(player->peer_id);
2485 dstream<<"TOSERVER_INVENTORY_ACTION: "
2486 <<"InventoryAction::deSerialize() returned NULL"
2490 else if(command == TOSERVER_CHAT_MESSAGE)
2498 std::string datastring((char*)&data[2], datasize-2);
2499 std::istringstream is(datastring, std::ios_base::binary);
2502 is.read((char*)buf, 2);
2503 u16 len = readU16(buf);
2505 std::wstring message;
2506 for(u16 i=0; i<len; i++)
2508 is.read((char*)buf, 2);
2509 message += (wchar_t)readU16(buf);
2512 // Get player name of this client
2513 std::wstring name = narrow_to_wide(player->getName());
2515 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2517 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2520 Send the message to all other clients
2522 for(core::map<u16, RemoteClient*>::Iterator
2523 i = m_clients.getIterator();
2524 i.atEnd() == false; i++)
2526 // Get client and check that it is valid
2527 RemoteClient *client = i.getNode()->getValue();
2528 assert(client->peer_id == i.getNode()->getKey());
2529 if(client->serialization_version == SER_FMT_VER_INVALID)
2532 // Don't send if it's the same one
2533 if(peer_id == client->peer_id)
2536 SendChatMessage(client->peer_id, line);
2541 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2542 "unknown command "<<command<<std::endl;
2546 catch(SendFailedException &e)
2548 derr_server<<"Server::ProcessData(): SendFailedException: "
2554 /*void Server::Send(u16 peer_id, u16 channelnum,
2555 SharedBuffer<u8> data, bool reliable)
2557 JMutexAutoLock lock(m_con_mutex);
2558 m_con.Send(peer_id, channelnum, data, reliable);
2561 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2563 DSTACK(__FUNCTION_NAME);
2565 Create a packet with the block in the right format
2568 std::ostringstream os(std::ios_base::binary);
2569 block->serialize(os, ver);
2570 std::string s = os.str();
2571 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2573 u32 replysize = 8 + blockdata.getSize();
2574 SharedBuffer<u8> reply(replysize);
2575 v3s16 p = block->getPos();
2576 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2577 writeS16(&reply[2], p.X);
2578 writeS16(&reply[4], p.Y);
2579 writeS16(&reply[6], p.Z);
2580 memcpy(&reply[8], *blockdata, blockdata.getSize());
2582 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2583 <<": \tpacket size: "<<replysize<<std::endl;*/
2588 m_con.Send(peer_id, 1, reply, true);
2591 core::list<PlayerInfo> Server::getPlayerInfo()
2593 DSTACK(__FUNCTION_NAME);
2594 JMutexAutoLock envlock(m_env_mutex);
2595 JMutexAutoLock conlock(m_con_mutex);
2597 core::list<PlayerInfo> list;
2599 core::list<Player*> players = m_env.getPlayers();
2601 core::list<Player*>::Iterator i;
2602 for(i = players.begin();
2603 i != players.end(); i++)
2607 Player *player = *i;
2610 con::Peer *peer = m_con.GetPeer(player->peer_id);
2611 // Copy info from peer to info struct
2613 info.address = peer->address;
2614 info.avg_rtt = peer->avg_rtt;
2616 catch(con::PeerNotFoundException &e)
2618 // Set dummy peer info
2620 info.address = Address(0,0,0,0,0);
2624 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2625 info.position = player->getPosition();
2627 list.push_back(info);
2633 void Server::peerAdded(con::Peer *peer)
2635 DSTACK(__FUNCTION_NAME);
2636 dout_server<<"Server::peerAdded(): peer->id="
2637 <<peer->id<<std::endl;
2640 c.type = PEER_ADDED;
2641 c.peer_id = peer->id;
2643 m_peer_change_queue.push_back(c);
2646 void Server::deletingPeer(con::Peer *peer, bool timeout)
2648 DSTACK(__FUNCTION_NAME);
2649 dout_server<<"Server::deletingPeer(): peer->id="
2650 <<peer->id<<", timeout="<<timeout<<std::endl;
2653 c.type = PEER_REMOVED;
2654 c.peer_id = peer->id;
2655 c.timeout = timeout;
2656 m_peer_change_queue.push_back(c);
2659 void Server::SendObjectData(float dtime)
2661 DSTACK(__FUNCTION_NAME);
2663 core::map<v3s16, bool> stepped_blocks;
2665 for(core::map<u16, RemoteClient*>::Iterator
2666 i = m_clients.getIterator();
2667 i.atEnd() == false; i++)
2669 u16 peer_id = i.getNode()->getKey();
2670 RemoteClient *client = i.getNode()->getValue();
2671 assert(client->peer_id == peer_id);
2673 if(client->serialization_version == SER_FMT_VER_INVALID)
2676 client->SendObjectData(this, dtime, stepped_blocks);
2680 void Server::SendPlayerInfos()
2682 DSTACK(__FUNCTION_NAME);
2684 //JMutexAutoLock envlock(m_env_mutex);
2686 // Get connected players
2687 core::list<Player*> players = m_env.getPlayers(true);
2689 u32 player_count = players.getSize();
2690 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2692 SharedBuffer<u8> data(datasize);
2693 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2696 core::list<Player*>::Iterator i;
2697 for(i = players.begin();
2698 i != players.end(); i++)
2700 Player *player = *i;
2702 /*dstream<<"Server sending player info for player with "
2703 "peer_id="<<player->peer_id<<std::endl;*/
2705 writeU16(&data[start], player->peer_id);
2706 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2707 start += 2+PLAYERNAME_SIZE;
2710 //JMutexAutoLock conlock(m_con_mutex);
2713 m_con.SendToAll(0, data, true);
2731 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2737 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2743 enum ItemSpecType type;
2744 // Only other one of these is used
2750 items: a pointer to an array of 9 pointers to items
2751 specs: a pointer to an array of 9 ItemSpecs
2753 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2755 u16 items_min_x = 100;
2756 u16 items_max_x = 100;
2757 u16 items_min_y = 100;
2758 u16 items_max_y = 100;
2759 for(u16 y=0; y<3; y++)
2760 for(u16 x=0; x<3; x++)
2762 if(items[y*3 + x] == NULL)
2764 if(items_min_x == 100 || x < items_min_x)
2766 if(items_min_y == 100 || y < items_min_y)
2768 if(items_max_x == 100 || x > items_max_x)
2770 if(items_max_y == 100 || y > items_max_y)
2773 // No items at all, just return false
2774 if(items_min_x == 100)
2777 u16 items_w = items_max_x - items_min_x + 1;
2778 u16 items_h = items_max_y - items_min_y + 1;
2780 u16 specs_min_x = 100;
2781 u16 specs_max_x = 100;
2782 u16 specs_min_y = 100;
2783 u16 specs_max_y = 100;
2784 for(u16 y=0; y<3; y++)
2785 for(u16 x=0; x<3; x++)
2787 if(specs[y*3 + x].type == ITEM_NONE)
2789 if(specs_min_x == 100 || x < specs_min_x)
2791 if(specs_min_y == 100 || y < specs_min_y)
2793 if(specs_max_x == 100 || x > specs_max_x)
2795 if(specs_max_y == 100 || y > specs_max_y)
2798 // No specs at all, just return false
2799 if(specs_min_x == 100)
2802 u16 specs_w = specs_max_x - specs_min_x + 1;
2803 u16 specs_h = specs_max_y - specs_min_y + 1;
2806 if(items_w != specs_w || items_h != specs_h)
2809 for(u16 y=0; y<specs_h; y++)
2810 for(u16 x=0; x<specs_w; x++)
2812 u16 items_x = items_min_x + x;
2813 u16 items_y = items_min_y + y;
2814 u16 specs_x = specs_min_x + x;
2815 u16 specs_y = specs_min_y + y;
2816 InventoryItem *item = items[items_y * 3 + items_x];
2817 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2819 if(spec.type == ITEM_NONE)
2821 // Has to be no item
2827 // There should be an item
2831 std::string itemname = item->getName();
2833 if(spec.type == ITEM_MATERIAL)
2835 if(itemname != "MaterialItem")
2837 MaterialItem *mitem = (MaterialItem*)item;
2838 if(mitem->getMaterial() != spec.num)
2841 else if(spec.type == ITEM_CRAFT)
2843 if(itemname != "CraftItem")
2845 CraftItem *mitem = (CraftItem*)item;
2846 if(mitem->getSubName() != spec.name)
2849 else if(spec.type == ITEM_TOOL)
2851 // Not supported yet
2854 else if(spec.type == ITEM_MBO)
2856 // Not supported yet
2861 // Not supported yet
2869 void Server::SendInventory(u16 peer_id)
2871 DSTACK(__FUNCTION_NAME);
2873 Player* player = m_env.getPlayer(peer_id);
2876 Calculate crafting stuff
2878 if(g_settings.getBool("creative_mode") == false)
2880 InventoryList *clist = player->inventory.getList("craft");
2881 InventoryList *rlist = player->inventory.getList("craftresult");
2884 rlist->clearItems();
2888 InventoryItem *items[9];
2889 for(u16 i=0; i<9; i++)
2891 items[i] = clist->getItem(i);
2900 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2901 if(checkItemCombination(items, specs))
2903 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2912 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2913 if(checkItemCombination(items, specs))
2915 rlist->addItem(new CraftItem("Stick", 4));
2924 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2925 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2926 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2927 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2928 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2929 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2930 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2931 if(checkItemCombination(items, specs))
2933 rlist->addItem(new MapBlockObjectItem("Sign"));
2942 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2943 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2944 if(checkItemCombination(items, specs))
2946 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2955 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2956 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2957 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2958 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2959 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2960 if(checkItemCombination(items, specs))
2962 rlist->addItem(new ToolItem("WPick", 0));
2971 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2972 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2973 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2974 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2975 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2976 if(checkItemCombination(items, specs))
2978 rlist->addItem(new ToolItem("STPick", 0));
2987 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2988 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2989 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2990 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2991 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2992 if(checkItemCombination(items, specs))
2994 rlist->addItem(new ToolItem("MesePick", 0));
2999 } // if creative_mode == false
3005 std::ostringstream os;
3006 //os.imbue(std::locale("C"));
3008 player->inventory.serialize(os);
3010 std::string s = os.str();
3012 SharedBuffer<u8> data(s.size()+2);
3013 writeU16(&data[0], TOCLIENT_INVENTORY);
3014 memcpy(&data[2], s.c_str(), s.size());
3017 m_con.Send(peer_id, 0, data, true);
3020 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3022 DSTACK(__FUNCTION_NAME);
3024 std::ostringstream os(std::ios_base::binary);
3028 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3029 os.write((char*)buf, 2);
3032 writeU16(buf, message.size());
3033 os.write((char*)buf, 2);
3036 for(u32 i=0; i<message.size(); i++)
3040 os.write((char*)buf, 2);
3044 std::string s = os.str();
3045 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3047 m_con.Send(peer_id, 0, data, true);
3050 void Server::BroadcastChatMessage(const std::wstring &message)
3052 for(core::map<u16, RemoteClient*>::Iterator
3053 i = m_clients.getIterator();
3054 i.atEnd() == false; i++)
3056 // Get client and check that it is valid
3057 RemoteClient *client = i.getNode()->getValue();
3058 assert(client->peer_id == i.getNode()->getKey());
3059 if(client->serialization_version == SER_FMT_VER_INVALID)
3062 SendChatMessage(client->peer_id, message);
3066 void Server::SendBlocks(float dtime)
3068 DSTACK(__FUNCTION_NAME);
3070 JMutexAutoLock envlock(m_env_mutex);
3072 core::array<PrioritySortedBlockTransfer> queue;
3074 s32 total_sending = 0;
3076 for(core::map<u16, RemoteClient*>::Iterator
3077 i = m_clients.getIterator();
3078 i.atEnd() == false; i++)
3080 RemoteClient *client = i.getNode()->getValue();
3081 assert(client->peer_id == i.getNode()->getKey());
3083 total_sending += client->SendingCount();
3085 if(client->serialization_version == SER_FMT_VER_INVALID)
3088 client->GetNextBlocks(this, dtime, queue);
3092 // Lowest priority number comes first.
3093 // Lowest is most important.
3096 JMutexAutoLock conlock(m_con_mutex);
3098 for(u32 i=0; i<queue.size(); i++)
3100 //TODO: Calculate limit dynamically
3101 if(total_sending >= g_settings.getS32
3102 ("max_simultaneous_block_sends_server_total"))
3105 PrioritySortedBlockTransfer q = queue[i];
3107 MapBlock *block = NULL;
3110 block = m_env.getMap().getBlockNoCreate(q.pos);
3112 catch(InvalidPositionException &e)
3117 RemoteClient *client = getClient(q.peer_id);
3119 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3121 client->SentBlock(q.pos);
3128 RemoteClient* Server::getClient(u16 peer_id)
3130 DSTACK(__FUNCTION_NAME);
3131 //JMutexAutoLock lock(m_con_mutex);
3132 core::map<u16, RemoteClient*>::Node *n;
3133 n = m_clients.find(peer_id);
3134 // A client should exist for all peers
3136 return n->getValue();
3139 Player *Server::emergePlayer(const char *name, const char *password,
3143 Try to get an existing player
3145 Player *player = m_env.getPlayer(name);
3148 // If player is already connected, cancel
3149 if(player->peer_id != 0)
3151 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3155 player->peer_id = peer_id;
3160 If player with the wanted peer_id already exists, cancel.
3162 if(m_env.getPlayer(peer_id) != NULL)
3164 dstream<<"emergePlayer(): Player with wrong name but same"
3165 " peer_id already exists"<<std::endl;
3173 player = new ServerRemotePlayer();
3174 //player->peer_id = c.peer_id;
3175 //player->peer_id = PEER_ID_INEXISTENT;
3176 player->peer_id = peer_id;
3177 player->updateName(name);
3183 dstream<<"Server: Finding spawn place for player \""
3184 <<player->getName()<<"\""<<std::endl;
3188 f32 groundheight = 0;
3189 // Try to find a good place a few times
3190 for(s32 i=0; i<500; i++)
3193 // We're going to try to throw the player to this position
3194 nodepos = v2s16(-range + (myrand()%(range*2)),
3195 -range + (myrand()%(range*2)));
3196 v2s16 sectorpos = getNodeSectorPos(nodepos);
3198 m_env.getMap().emergeSector(sectorpos);
3199 // Get ground height at point
3200 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3201 // The sector should have been generated -> groundheight exists
3202 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3203 // Don't go underwater
3204 if(groundheight < WATER_LEVEL)
3206 //dstream<<"-> Underwater"<<std::endl;
3209 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3210 // Get block at point
3212 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3213 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3214 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3215 // Don't go inside ground
3217 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3218 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3219 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3220 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3221 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3222 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3224 dstream<<"-> Inside ground"<<std::endl;
3228 }catch(InvalidPositionException &e)
3230 dstream<<"-> Invalid position"<<std::endl;
3231 // Ignore invalid position
3235 // Found a good place
3236 dstream<<"Searched through "<<i<<" places."<<std::endl;
3241 // If no suitable place was not found, go above water at least.
3242 if(groundheight < WATER_LEVEL)
3243 groundheight = WATER_LEVEL;
3245 player->setPosition(intToFloat(v3s16(
3252 Add player to environment
3255 m_env.addPlayer(player);
3258 Add stuff to inventory
3261 if(g_settings.getBool("creative_mode"))
3263 // Give some good picks
3265 InventoryItem *item = new ToolItem("STPick", 0);
3266 void* r = player->inventory.addItem("main", item);
3270 InventoryItem *item = new ToolItem("MesePick", 0);
3271 void* r = player->inventory.addItem("main", item);
3278 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3281 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3282 player->inventory.addItem("main", item);
3285 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3287 // Skip some materials
3288 if(i == CONTENT_WATER || i == CONTENT_TORCH)
3291 InventoryItem *item = new MaterialItem(i, 1);
3292 player->inventory.addItem("main", item);
3296 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3297 void* r = player->inventory.addItem("main", item);
3304 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3305 void* r = player->inventory.addItem("main", item);
3309 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3310 void* r = player->inventory.addItem("main", item);
3314 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3315 void* r = player->inventory.addItem("main", item);
3319 InventoryItem *item = new CraftItem("Stick", 4);
3320 void* r = player->inventory.addItem("main", item);
3324 InventoryItem *item = new ToolItem("WPick", 32000);
3325 void* r = player->inventory.addItem("main", item);
3329 InventoryItem *item = new ToolItem("STPick", 32000);
3330 void* r = player->inventory.addItem("main", item);
3333 /*// Give some lights
3335 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3336 bool r = player->inventory.addItem("main", item);
3340 for(u16 i=0; i<4; i++)
3342 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3343 bool r = player->inventory.addItem("main", item);
3346 /*// Give some other stuff
3348 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3349 bool r = player->inventory.addItem("main", item);
3356 } // create new player
3360 void Server::UpdateBlockWaterPressure(MapBlock *block,
3361 core::map<v3s16, MapBlock*> &modified_blocks)
3363 MapVoxelManipulator v(&m_env.getMap());
3364 v.m_disable_water_climb =
3365 g_settings.getBool("disable_water_climb");
3367 VoxelArea area(block->getPosRelative(),
3368 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3372 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3374 catch(ProcessingLimitException &e)
3376 dstream<<"Processing limit reached (1)"<<std::endl;
3379 v.blitBack(modified_blocks);
3383 void Server::handlePeerChange(PeerChange &c)
3385 JMutexAutoLock envlock(m_env_mutex);
3386 JMutexAutoLock conlock(m_con_mutex);
3388 if(c.type == PEER_ADDED)
3395 core::map<u16, RemoteClient*>::Node *n;
3396 n = m_clients.find(c.peer_id);
3397 // The client shouldn't already exist
3401 RemoteClient *client = new RemoteClient();
3402 client->peer_id = c.peer_id;
3403 m_clients.insert(client->peer_id, client);
3406 else if(c.type == PEER_REMOVED)
3413 core::map<u16, RemoteClient*>::Node *n;
3414 n = m_clients.find(c.peer_id);
3415 // The client should exist
3418 // Collect information about leaving in chat
3419 std::wstring message;
3421 std::wstring name = L"unknown";
3422 Player *player = m_env.getPlayer(c.peer_id);
3424 name = narrow_to_wide(player->getName());
3428 message += L" left game";
3430 message += L" (timed out)";
3435 m_env.removePlayer(c.peer_id);
3438 // Set player client disconnected
3440 Player *player = m_env.getPlayer(c.peer_id);
3442 player->peer_id = 0;
3446 delete m_clients[c.peer_id];
3447 m_clients.remove(c.peer_id);
3449 // Send player info to all remaining clients
3452 // Send leave chat message to all remaining clients
3453 BroadcastChatMessage(message);
3462 void Server::handlePeerChanges()
3464 while(m_peer_change_queue.size() > 0)
3466 PeerChange c = m_peer_change_queue.pop_front();
3468 dout_server<<"Server: Handling peer change: "
3469 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3472 handlePeerChange(c);
3476 void dedicated_server_loop(Server &server)
3478 DSTACK(__FUNCTION_NAME);
3480 std::cout<<std::endl;
3481 std::cout<<"========================"<<std::endl;
3482 std::cout<<"Running dedicated server"<<std::endl;
3483 std::cout<<"========================"<<std::endl;
3484 std::cout<<std::endl;
3488 // This is kind of a hack but can be done like this
3489 // because server.step() is very light
3493 static int counter = 0;
3499 core::list<PlayerInfo> list = server.getPlayerInfo();
3500 core::list<PlayerInfo>::Iterator i;
3501 static u32 sum_old = 0;
3502 u32 sum = PIChecksum(list);
3505 std::cout<<DTIME<<"Player info:"<<std::endl;
3506 for(i=list.begin(); i!=list.end(); i++)
3508 i->PrintLine(&std::cout);