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"
36 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
38 void * ServerThread::Thread()
42 DSTACK(__FUNCTION_NAME);
44 BEGIN_DEBUG_EXCEPTION_HANDLER
49 m_server->AsyncRunStep();
51 //dout_server<<"Running m_server->Receive()"<<std::endl;
54 catch(con::NoIncomingDataException &e)
57 catch(con::PeerNotFoundException &e)
59 dout_server<<"Server: PeerNotFoundException"<<std::endl;
63 END_DEBUG_EXCEPTION_HANDLER
68 void * EmergeThread::Thread()
72 DSTACK(__FUNCTION_NAME);
76 BEGIN_DEBUG_EXCEPTION_HANDLER
79 Get block info from queue, emerge them and send them
82 After queue is empty, exit.
86 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
90 SharedPtr<QueuedBlockEmerge> q(qptr);
94 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
96 //TimeTaker timer("block emerge");
99 Try to emerge it from somewhere.
101 If it is only wanted as optional, only loading from disk
106 Check if any peer wants it as non-optional. In that case it
109 Also decrement the emerge queue count in clients.
112 bool optional = true;
115 core::map<u16, u8>::Iterator i;
116 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
118 //u16 peer_id = i.getNode()->getKey();
121 u8 flags = i.getNode()->getValue();
122 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
128 /*dstream<<"EmergeThread: p="
129 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
130 <<"optional="<<optional<<std::endl;*/
132 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
134 core::map<v3s16, MapBlock*> changed_blocks;
135 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
137 MapBlock *block = NULL;
138 bool got_block = true;
139 core::map<v3s16, MapBlock*> modified_blocks;
143 //TimeTaker envlockwaittimer("block emerge envlock wait time");
146 JMutexAutoLock envlock(m_server->m_env_mutex);
148 //envlockwaittimer.stop();
150 //TimeTaker timer("block emerge (while env locked)");
153 bool only_from_disk = false;
156 only_from_disk = true;
158 // First check if the block already exists
159 //block = map.getBlockNoCreate(p);
163 //dstream<<"Calling emergeBlock"<<std::endl;
164 block = map.emergeBlock(
168 lighting_invalidated_blocks);
172 EXPERIMENTAL: Create a few other blocks too
179 lighting_invalidated_blocks);
185 lighting_invalidated_blocks);
191 lighting_invalidated_blocks);
197 lighting_invalidated_blocks);
202 // If it is a dummy, block was not found on disk
205 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
208 if(only_from_disk == false)
210 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
215 catch(InvalidPositionException &e)
218 // This happens when position is over limit.
224 if(debug && changed_blocks.size() > 0)
226 dout_server<<DTIME<<"Got changed_blocks: ";
227 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
228 i.atEnd() == false; i++)
230 MapBlock *block = i.getNode()->getValue();
231 v3s16 p = block->getPos();
232 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
234 dout_server<<std::endl;
238 Collect a list of blocks that have been modified in
239 addition to the fetched one.
242 // Add all the "changed blocks" to modified_blocks
243 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
244 i.atEnd() == false; i++)
246 MapBlock *block = i.getNode()->getValue();
247 modified_blocks.insert(block->getPos(), block);
250 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
251 <<" blocks"<<std::endl;*/
253 //TimeTaker timer("** updateLighting");
255 // Update lighting without locking the environment mutex,
256 // add modified blocks to changed blocks
257 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
259 // If we got no block, there should be no invalidated blocks
262 assert(lighting_invalidated_blocks.size() == 0);
268 Set sent status of modified blocks on clients
271 // NOTE: Server's clients are also behind the connection mutex
272 JMutexAutoLock lock(m_server->m_con_mutex);
275 Add the originally fetched block to the modified list
279 modified_blocks.insert(p, block);
283 Set the modified blocks unsent for all the clients
286 for(core::map<u16, RemoteClient*>::Iterator
287 i = m_server->m_clients.getIterator();
288 i.atEnd() == false; i++)
290 RemoteClient *client = i.getNode()->getValue();
292 if(modified_blocks.size() > 0)
294 // Remove block from sent history
295 client->SetBlocksNotSent(modified_blocks);
301 END_DEBUG_EXCEPTION_HANDLER
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307 core::array<PrioritySortedBlockTransfer> &dest)
309 DSTACK(__FUNCTION_NAME);
313 JMutexAutoLock lock(m_blocks_sent_mutex);
314 m_nearest_unsent_reset_timer += dtime;
317 // Won't send anything if already sending
319 JMutexAutoLock lock(m_blocks_sending_mutex);
321 if(m_blocks_sending.size() >= g_settings.getU16
322 ("max_simultaneous_block_sends_per_client"))
324 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
329 Player *player = server->m_env.getPlayer(peer_id);
331 assert(player != NULL);
333 v3f playerpos = player->getPosition();
334 v3f playerspeed = player->getSpeed();
336 v3s16 center_nodepos = floatToInt(playerpos);
338 v3s16 center = getNodeBlockPos(center_nodepos);
340 // Camera position and direction
342 playerpos + v3f(0, BS+BS/2, 0);
343 v3f camera_dir = v3f(0,0,1);
344 camera_dir.rotateYZBy(player->getPitch());
345 camera_dir.rotateXZBy(player->getYaw());
348 Get the starting value of the block finder radius.
350 s16 last_nearest_unsent_d;
353 JMutexAutoLock lock(m_blocks_sent_mutex);
355 if(m_last_center != center)
357 m_nearest_unsent_d = 0;
358 m_last_center = center;
361 /*dstream<<"m_nearest_unsent_reset_timer="
362 <<m_nearest_unsent_reset_timer<<std::endl;*/
363 if(m_nearest_unsent_reset_timer > 5.0)
365 m_nearest_unsent_reset_timer = 0;
366 m_nearest_unsent_d = 0;
367 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
370 last_nearest_unsent_d = m_nearest_unsent_d;
372 d_start = m_nearest_unsent_d;
375 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
376 ("max_simultaneous_block_sends_per_client");
377 u16 maximum_simultaneous_block_sends =
378 maximum_simultaneous_block_sends_setting;
381 Check the time from last addNode/removeNode.
383 Decrease send rate if player is building stuff.
386 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
387 m_time_from_building.m_value += dtime;
388 /*if(m_time_from_building.m_value
389 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
390 if(m_time_from_building.m_value < g_settings.getFloat(
391 "full_block_send_enable_min_time_from_building"))
393 maximum_simultaneous_block_sends
394 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
398 u32 num_blocks_selected;
400 JMutexAutoLock lock(m_blocks_sending_mutex);
401 num_blocks_selected = m_blocks_sending.size();
405 next time d will be continued from the d from which the nearest
406 unsent block was found this time.
408 This is because not necessarily any of the blocks found this
409 time are actually sent.
411 s32 new_nearest_unsent_d = -1;
413 // Serialization version used
414 //u8 ser_version = serialization_version;
416 //bool has_incomplete_blocks = false;
418 s16 d_max = g_settings.getS16("max_block_send_distance");
419 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
421 //dstream<<"Starting from "<<d_start<<std::endl;
423 for(s16 d = d_start; d <= d_max; d++)
425 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
427 //if(has_incomplete_blocks == false)
429 JMutexAutoLock lock(m_blocks_sent_mutex);
431 If m_nearest_unsent_d was changed by the EmergeThread
432 (it can change it to 0 through SetBlockNotSent),
434 Else update m_nearest_unsent_d
436 if(m_nearest_unsent_d != last_nearest_unsent_d)
438 d = m_nearest_unsent_d;
439 last_nearest_unsent_d = m_nearest_unsent_d;
444 Get the border/face dot coordinates of a "d-radiused"
447 core::list<v3s16> list;
448 getFacePositions(list, d);
450 core::list<v3s16>::Iterator li;
451 for(li=list.begin(); li!=list.end(); li++)
453 v3s16 p = *li + center;
457 - Don't allow too many simultaneous transfers
458 - EXCEPT when the blocks are very close
460 Also, don't send blocks that are already flying.
463 u16 maximum_simultaneous_block_sends_now =
464 maximum_simultaneous_block_sends;
466 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
468 maximum_simultaneous_block_sends_now =
469 maximum_simultaneous_block_sends_setting;
473 JMutexAutoLock lock(m_blocks_sending_mutex);
475 // Limit is dynamically lowered when building
476 if(num_blocks_selected
477 >= maximum_simultaneous_block_sends_now)
479 /*dstream<<"Not sending more blocks. Queue full. "
480 <<m_blocks_sending.size()
485 if(m_blocks_sending.find(p) != NULL)
492 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
493 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
494 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
495 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
496 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
497 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
500 // If this is true, inexistent block will be made from scratch
501 bool generate = d <= d_max_gen;
504 /*// Limit the generating area vertically to 2/3
505 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
508 // Limit the send area vertically to 2/3
509 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
515 If block is far away, don't generate it unless it is
518 NOTE: We can't know the ground level this way with the
524 MapSector *sector = NULL;
527 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
529 catch(InvalidPositionException &e)
535 // Get center ground height in nodes
536 f32 gh = sector->getGroundHeight(
537 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
538 // Block center y in nodes
539 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
540 // If differs a lot, don't generate
541 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
548 Don't draw if not in sight
551 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
557 Don't send already sent blocks
560 JMutexAutoLock lock(m_blocks_sent_mutex);
562 if(m_blocks_sent.find(p) != NULL)
567 Check if map has this block
569 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
571 bool surely_not_found_on_disk = false;
572 bool block_is_invalid = false;
575 /*if(block->isIncomplete())
577 has_incomplete_blocks = true;
583 surely_not_found_on_disk = true;
586 if(block->isValid() == false)
588 block_is_invalid = true;
592 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
593 v2s16 chunkpos = map->sector_to_chunk(p2d);
594 if(map->chunkNonVolatile(chunkpos) == false)
595 block_is_invalid = true;
596 /*MapChunk *chunk = map->getChunk(chunkpos);
598 block_is_invalid = true;
599 else if(chunk->getIsVolatile() == true)
600 block_is_invalid = true;*/
604 If block has been marked to not exist on disk (dummy)
605 and generating new ones is not wanted, skip block.
607 if(generate == false && surely_not_found_on_disk == true)
614 Record the lowest d from which a a block has been
615 found being not sent and possibly to exist
617 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
619 new_nearest_unsent_d = d;
623 Add inexistent block to emerge queue.
625 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
627 //dstream<<"asd"<<std::endl;
629 /*SharedPtr<JMutexAutoLock> lock
630 (m_num_blocks_in_emerge_queue.getLock());*/
632 //TODO: Get value from somewhere
633 // Allow only one block in emerge queue
634 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
636 //dstream<<"Adding block to emerge queue"<<std::endl;
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/10)<<","<<(position.Y/10)
966 <<","<<(position.Z/10)<<") ";
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
993 m_env(new ServerMap(mapsavedir), dout_server),
994 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
996 m_emergethread(this),
999 m_time_of_day_send_timer(0),
1001 m_mapsavedir(mapsavedir)
1003 //m_flowwater_timer = 0.0;
1004 m_liquid_transform_timer = 0.0;
1005 m_print_info_timer = 0.0;
1006 m_objectdata_timer = 0.0;
1007 m_emergethread_trigger_timer = 0.0;
1008 m_savemap_timer = 0.0;
1012 m_step_dtime_mutex.Init();
1016 m_env.deSerializePlayers(m_mapsavedir);
1022 m_env.serializePlayers(m_mapsavedir);
1027 JMutexAutoLock clientslock(m_con_mutex);
1029 for(core::map<u16, RemoteClient*>::Iterator
1030 i = m_clients.getIterator();
1031 i.atEnd() == false; i++)
1034 // NOTE: These are removed by env destructor
1036 u16 peer_id = i.getNode()->getKey();
1037 JMutexAutoLock envlock(m_env_mutex);
1038 m_env.removePlayer(peer_id);
1042 delete i.getNode()->getValue();
1046 void Server::start(unsigned short port)
1048 DSTACK(__FUNCTION_NAME);
1049 // Stop thread if already running
1052 // Initialize connection
1053 m_con.setTimeoutMs(30);
1057 m_thread.setRun(true);
1060 dout_server<<"Server started on port "<<port<<std::endl;
1065 DSTACK(__FUNCTION_NAME);
1066 // Stop threads (set run=false first so both start stopping)
1067 m_thread.setRun(false);
1068 m_emergethread.setRun(false);
1070 m_emergethread.stop();
1072 dout_server<<"Server threads stopped"<<std::endl;
1075 void Server::step(float dtime)
1077 DSTACK(__FUNCTION_NAME);
1082 JMutexAutoLock lock(m_step_dtime_mutex);
1083 m_step_dtime += dtime;
1087 void Server::AsyncRunStep()
1089 DSTACK(__FUNCTION_NAME);
1093 JMutexAutoLock lock1(m_step_dtime_mutex);
1094 dtime = m_step_dtime;
1097 // Send blocks to clients
1103 //dstream<<"Server steps "<<dtime<<std::endl;
1104 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1107 JMutexAutoLock lock1(m_step_dtime_mutex);
1108 m_step_dtime -= dtime;
1115 m_uptime.set(m_uptime.get() + dtime);
1119 Update m_time_of_day
1122 m_time_counter += dtime;
1123 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1124 u32 units = (u32)(m_time_counter*speed);
1125 m_time_counter -= (f32)units / speed;
1126 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1128 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1131 Send to clients at constant intervals
1134 m_time_of_day_send_timer -= dtime;
1135 if(m_time_of_day_send_timer < 0.0)
1137 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1139 //JMutexAutoLock envlock(m_env_mutex);
1140 JMutexAutoLock conlock(m_con_mutex);
1142 for(core::map<u16, RemoteClient*>::Iterator
1143 i = m_clients.getIterator();
1144 i.atEnd() == false; i++)
1146 RemoteClient *client = i.getNode()->getValue();
1147 //Player *player = m_env.getPlayer(client->peer_id);
1149 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1150 m_time_of_day.get());
1152 m_con.Send(client->peer_id, 0, data, true);
1158 // Process connection's timeouts
1159 JMutexAutoLock lock2(m_con_mutex);
1160 m_con.RunTimeouts(dtime);
1164 // This has to be called so that the client list gets synced
1165 // with the peer list of the connection
1166 handlePeerChanges();
1171 // This also runs Map's timers
1172 JMutexAutoLock lock(m_env_mutex);
1183 m_liquid_transform_timer += dtime;
1184 if(m_liquid_transform_timer >= 1.00)
1186 m_liquid_transform_timer -= 1.00;
1188 JMutexAutoLock lock(m_env_mutex);
1190 core::map<v3s16, MapBlock*> modified_blocks;
1191 m_env.getMap().transformLiquids(modified_blocks);
1196 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1197 ServerMap &map = ((ServerMap&)m_env.getMap());
1198 map.updateLighting(modified_blocks, lighting_modified_blocks);
1200 // Add blocks modified by lighting to modified_blocks
1201 for(core::map<v3s16, MapBlock*>::Iterator
1202 i = lighting_modified_blocks.getIterator();
1203 i.atEnd() == false; i++)
1205 MapBlock *block = i.getNode()->getValue();
1206 modified_blocks.insert(block->getPos(), block);
1210 Set the modified blocks unsent for all the clients
1213 JMutexAutoLock lock2(m_con_mutex);
1215 for(core::map<u16, RemoteClient*>::Iterator
1216 i = m_clients.getIterator();
1217 i.atEnd() == false; i++)
1219 RemoteClient *client = i.getNode()->getValue();
1221 if(modified_blocks.size() > 0)
1223 // Remove block from sent history
1224 client->SetBlocksNotSent(modified_blocks);
1229 // Periodically print some info
1231 float &counter = m_print_info_timer;
1237 JMutexAutoLock lock2(m_con_mutex);
1239 for(core::map<u16, RemoteClient*>::Iterator
1240 i = m_clients.getIterator();
1241 i.atEnd() == false; i++)
1243 //u16 peer_id = i.getNode()->getKey();
1244 RemoteClient *client = i.getNode()->getValue();
1245 client->PrintInfo(std::cout);
1253 NOTE: Some of this could be moved to RemoteClient
1257 JMutexAutoLock envlock(m_env_mutex);
1258 JMutexAutoLock conlock(m_con_mutex);
1260 for(core::map<u16, RemoteClient*>::Iterator
1261 i = m_clients.getIterator();
1262 i.atEnd() == false; i++)
1264 RemoteClient *client = i.getNode()->getValue();
1265 Player *player = m_env.getPlayer(client->peer_id);
1267 JMutexAutoLock digmutex(client->m_dig_mutex);
1269 if(client->m_dig_tool_item == -1)
1272 client->m_dig_time_remaining -= dtime;
1274 if(client->m_dig_time_remaining > 0)
1276 client->m_time_from_building.set(0.0);
1280 v3s16 p_under = client->m_dig_position;
1282 // Mandatory parameter; actually used for nothing
1283 core::map<v3s16, MapBlock*> modified_blocks;
1289 // Get material at position
1290 material = m_env.getMap().getNode(p_under).d;
1291 // If it's not diggable, do nothing
1292 if(content_diggable(material) == false)
1294 derr_server<<"Server: Not finishing digging: Node not diggable"
1296 client->m_dig_tool_item = -1;
1300 catch(InvalidPositionException &e)
1302 derr_server<<"Server: Not finishing digging: Node not found"
1304 client->m_dig_tool_item = -1;
1310 SharedBuffer<u8> reply(replysize);
1311 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1312 writeS16(&reply[2], p_under.X);
1313 writeS16(&reply[4], p_under.Y);
1314 writeS16(&reply[6], p_under.Z);
1316 m_con.SendToAll(0, reply, true);
1318 if(g_settings.getBool("creative_mode") == false)
1320 // Add to inventory and send inventory
1321 InventoryItem *item = new MaterialItem(material, 1);
1322 player->inventory.addItem("main", item);
1323 SendInventory(player->peer_id);
1328 (this takes some time so it is done after the quick stuff)
1330 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1336 // Update water pressure around modification
1337 // This also adds it to m_flow_active_nodes if appropriate
1339 MapVoxelManipulator v(&m_env.getMap());
1340 v.m_disable_water_climb =
1341 g_settings.getBool("disable_water_climb");
1343 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1347 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1349 catch(ProcessingLimitException &e)
1351 dstream<<"Processing limit reached (1)"<<std::endl;
1354 v.blitBack(modified_blocks);
1359 // Send object positions
1361 float &counter = m_objectdata_timer;
1363 if(counter >= g_settings.getFloat("objectdata_interval"))
1365 JMutexAutoLock lock1(m_env_mutex);
1366 JMutexAutoLock lock2(m_con_mutex);
1367 SendObjectData(counter);
1374 Trigger emergethread (it somehow gets to a non-triggered but
1375 bysy state sometimes)
1378 float &counter = m_emergethread_trigger_timer;
1384 m_emergethread.trigger();
1390 float &counter = m_savemap_timer;
1392 if(counter >= g_settings.getFloat("server_map_save_interval"))
1396 JMutexAutoLock lock(m_env_mutex);
1398 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1400 // Save only changed parts
1401 m_env.getMap().save(true);
1403 // Delete unused sectors
1404 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1405 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1406 if(deleted_count > 0)
1408 dout_server<<"Server: Unloaded "<<deleted_count
1409 <<" sectors from memory"<<std::endl;
1413 m_env.serializePlayers(m_mapsavedir);
1419 void Server::Receive()
1421 DSTACK(__FUNCTION_NAME);
1422 u32 data_maxsize = 10000;
1423 Buffer<u8> data(data_maxsize);
1428 JMutexAutoLock conlock(m_con_mutex);
1429 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1432 // This has to be called so that the client list gets synced
1433 // with the peer list of the connection
1434 handlePeerChanges();
1436 ProcessData(*data, datasize, peer_id);
1438 catch(con::InvalidIncomingDataException &e)
1440 derr_server<<"Server::Receive(): "
1441 "InvalidIncomingDataException: what()="
1442 <<e.what()<<std::endl;
1444 catch(con::PeerNotFoundException &e)
1446 //NOTE: This is not needed anymore
1448 // The peer has been disconnected.
1449 // Find the associated player and remove it.
1451 /*JMutexAutoLock envlock(m_env_mutex);
1453 dout_server<<"ServerThread: peer_id="<<peer_id
1454 <<" has apparently closed connection. "
1455 <<"Removing player."<<std::endl;
1457 m_env.removePlayer(peer_id);*/
1461 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1463 DSTACK(__FUNCTION_NAME);
1464 // Environment is locked first.
1465 JMutexAutoLock envlock(m_env_mutex);
1466 JMutexAutoLock conlock(m_con_mutex);
1470 peer = m_con.GetPeer(peer_id);
1472 catch(con::PeerNotFoundException &e)
1474 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1475 <<peer_id<<" not found"<<std::endl;
1479 //u8 peer_ser_ver = peer->serialization_version;
1480 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1488 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1490 if(command == TOSERVER_INIT)
1492 // [0] u16 TOSERVER_INIT
1493 // [2] u8 SER_FMT_VER_HIGHEST
1494 // [3] u8[20] player_name
1499 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1500 <<peer->id<<std::endl;
1502 // First byte after command is maximum supported
1503 // serialization version
1504 u8 client_max = data[2];
1505 u8 our_max = SER_FMT_VER_HIGHEST;
1506 // Use the highest version supported by both
1507 u8 deployed = core::min_(client_max, our_max);
1508 // If it's lower than the lowest supported, give up.
1509 if(deployed < SER_FMT_VER_LOWEST)
1510 deployed = SER_FMT_VER_INVALID;
1512 //peer->serialization_version = deployed;
1513 getClient(peer->id)->pending_serialization_version = deployed;
1515 if(deployed == SER_FMT_VER_INVALID)
1517 derr_server<<DTIME<<"Server: Cannot negotiate "
1518 "serialization version with peer "
1519 <<peer_id<<std::endl;
1528 const u32 playername_size = 20;
1529 char playername[playername_size];
1530 for(u32 i=0; i<playername_size-1; i++)
1532 playername[i] = data[3+i];
1534 playername[playername_size-1] = 0;
1537 Player *player = emergePlayer(playername, "", peer_id);
1538 //Player *player = m_env.getPlayer(peer_id);
1541 // DEBUG: Test serialization
1542 std::ostringstream test_os;
1543 player->serialize(test_os);
1544 dstream<<"Player serialization test: \""<<test_os.str()
1546 std::istringstream test_is(test_os.str());
1547 player->deSerialize(test_is);
1550 // If failed, cancel
1553 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1554 <<": failed to emerge player"<<std::endl;
1559 // If a client is already connected to the player, cancel
1560 if(player->peer_id != 0)
1562 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1563 <<" tried to connect to "
1564 "an already connected player (peer_id="
1565 <<player->peer_id<<")"<<std::endl;
1568 // Set client of player
1569 player->peer_id = peer_id;
1572 // Check if player doesn't exist
1574 throw con::InvalidIncomingDataException
1575 ("Server::ProcessData(): INIT: Player doesn't exist");
1577 /*// update name if it was supplied
1578 if(datasize >= 20+3)
1581 player->updateName((const char*)&data[3]);
1584 // Now answer with a TOCLIENT_INIT
1586 SharedBuffer<u8> reply(2+1+6);
1587 writeU16(&reply[0], TOCLIENT_INIT);
1588 writeU8(&reply[2], deployed);
1589 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1591 m_con.Send(peer_id, 0, reply, true);
1595 if(command == TOSERVER_INIT2)
1597 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1598 <<peer->id<<std::endl;
1601 getClient(peer->id)->serialization_version
1602 = getClient(peer->id)->pending_serialization_version;
1605 Send some initialization data
1608 // Send player info to all players
1611 // Send inventory to player
1612 SendInventory(peer->id);
1616 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1617 m_time_of_day.get());
1618 m_con.Send(peer->id, 0, data, true);
1621 // Send information about server to player in chat
1623 std::wostringstream os(std::ios_base::binary);
1626 os<<L"uptime="<<m_uptime.get();
1627 // Information about clients
1629 for(core::map<u16, RemoteClient*>::Iterator
1630 i = m_clients.getIterator();
1631 i.atEnd() == false; i++)
1633 // Get client and check that it is valid
1634 RemoteClient *client = i.getNode()->getValue();
1635 assert(client->peer_id == i.getNode()->getKey());
1636 if(client->serialization_version == SER_FMT_VER_INVALID)
1639 Player *player = m_env.getPlayer(client->peer_id);
1640 // Get name of player
1641 std::wstring name = L"unknown";
1643 name = narrow_to_wide(player->getName());
1644 // Add name to information string
1648 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
1649 os<<" WARNING: Map saving is disabled."<<std::endl;
1651 SendChatMessage(peer_id, os.str());
1654 // Send information about joining in chat
1656 std::wstring name = L"unknown";
1657 Player *player = m_env.getPlayer(peer_id);
1659 name = narrow_to_wide(player->getName());
1661 std::wstring message;
1664 message += L" joined game";
1665 BroadcastChatMessage(message);
1671 if(peer_ser_ver == SER_FMT_VER_INVALID)
1673 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1674 " serialization format invalid or not initialized."
1675 " Skipping incoming command="<<command<<std::endl;
1679 Player *player = m_env.getPlayer(peer_id);
1682 derr_server<<"Server::ProcessData(): Cancelling: "
1683 "No player for peer_id="<<peer_id
1687 if(command == TOSERVER_PLAYERPOS)
1689 if(datasize < 2+12+12+4+4)
1693 v3s32 ps = readV3S32(&data[start+2]);
1694 v3s32 ss = readV3S32(&data[start+2+12]);
1695 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1696 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1697 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1698 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1699 pitch = wrapDegrees(pitch);
1700 yaw = wrapDegrees(yaw);
1701 player->setPosition(position);
1702 player->setSpeed(speed);
1703 player->setPitch(pitch);
1704 player->setYaw(yaw);
1706 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1707 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1708 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1710 else if(command == TOSERVER_GOTBLOCKS)
1723 u16 count = data[2];
1724 for(u16 i=0; i<count; i++)
1726 if((s16)datasize < 2+1+(i+1)*6)
1727 throw con::InvalidIncomingDataException
1728 ("GOTBLOCKS length is too short");
1729 v3s16 p = readV3S16(&data[2+1+i*6]);
1730 /*dstream<<"Server: GOTBLOCKS ("
1731 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1732 RemoteClient *client = getClient(peer_id);
1733 client->GotBlock(p);
1736 else if(command == TOSERVER_DELETEDBLOCKS)
1749 u16 count = data[2];
1750 for(u16 i=0; i<count; i++)
1752 if((s16)datasize < 2+1+(i+1)*6)
1753 throw con::InvalidIncomingDataException
1754 ("DELETEDBLOCKS length is too short");
1755 v3s16 p = readV3S16(&data[2+1+i*6]);
1756 /*dstream<<"Server: DELETEDBLOCKS ("
1757 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1758 RemoteClient *client = getClient(peer_id);
1759 client->SetBlockNotSent(p);
1762 else if(command == TOSERVER_CLICK_OBJECT)
1769 [2] u8 button (0=left, 1=right)
1774 u8 button = readU8(&data[2]);
1776 p.X = readS16(&data[3]);
1777 p.Y = readS16(&data[5]);
1778 p.Z = readS16(&data[7]);
1779 s16 id = readS16(&data[9]);
1780 //u16 item_i = readU16(&data[11]);
1782 MapBlock *block = NULL;
1785 block = m_env.getMap().getBlockNoCreate(p);
1787 catch(InvalidPositionException &e)
1789 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1793 MapBlockObject *obj = block->getObject(id);
1797 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1801 //TODO: Check that object is reasonably close
1806 InventoryList *ilist = player->inventory.getList("main");
1807 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1810 // Skip if inventory has no free space
1811 if(ilist->getUsedSlots() == ilist->getSize())
1813 dout_server<<"Player inventory has no free space"<<std::endl;
1818 Create the inventory item
1820 InventoryItem *item = NULL;
1821 // If it is an item-object, take the item from it
1822 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1824 item = ((ItemObject*)obj)->createInventoryItem();
1826 // Else create an item of the object
1829 item = new MapBlockObjectItem
1830 (obj->getInventoryString());
1833 // Add to inventory and send inventory
1834 ilist->addItem(item);
1835 SendInventory(player->peer_id);
1838 // Remove from block
1839 block->removeObject(id);
1842 else if(command == TOSERVER_GROUND_ACTION)
1850 [3] v3s16 nodepos_undersurface
1851 [9] v3s16 nodepos_abovesurface
1856 2: stop digging (all parameters ignored)
1858 u8 action = readU8(&data[2]);
1860 p_under.X = readS16(&data[3]);
1861 p_under.Y = readS16(&data[5]);
1862 p_under.Z = readS16(&data[7]);
1864 p_over.X = readS16(&data[9]);
1865 p_over.Y = readS16(&data[11]);
1866 p_over.Z = readS16(&data[13]);
1867 u16 item_i = readU16(&data[15]);
1869 //TODO: Check that target is reasonably close
1877 NOTE: This can be used in the future to check if
1878 somebody is cheating, by checking the timing.
1885 else if(action == 2)
1888 RemoteClient *client = getClient(peer->id);
1889 JMutexAutoLock digmutex(client->m_dig_mutex);
1890 client->m_dig_tool_item = -1;
1895 3: Digging completed
1897 else if(action == 3)
1899 // Mandatory parameter; actually used for nothing
1900 core::map<v3s16, MapBlock*> modified_blocks;
1903 u8 mineral = MINERAL_NONE;
1907 MapNode n = m_env.getMap().getNode(p_under);
1908 // Get material at position
1910 // If it's not diggable, do nothing
1911 if(content_diggable(material) == false)
1913 derr_server<<"Server: Not finishing digging: Node not diggable"
1918 mineral = n.getMineral();
1920 catch(InvalidPositionException &e)
1922 derr_server<<"Server: Not finishing digging: Node not found."
1923 <<" Adding block to emerge queue."
1925 m_emerge_queue.addBlock(peer_id,
1926 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1931 Send the removal to all other clients
1936 SharedBuffer<u8> reply(replysize);
1937 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1938 writeS16(&reply[2], p_under.X);
1939 writeS16(&reply[4], p_under.Y);
1940 writeS16(&reply[6], p_under.Z);
1942 for(core::map<u16, RemoteClient*>::Iterator
1943 i = m_clients.getIterator();
1944 i.atEnd() == false; i++)
1946 // Get client and check that it is valid
1947 RemoteClient *client = i.getNode()->getValue();
1948 assert(client->peer_id == i.getNode()->getKey());
1949 if(client->serialization_version == SER_FMT_VER_INVALID)
1952 // Don't send if it's the same one
1953 if(peer_id == client->peer_id)
1957 m_con.Send(client->peer_id, 0, reply, true);
1961 Update and send inventory
1964 if(g_settings.getBool("creative_mode") == false)
1969 InventoryList *mlist = player->inventory.getList("main");
1972 InventoryItem *item = mlist->getItem(item_i);
1973 if(item && (std::string)item->getName() == "ToolItem")
1975 ToolItem *titem = (ToolItem*)item;
1976 std::string toolname = titem->getToolName();
1978 // Get digging properties for material and tool
1979 DiggingProperties prop =
1980 getDiggingProperties(material, toolname);
1982 if(prop.diggable == false)
1984 derr_server<<"Server: WARNING: Player digged"
1985 <<" with impossible material + tool"
1986 <<" combination"<<std::endl;
1989 bool weared_out = titem->addWear(prop.wear);
1993 mlist->deleteItem(item_i);
1999 Add digged item to inventory
2002 InventoryItem *item = NULL;
2004 if(mineral != MINERAL_NONE)
2005 item = getDiggedMineralItem(mineral);
2008 item = new MaterialItem(material, 1);
2010 player->inventory.addItem("main", item);
2015 SendInventory(player->peer_id);
2020 (this takes some time so it is done after the quick stuff)
2022 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2029 // Update water pressure around modification
2030 // This also adds it to m_flow_active_nodes if appropriate
2032 MapVoxelManipulator v(&m_env.getMap());
2033 v.m_disable_water_climb =
2034 g_settings.getBool("disable_water_climb");
2036 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2040 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2042 catch(ProcessingLimitException &e)
2044 dstream<<"Processing limit reached (1)"<<std::endl;
2047 v.blitBack(modified_blocks);
2054 else if(action == 1)
2057 InventoryList *ilist = player->inventory.getList("main");
2062 InventoryItem *item = ilist->getItem(item_i);
2064 // If there is no item, it is not possible to add it anywhere
2069 Handle material items
2071 if(std::string("MaterialItem") == item->getName())
2074 // Don't add a node if this is not a free space
2075 MapNode n2 = m_env.getMap().getNode(p_over);
2076 if(content_buildable_to(n2.d) == false)
2078 // Client probably has wrong data.
2079 // Set block not sent, so that client will get
2081 dstream<<"Client "<<peer_id<<" tried to place"
2082 <<" node in invalid position; setting"
2083 <<" MapBlock not sent."<<std::endl;
2084 RemoteClient *client = getClient(peer_id);
2085 v3s16 blockpos = getNodeBlockPos(p_over);
2086 client->SetBlockNotSent(blockpos);
2090 catch(InvalidPositionException &e)
2092 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2093 <<" Adding block to emerge queue."
2095 m_emerge_queue.addBlock(peer_id,
2096 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2100 // Reset build time counter
2101 getClient(peer->id)->m_time_from_building.set(0.0);
2104 MaterialItem *mitem = (MaterialItem*)item;
2106 n.d = mitem->getMaterial();
2107 if(content_features(n.d).wall_mounted)
2108 n.dir = packDir(p_under - p_over);
2112 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2113 SharedBuffer<u8> reply(replysize);
2114 writeU16(&reply[0], TOCLIENT_ADDNODE);
2115 writeS16(&reply[2], p_over.X);
2116 writeS16(&reply[4], p_over.Y);
2117 writeS16(&reply[6], p_over.Z);
2118 n.serialize(&reply[8], peer_ser_ver);
2120 m_con.SendToAll(0, reply, true);
2125 InventoryList *ilist = player->inventory.getList("main");
2126 if(g_settings.getBool("creative_mode") == false && ilist)
2128 // Remove from inventory and send inventory
2129 if(mitem->getCount() == 1)
2130 ilist->deleteItem(item_i);
2134 SendInventory(peer_id);
2140 This takes some time so it is done after the quick stuff
2142 core::map<v3s16, MapBlock*> modified_blocks;
2143 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
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);
2170 Set the modified blocks unsent for all the clients
2173 //JMutexAutoLock lock2(m_con_mutex);
2175 for(core::map<u16, RemoteClient*>::Iterator
2176 i = m_clients.getIterator();
2177 i.atEnd() == false; i++)
2179 RemoteClient *client = i.getNode()->getValue();
2181 if(modified_blocks.size() > 0)
2183 // Remove block from sent history
2184 client->SetBlocksNotSent(modified_blocks);
2194 // Update water pressure around modification
2195 // This also adds it to m_flow_active_nodes if appropriate
2197 MapVoxelManipulator v(&m_env.getMap());
2198 v.m_disable_water_climb =
2199 g_settings.getBool("disable_water_climb");
2201 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2205 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2207 catch(ProcessingLimitException &e)
2209 dstream<<"Processing limit reached (1)"<<std::endl;
2212 v.blitBack(modified_blocks);
2220 v3s16 blockpos = getNodeBlockPos(p_over);
2222 MapBlock *block = NULL;
2225 block = m_env.getMap().getBlockNoCreate(blockpos);
2227 catch(InvalidPositionException &e)
2229 derr_server<<"Error while placing object: "
2230 "block not found"<<std::endl;
2234 v3s16 block_pos_i_on_map = block->getPosRelative();
2235 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2237 v3f pos = intToFloat(p_over);
2238 pos -= block_pos_f_on_map;
2240 /*dout_server<<"pos="
2241 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2244 MapBlockObject *obj = NULL;
2247 Handle block object items
2249 if(std::string("MBOItem") == item->getName())
2251 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2253 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2254 "inventorystring=\""
2255 <<oitem->getInventoryString()
2256 <<"\""<<std::endl;*/
2258 obj = oitem->createObject
2259 (pos, player->getYaw(), player->getPitch());
2266 dout_server<<"Placing a miscellaneous item on map"
2269 Create an ItemObject that contains the item.
2271 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2272 std::ostringstream os(std::ios_base::binary);
2273 item->serialize(os);
2274 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2275 iobj->setItemString(os.str());
2281 derr_server<<"WARNING: item resulted in NULL object, "
2282 <<"not placing onto map"
2287 block->addObject(obj);
2289 dout_server<<"Placed object"<<std::endl;
2291 InventoryList *ilist = player->inventory.getList("main");
2292 if(g_settings.getBool("creative_mode") == false && ilist)
2294 // Remove from inventory and send inventory
2295 ilist->deleteItem(item_i);
2297 SendInventory(peer_id);
2305 Catch invalid actions
2309 derr_server<<"WARNING: Server: Invalid action "
2310 <<action<<std::endl;
2314 else if(command == TOSERVER_RELEASE)
2323 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2326 else if(command == TOSERVER_SIGNTEXT)
2335 std::string datastring((char*)&data[2], datasize-2);
2336 std::istringstream is(datastring, std::ios_base::binary);
2339 is.read((char*)buf, 6);
2340 v3s16 blockpos = readV3S16(buf);
2341 is.read((char*)buf, 2);
2342 s16 id = readS16(buf);
2343 is.read((char*)buf, 2);
2344 u16 textlen = readU16(buf);
2346 for(u16 i=0; i<textlen; i++)
2348 is.read((char*)buf, 1);
2349 text += (char)buf[0];
2352 MapBlock *block = NULL;
2355 block = m_env.getMap().getBlockNoCreate(blockpos);
2357 catch(InvalidPositionException &e)
2359 derr_server<<"Error while setting sign text: "
2360 "block not found"<<std::endl;
2364 MapBlockObject *obj = block->getObject(id);
2367 derr_server<<"Error while setting sign text: "
2368 "object not found"<<std::endl;
2372 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2374 derr_server<<"Error while setting sign text: "
2375 "object is not a sign"<<std::endl;
2379 ((SignObject*)obj)->setText(text);
2381 obj->getBlock()->setChangedFlag();
2383 else if(command == TOSERVER_INVENTORY_ACTION)
2385 /*// Ignore inventory changes if in creative mode
2386 if(g_settings.getBool("creative_mode") == true)
2388 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2392 // Strip command and create a stream
2393 std::string datastring((char*)&data[2], datasize-2);
2394 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2395 std::istringstream is(datastring, std::ios_base::binary);
2397 InventoryAction *a = InventoryAction::deSerialize(is);
2401 Handle craftresult specially if not in creative mode
2403 bool disable_action = false;
2404 if(a->getType() == IACTION_MOVE
2405 && g_settings.getBool("creative_mode") == false)
2407 IMoveAction *ma = (IMoveAction*)a;
2408 // Don't allow moving anything to craftresult
2409 if(ma->to_name == "craftresult")
2412 disable_action = true;
2414 // When something is removed from craftresult
2415 if(ma->from_name == "craftresult")
2417 disable_action = true;
2418 // Remove stuff from craft
2419 InventoryList *clist = player->inventory.getList("craft");
2422 u16 count = ma->count;
2425 clist->decrementMaterials(count);
2428 // Feed action to player inventory
2429 a->apply(&player->inventory);
2432 // If something appeared in craftresult, throw it
2434 InventoryList *rlist = player->inventory.getList("craftresult");
2435 InventoryList *mlist = player->inventory.getList("main");
2436 if(rlist && mlist && rlist->getUsedSlots() == 1)
2438 InventoryItem *item1 = rlist->changeItem(0, NULL);
2439 mlist->addItem(item1);
2443 if(disable_action == false)
2445 // Feed action to player inventory
2446 a->apply(&player->inventory);
2451 SendInventory(player->peer_id);
2455 dstream<<"TOSERVER_INVENTORY_ACTION: "
2456 <<"InventoryAction::deSerialize() returned NULL"
2460 else if(command == TOSERVER_CHAT_MESSAGE)
2468 std::string datastring((char*)&data[2], datasize-2);
2469 std::istringstream is(datastring, std::ios_base::binary);
2472 is.read((char*)buf, 2);
2473 u16 len = readU16(buf);
2475 std::wstring message;
2476 for(u16 i=0; i<len; i++)
2478 is.read((char*)buf, 2);
2479 message += (wchar_t)readU16(buf);
2482 // Get player name of this client
2483 std::wstring name = narrow_to_wide(player->getName());
2485 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2487 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2490 Send the message to all other clients
2492 for(core::map<u16, RemoteClient*>::Iterator
2493 i = m_clients.getIterator();
2494 i.atEnd() == false; i++)
2496 // Get client and check that it is valid
2497 RemoteClient *client = i.getNode()->getValue();
2498 assert(client->peer_id == i.getNode()->getKey());
2499 if(client->serialization_version == SER_FMT_VER_INVALID)
2502 // Don't send if it's the same one
2503 if(peer_id == client->peer_id)
2506 SendChatMessage(client->peer_id, line);
2511 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2512 "unknown command "<<command<<std::endl;
2516 catch(SendFailedException &e)
2518 derr_server<<"Server::ProcessData(): SendFailedException: "
2524 /*void Server::Send(u16 peer_id, u16 channelnum,
2525 SharedBuffer<u8> data, bool reliable)
2527 JMutexAutoLock lock(m_con_mutex);
2528 m_con.Send(peer_id, channelnum, data, reliable);
2531 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2533 DSTACK(__FUNCTION_NAME);
2535 Create a packet with the block in the right format
2538 std::ostringstream os(std::ios_base::binary);
2539 block->serialize(os, ver);
2540 std::string s = os.str();
2541 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2543 u32 replysize = 8 + blockdata.getSize();
2544 SharedBuffer<u8> reply(replysize);
2545 v3s16 p = block->getPos();
2546 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2547 writeS16(&reply[2], p.X);
2548 writeS16(&reply[4], p.Y);
2549 writeS16(&reply[6], p.Z);
2550 memcpy(&reply[8], *blockdata, blockdata.getSize());
2552 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2553 <<": \tpacket size: "<<replysize<<std::endl;*/
2558 m_con.Send(peer_id, 1, reply, true);
2561 core::list<PlayerInfo> Server::getPlayerInfo()
2563 DSTACK(__FUNCTION_NAME);
2564 JMutexAutoLock envlock(m_env_mutex);
2565 JMutexAutoLock conlock(m_con_mutex);
2567 core::list<PlayerInfo> list;
2569 core::list<Player*> players = m_env.getPlayers();
2571 core::list<Player*>::Iterator i;
2572 for(i = players.begin();
2573 i != players.end(); i++)
2577 Player *player = *i;
2580 con::Peer *peer = m_con.GetPeer(player->peer_id);
2581 // Copy info from peer to info struct
2583 info.address = peer->address;
2584 info.avg_rtt = peer->avg_rtt;
2586 catch(con::PeerNotFoundException &e)
2588 // Set dummy peer info
2590 info.address = Address(0,0,0,0,0);
2594 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2595 info.position = player->getPosition();
2597 list.push_back(info);
2603 void Server::peerAdded(con::Peer *peer)
2605 DSTACK(__FUNCTION_NAME);
2606 dout_server<<"Server::peerAdded(): peer->id="
2607 <<peer->id<<std::endl;
2610 c.type = PEER_ADDED;
2611 c.peer_id = peer->id;
2613 m_peer_change_queue.push_back(c);
2616 void Server::deletingPeer(con::Peer *peer, bool timeout)
2618 DSTACK(__FUNCTION_NAME);
2619 dout_server<<"Server::deletingPeer(): peer->id="
2620 <<peer->id<<", timeout="<<timeout<<std::endl;
2623 c.type = PEER_REMOVED;
2624 c.peer_id = peer->id;
2625 c.timeout = timeout;
2626 m_peer_change_queue.push_back(c);
2629 void Server::SendObjectData(float dtime)
2631 DSTACK(__FUNCTION_NAME);
2633 core::map<v3s16, bool> stepped_blocks;
2635 for(core::map<u16, RemoteClient*>::Iterator
2636 i = m_clients.getIterator();
2637 i.atEnd() == false; i++)
2639 u16 peer_id = i.getNode()->getKey();
2640 RemoteClient *client = i.getNode()->getValue();
2641 assert(client->peer_id == peer_id);
2643 if(client->serialization_version == SER_FMT_VER_INVALID)
2646 client->SendObjectData(this, dtime, stepped_blocks);
2650 void Server::SendPlayerInfos()
2652 DSTACK(__FUNCTION_NAME);
2654 //JMutexAutoLock envlock(m_env_mutex);
2656 // Get connected players
2657 core::list<Player*> players = m_env.getPlayers(true);
2659 u32 player_count = players.getSize();
2660 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2662 SharedBuffer<u8> data(datasize);
2663 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2666 core::list<Player*>::Iterator i;
2667 for(i = players.begin();
2668 i != players.end(); i++)
2670 Player *player = *i;
2672 /*dstream<<"Server sending player info for player with "
2673 "peer_id="<<player->peer_id<<std::endl;*/
2675 writeU16(&data[start], player->peer_id);
2676 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2677 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2678 start += 2+PLAYERNAME_SIZE;
2681 //JMutexAutoLock conlock(m_con_mutex);
2684 m_con.SendToAll(0, data, true);
2702 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2708 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2714 enum ItemSpecType type;
2715 // Only other one of these is used
2721 items: a pointer to an array of 9 pointers to items
2722 specs: a pointer to an array of 9 ItemSpecs
2724 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2726 u16 items_min_x = 100;
2727 u16 items_max_x = 100;
2728 u16 items_min_y = 100;
2729 u16 items_max_y = 100;
2730 for(u16 y=0; y<3; y++)
2731 for(u16 x=0; x<3; x++)
2733 if(items[y*3 + x] == NULL)
2735 if(items_min_x == 100 || x < items_min_x)
2737 if(items_min_y == 100 || y < items_min_y)
2739 if(items_max_x == 100 || x > items_max_x)
2741 if(items_max_y == 100 || y > items_max_y)
2744 // No items at all, just return false
2745 if(items_min_x == 100)
2748 u16 items_w = items_max_x - items_min_x + 1;
2749 u16 items_h = items_max_y - items_min_y + 1;
2751 u16 specs_min_x = 100;
2752 u16 specs_max_x = 100;
2753 u16 specs_min_y = 100;
2754 u16 specs_max_y = 100;
2755 for(u16 y=0; y<3; y++)
2756 for(u16 x=0; x<3; x++)
2758 if(specs[y*3 + x].type == ITEM_NONE)
2760 if(specs_min_x == 100 || x < specs_min_x)
2762 if(specs_min_y == 100 || y < specs_min_y)
2764 if(specs_max_x == 100 || x > specs_max_x)
2766 if(specs_max_y == 100 || y > specs_max_y)
2769 // No specs at all, just return false
2770 if(specs_min_x == 100)
2773 u16 specs_w = specs_max_x - specs_min_x + 1;
2774 u16 specs_h = specs_max_y - specs_min_y + 1;
2777 if(items_w != specs_w || items_h != specs_h)
2780 for(u16 y=0; y<specs_h; y++)
2781 for(u16 x=0; x<specs_w; x++)
2783 u16 items_x = items_min_x + x;
2784 u16 items_y = items_min_y + y;
2785 u16 specs_x = specs_min_x + x;
2786 u16 specs_y = specs_min_y + y;
2787 InventoryItem *item = items[items_y * 3 + items_x];
2788 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2790 if(spec.type == ITEM_NONE)
2792 // Has to be no item
2798 // There should be an item
2802 std::string itemname = item->getName();
2804 if(spec.type == ITEM_MATERIAL)
2806 if(itemname != "MaterialItem")
2808 MaterialItem *mitem = (MaterialItem*)item;
2809 if(mitem->getMaterial() != spec.num)
2812 else if(spec.type == ITEM_CRAFT)
2814 if(itemname != "CraftItem")
2816 CraftItem *mitem = (CraftItem*)item;
2817 if(mitem->getSubName() != spec.name)
2820 else if(spec.type == ITEM_TOOL)
2822 // Not supported yet
2825 else if(spec.type == ITEM_MBO)
2827 // Not supported yet
2832 // Not supported yet
2840 void Server::SendInventory(u16 peer_id)
2842 DSTACK(__FUNCTION_NAME);
2844 Player* player = m_env.getPlayer(peer_id);
2847 Calculate crafting stuff
2849 if(g_settings.getBool("creative_mode") == false)
2851 InventoryList *clist = player->inventory.getList("craft");
2852 InventoryList *rlist = player->inventory.getList("craftresult");
2855 rlist->clearItems();
2859 InventoryItem *items[9];
2860 for(u16 i=0; i<9; i++)
2862 items[i] = clist->getItem(i);
2871 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2872 if(checkItemCombination(items, specs))
2874 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2883 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2884 if(checkItemCombination(items, specs))
2886 rlist->addItem(new CraftItem("Stick", 4));
2895 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2896 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2897 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2898 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2899 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2900 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2901 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2902 if(checkItemCombination(items, specs))
2904 rlist->addItem(new MapBlockObjectItem("Sign"));
2913 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2914 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2915 if(checkItemCombination(items, specs))
2917 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2926 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2927 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2928 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2929 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2930 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2931 if(checkItemCombination(items, specs))
2933 rlist->addItem(new ToolItem("WPick", 0));
2942 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2943 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2944 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2945 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2946 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2947 if(checkItemCombination(items, specs))
2949 rlist->addItem(new ToolItem("STPick", 0));
2958 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2959 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2960 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2961 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2962 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2963 if(checkItemCombination(items, specs))
2965 rlist->addItem(new ToolItem("MesePick", 0));
2970 } // if creative_mode == false
2976 std::ostringstream os;
2977 //os.imbue(std::locale("C"));
2979 player->inventory.serialize(os);
2981 std::string s = os.str();
2983 SharedBuffer<u8> data(s.size()+2);
2984 writeU16(&data[0], TOCLIENT_INVENTORY);
2985 memcpy(&data[2], s.c_str(), s.size());
2988 m_con.Send(peer_id, 0, data, true);
2991 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2993 DSTACK(__FUNCTION_NAME);
2995 std::ostringstream os(std::ios_base::binary);
2999 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3000 os.write((char*)buf, 2);
3003 writeU16(buf, message.size());
3004 os.write((char*)buf, 2);
3007 for(u32 i=0; i<message.size(); i++)
3011 os.write((char*)buf, 2);
3015 std::string s = os.str();
3016 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3018 m_con.Send(peer_id, 0, data, true);
3021 void Server::BroadcastChatMessage(const std::wstring &message)
3023 for(core::map<u16, RemoteClient*>::Iterator
3024 i = m_clients.getIterator();
3025 i.atEnd() == false; i++)
3027 // Get client and check that it is valid
3028 RemoteClient *client = i.getNode()->getValue();
3029 assert(client->peer_id == i.getNode()->getKey());
3030 if(client->serialization_version == SER_FMT_VER_INVALID)
3033 SendChatMessage(client->peer_id, message);
3037 void Server::SendBlocks(float dtime)
3039 DSTACK(__FUNCTION_NAME);
3041 JMutexAutoLock envlock(m_env_mutex);
3043 core::array<PrioritySortedBlockTransfer> queue;
3045 s32 total_sending = 0;
3047 for(core::map<u16, RemoteClient*>::Iterator
3048 i = m_clients.getIterator();
3049 i.atEnd() == false; i++)
3051 RemoteClient *client = i.getNode()->getValue();
3052 assert(client->peer_id == i.getNode()->getKey());
3054 total_sending += client->SendingCount();
3056 if(client->serialization_version == SER_FMT_VER_INVALID)
3059 client->GetNextBlocks(this, dtime, queue);
3063 // Lowest priority number comes first.
3064 // Lowest is most important.
3067 JMutexAutoLock conlock(m_con_mutex);
3069 for(u32 i=0; i<queue.size(); i++)
3071 //TODO: Calculate limit dynamically
3072 if(total_sending >= g_settings.getS32
3073 ("max_simultaneous_block_sends_server_total"))
3076 PrioritySortedBlockTransfer q = queue[i];
3078 MapBlock *block = NULL;
3081 block = m_env.getMap().getBlockNoCreate(q.pos);
3083 catch(InvalidPositionException &e)
3088 RemoteClient *client = getClient(q.peer_id);
3090 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3092 client->SentBlock(q.pos);
3099 RemoteClient* Server::getClient(u16 peer_id)
3101 DSTACK(__FUNCTION_NAME);
3102 //JMutexAutoLock lock(m_con_mutex);
3103 core::map<u16, RemoteClient*>::Node *n;
3104 n = m_clients.find(peer_id);
3105 // A client should exist for all peers
3107 return n->getValue();
3110 void setCreativeInventory(Player *player)
3112 player->resetInventory();
3114 // Give some good picks
3116 InventoryItem *item = new ToolItem("STPick", 0);
3117 void* r = player->inventory.addItem("main", item);
3121 InventoryItem *item = new ToolItem("MesePick", 0);
3122 void* r = player->inventory.addItem("main", item);
3129 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3132 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3133 player->inventory.addItem("main", item);
3136 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3138 // Skip some materials
3139 if(i == CONTENT_WATER || i == CONTENT_TORCH
3140 || i == CONTENT_COALSTONE)
3143 InventoryItem *item = new MaterialItem(i, 1);
3144 player->inventory.addItem("main", item);
3148 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3149 void* r = player->inventory.addItem("main", item);
3154 Player *Server::emergePlayer(const char *name, const char *password,
3158 Try to get an existing player
3160 Player *player = m_env.getPlayer(name);
3163 // If player is already connected, cancel
3164 if(player->peer_id != 0)
3166 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3171 player->peer_id = peer_id;
3173 // Reset inventory to creative if in creative mode
3174 if(g_settings.getBool("creative_mode"))
3176 setCreativeInventory(player);
3183 If player with the wanted peer_id already exists, cancel.
3185 if(m_env.getPlayer(peer_id) != NULL)
3187 dstream<<"emergePlayer(): Player with wrong name but same"
3188 " peer_id already exists"<<std::endl;
3196 player = new ServerRemotePlayer();
3197 //player->peer_id = c.peer_id;
3198 //player->peer_id = PEER_ID_INEXISTENT;
3199 player->peer_id = peer_id;
3200 player->updateName(name);
3206 dstream<<"Server: Finding spawn place for player \""
3207 <<player->getName()<<"\""<<std::endl;
3211 player->setPosition(intToFloat(v3s16(
3218 f32 groundheight = 0;
3220 // Try to find a good place a few times
3221 for(s32 i=0; i<500; i++)
3224 // We're going to try to throw the player to this position
3225 nodepos = v2s16(-range + (myrand()%(range*2)),
3226 -range + (myrand()%(range*2)));
3227 v2s16 sectorpos = getNodeSectorPos(nodepos);
3229 m_env.getMap().emergeSector(sectorpos);
3230 // Get ground height at point
3231 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3232 // The sector should have been generated -> groundheight exists
3233 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3234 // Don't go underwater
3235 if(groundheight < WATER_LEVEL)
3237 //dstream<<"-> Underwater"<<std::endl;
3240 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3241 // Get block at point
3243 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3244 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3245 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3246 // Don't go inside ground
3248 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3249 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3250 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3251 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3252 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3253 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3255 dstream<<"-> Inside ground"<<std::endl;
3259 }catch(InvalidPositionException &e)
3261 dstream<<"-> Invalid position"<<std::endl;
3262 // Ignore invalid position
3266 // Found a good place
3267 dstream<<"Searched through "<<i<<" places."<<std::endl;
3272 // If no suitable place was not found, go above water at least.
3273 if(groundheight < WATER_LEVEL)
3274 groundheight = WATER_LEVEL;
3276 player->setPosition(intToFloat(v3s16(
3285 Add player to environment
3288 m_env.addPlayer(player);
3291 Add stuff to inventory
3294 if(g_settings.getBool("creative_mode"))
3296 setCreativeInventory(player);
3301 InventoryItem *item = new ToolItem("WPick", 32000);
3302 void* r = player->inventory.addItem("main", item);
3306 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3307 void* r = player->inventory.addItem("main", item);
3311 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3312 void* r = player->inventory.addItem("main", item);
3316 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3317 void* r = player->inventory.addItem("main", item);
3321 InventoryItem *item = new CraftItem("Stick", 4);
3322 void* r = player->inventory.addItem("main", item);
3326 InventoryItem *item = new ToolItem("WPick", 32000);
3327 void* r = player->inventory.addItem("main", item);
3331 InventoryItem *item = new ToolItem("STPick", 32000);
3332 void* r = player->inventory.addItem("main", item);
3335 /*// Give some lights
3337 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3338 bool r = player->inventory.addItem("main", item);
3342 for(u16 i=0; i<4; i++)
3344 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3345 bool r = player->inventory.addItem("main", item);
3348 /*// Give some other stuff
3350 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3351 bool r = player->inventory.addItem("main", item);
3358 } // create new player
3362 void Server::UpdateBlockWaterPressure(MapBlock *block,
3363 core::map<v3s16, MapBlock*> &modified_blocks)
3365 MapVoxelManipulator v(&m_env.getMap());
3366 v.m_disable_water_climb =
3367 g_settings.getBool("disable_water_climb");
3369 VoxelArea area(block->getPosRelative(),
3370 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3374 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3376 catch(ProcessingLimitException &e)
3378 dstream<<"Processing limit reached (1)"<<std::endl;
3381 v.blitBack(modified_blocks);
3385 void Server::handlePeerChange(PeerChange &c)
3387 JMutexAutoLock envlock(m_env_mutex);
3388 JMutexAutoLock conlock(m_con_mutex);
3390 if(c.type == PEER_ADDED)
3397 core::map<u16, RemoteClient*>::Node *n;
3398 n = m_clients.find(c.peer_id);
3399 // The client shouldn't already exist
3403 RemoteClient *client = new RemoteClient();
3404 client->peer_id = c.peer_id;
3405 m_clients.insert(client->peer_id, client);
3408 else if(c.type == PEER_REMOVED)
3415 core::map<u16, RemoteClient*>::Node *n;
3416 n = m_clients.find(c.peer_id);
3417 // The client should exist
3420 // Collect information about leaving in chat
3421 std::wstring message;
3423 std::wstring name = L"unknown";
3424 Player *player = m_env.getPlayer(c.peer_id);
3426 name = narrow_to_wide(player->getName());
3430 message += L" left game";
3432 message += L" (timed out)";
3437 m_env.removePlayer(c.peer_id);
3440 // Set player client disconnected
3442 Player *player = m_env.getPlayer(c.peer_id);
3444 player->peer_id = 0;
3448 delete m_clients[c.peer_id];
3449 m_clients.remove(c.peer_id);
3451 // Send player info to all remaining clients
3454 // Send leave chat message to all remaining clients
3455 BroadcastChatMessage(message);
3464 void Server::handlePeerChanges()
3466 while(m_peer_change_queue.size() > 0)
3468 PeerChange c = m_peer_change_queue.pop_front();
3470 dout_server<<"Server: Handling peer change: "
3471 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3474 handlePeerChange(c);
3478 void dedicated_server_loop(Server &server)
3480 DSTACK(__FUNCTION_NAME);
3482 std::cout<<std::endl;
3483 std::cout<<"========================"<<std::endl;
3484 std::cout<<"Running dedicated server"<<std::endl;
3485 std::cout<<"========================"<<std::endl;
3486 std::cout<<std::endl;
3490 // This is kind of a hack but can be done like this
3491 // because server.step() is very light
3495 static int counter = 0;
3501 core::list<PlayerInfo> list = server.getPlayerInfo();
3502 core::list<PlayerInfo>::Iterator i;
3503 static u32 sum_old = 0;
3504 u32 sum = PIChecksum(list);
3507 std::cout<<DTIME<<"Player info:"<<std::endl;
3508 for(i=list.begin(); i!=list.end(); i++)
3510 i->PrintLine(&std::cout);