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 bool haxmode = g_settings.getBool("haxmode");
331 Player *player = server->m_env.getPlayer(peer_id);
333 assert(player != NULL);
335 v3f playerpos = player->getPosition();
336 v3f playerspeed = player->getSpeed();
338 v3s16 center_nodepos = floatToInt(playerpos);
340 v3s16 center = getNodeBlockPos(center_nodepos);
342 // Camera position and direction
344 playerpos + v3f(0, BS+BS/2, 0);
345 v3f camera_dir = v3f(0,0,1);
346 camera_dir.rotateYZBy(player->getPitch());
347 camera_dir.rotateXZBy(player->getYaw());
350 Get the starting value of the block finder radius.
352 s16 last_nearest_unsent_d;
355 JMutexAutoLock lock(m_blocks_sent_mutex);
357 if(m_last_center != center)
359 m_nearest_unsent_d = 0;
360 m_last_center = center;
363 /*dstream<<"m_nearest_unsent_reset_timer="
364 <<m_nearest_unsent_reset_timer<<std::endl;*/
365 if(m_nearest_unsent_reset_timer > 5.0)
367 m_nearest_unsent_reset_timer = 0;
368 m_nearest_unsent_d = 0;
369 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
372 last_nearest_unsent_d = m_nearest_unsent_d;
374 d_start = m_nearest_unsent_d;
377 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
378 ("max_simultaneous_block_sends_per_client");
379 u16 maximum_simultaneous_block_sends =
380 maximum_simultaneous_block_sends_setting;
383 Check the time from last addNode/removeNode.
385 Decrease send rate if player is building stuff.
388 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
389 m_time_from_building.m_value += dtime;
390 /*if(m_time_from_building.m_value
391 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
392 if(m_time_from_building.m_value < g_settings.getFloat(
393 "full_block_send_enable_min_time_from_building"))
395 maximum_simultaneous_block_sends
396 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
400 u32 num_blocks_selected;
402 JMutexAutoLock lock(m_blocks_sending_mutex);
403 num_blocks_selected = m_blocks_sending.size();
407 next time d will be continued from the d from which the nearest
408 unsent block was found this time.
410 This is because not necessarily any of the blocks found this
411 time are actually sent.
413 s32 new_nearest_unsent_d = -1;
415 // Serialization version used
416 //u8 ser_version = serialization_version;
418 //bool has_incomplete_blocks = false;
420 s16 d_max = g_settings.getS16("max_block_send_distance");
421 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
423 //dstream<<"Starting from "<<d_start<<std::endl;
425 for(s16 d = d_start; d <= d_max; d++)
427 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
429 //if(has_incomplete_blocks == false)
431 JMutexAutoLock lock(m_blocks_sent_mutex);
433 If m_nearest_unsent_d was changed by the EmergeThread
434 (it can change it to 0 through SetBlockNotSent),
436 Else update m_nearest_unsent_d
438 if(m_nearest_unsent_d != last_nearest_unsent_d)
440 d = m_nearest_unsent_d;
441 last_nearest_unsent_d = m_nearest_unsent_d;
446 Get the border/face dot coordinates of a "d-radiused"
449 core::list<v3s16> list;
450 getFacePositions(list, d);
452 core::list<v3s16>::Iterator li;
453 for(li=list.begin(); li!=list.end(); li++)
455 v3s16 p = *li + center;
459 - Don't allow too many simultaneous transfers
460 - EXCEPT when the blocks are very close
462 Also, don't send blocks that are already flying.
465 u16 maximum_simultaneous_block_sends_now =
466 maximum_simultaneous_block_sends;
468 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
470 maximum_simultaneous_block_sends_now =
471 maximum_simultaneous_block_sends_setting;
475 JMutexAutoLock lock(m_blocks_sending_mutex);
477 // Limit is dynamically lowered when building
478 if(num_blocks_selected
479 >= maximum_simultaneous_block_sends_now)
481 /*dstream<<"Not sending more blocks. Queue full. "
482 <<m_blocks_sending.size()
487 if(m_blocks_sending.find(p) != NULL)
494 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
495 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
496 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
497 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
498 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
499 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
502 // If this is true, inexistent block will be made from scratch
503 bool generate = d <= d_max_gen;
507 // Don't generate above player
513 // Limit the generating area vertically to 2/3
514 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
520 If block is far away, don't generate it unless it is
523 NOTE: We can't know the ground level this way with the
529 MapSector *sector = NULL;
532 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
534 catch(InvalidPositionException &e)
540 // Get center ground height in nodes
541 f32 gh = sector->getGroundHeight(
542 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
543 // Block center y in nodes
544 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
545 // If differs a lot, don't generate
546 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
553 Don't draw if not in sight
556 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
562 Don't send already sent blocks
565 JMutexAutoLock lock(m_blocks_sent_mutex);
567 if(m_blocks_sent.find(p) != NULL)
573 NOTE: We can't know the ground level this way with the
579 Ignore block if it is not at ground surface
580 but don't ignore water surface blocks
582 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
583 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
584 f32 y = server->m_env.getMap().getGroundHeight(p2d);
585 // The sector might not exist yet, thus no heightmap
586 if(y > GROUNDHEIGHT_VALID_MINVALUE)
588 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
589 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
590 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
597 Check if map has this block
599 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
601 bool surely_not_found_on_disk = false;
602 bool block_is_invalid = false;
605 /*if(block->isIncomplete())
607 has_incomplete_blocks = true;
613 surely_not_found_on_disk = true;
616 if(block->isValid() == false)
618 block_is_invalid = true;
622 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
623 v2s16 chunkpos = map->sector_to_chunk(p2d);
624 if(map->chunkNonVolatile(chunkpos) == false)
625 block_is_invalid = true;
626 /*MapChunk *chunk = map->getChunk(chunkpos);
628 block_is_invalid = true;
629 else if(chunk->getIsVolatile() == true)
630 block_is_invalid = true;*/
634 If block has been marked to not exist on disk (dummy)
635 and generating new ones is not wanted, skip block.
637 if(generate == false && surely_not_found_on_disk == true)
644 Record the lowest d from which a a block has been
645 found being not sent and possibly to exist
647 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
649 new_nearest_unsent_d = d;
653 Add inexistent block to emerge queue.
655 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
657 //dstream<<"asd"<<std::endl;
659 /*SharedPtr<JMutexAutoLock> lock
660 (m_num_blocks_in_emerge_queue.getLock());*/
662 //TODO: Get value from somewhere
663 // Allow only one block in emerge queue
664 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
666 //dstream<<"Adding block to emerge queue"<<std::endl;
668 // Add it to the emerge queue and trigger the thread
671 if(generate == false)
672 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
674 server->m_emerge_queue.addBlock(peer_id, p, flags);
675 server->m_emergethread.trigger();
686 PrioritySortedBlockTransfer q((float)d, p, peer_id);
690 num_blocks_selected += 1;
695 if(new_nearest_unsent_d != -1)
697 JMutexAutoLock lock(m_blocks_sent_mutex);
698 m_nearest_unsent_d = new_nearest_unsent_d;
702 void RemoteClient::SendObjectData(
705 core::map<v3s16, bool> &stepped_blocks
708 DSTACK(__FUNCTION_NAME);
710 // Can't send anything without knowing version
711 if(serialization_version == SER_FMT_VER_INVALID)
713 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
719 Send a TOCLIENT_OBJECTDATA packet.
723 u16 number of player positions
734 std::ostringstream os(std::ios_base::binary);
738 writeU16(buf, TOCLIENT_OBJECTDATA);
739 os.write((char*)buf, 2);
742 Get and write player data
745 // Get connected players
746 core::list<Player*> players = server->m_env.getPlayers(true);
748 // Write player count
749 u16 playercount = players.size();
750 writeU16(buf, playercount);
751 os.write((char*)buf, 2);
753 core::list<Player*>::Iterator i;
754 for(i = players.begin();
755 i != players.end(); i++)
759 v3f pf = player->getPosition();
760 v3f sf = player->getSpeed();
762 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
763 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
764 s32 pitch_i (player->getPitch() * 100);
765 s32 yaw_i (player->getYaw() * 100);
767 writeU16(buf, player->peer_id);
768 os.write((char*)buf, 2);
769 writeV3S32(buf, position_i);
770 os.write((char*)buf, 12);
771 writeV3S32(buf, speed_i);
772 os.write((char*)buf, 12);
773 writeS32(buf, pitch_i);
774 os.write((char*)buf, 4);
775 writeS32(buf, yaw_i);
776 os.write((char*)buf, 4);
780 Get and write object data
786 For making players to be able to build to their nearby
787 environment (building is not possible on blocks that are not
790 - Add blocks to emerge queue if they are not found
792 SUGGESTION: These could be ignored from the backside of the player
795 Player *player = server->m_env.getPlayer(peer_id);
799 v3f playerpos = player->getPosition();
800 v3f playerspeed = player->getSpeed();
802 v3s16 center_nodepos = floatToInt(playerpos);
803 v3s16 center = getNodeBlockPos(center_nodepos);
805 s16 d_max = g_settings.getS16("active_object_range");
807 // Number of blocks whose objects were written to bos
810 std::ostringstream bos(std::ios_base::binary);
812 for(s16 d = 0; d <= d_max; d++)
814 core::list<v3s16> list;
815 getFacePositions(list, d);
817 core::list<v3s16>::Iterator li;
818 for(li=list.begin(); li!=list.end(); li++)
820 v3s16 p = *li + center;
823 Ignore blocks that haven't been sent to the client
826 JMutexAutoLock sentlock(m_blocks_sent_mutex);
827 if(m_blocks_sent.find(p) == NULL)
831 // Try stepping block and add it to a send queue
836 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
839 Step block if not in stepped_blocks and add to stepped_blocks.
841 if(stepped_blocks.find(p) == NULL)
843 block->stepObjects(dtime, true, server->getDayNightRatio());
844 stepped_blocks.insert(p, true);
845 block->setChangedFlag();
848 // Skip block if there are no objects
849 if(block->getObjectCount() == 0)
858 bos.write((char*)buf, 6);
861 block->serializeObjects(bos, serialization_version);
866 Stop collecting objects if data is already too big
868 // Sum of player and object data sizes
869 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
870 // break out if data too big
871 if(sum > MAX_OBJECTDATA_SIZE)
873 goto skip_subsequent;
877 catch(InvalidPositionException &e)
880 // Add it to the emerge queue and trigger the thread.
881 // Fetch the block only if it is on disk.
883 // Grab and increment counter
884 /*SharedPtr<JMutexAutoLock> lock
885 (m_num_blocks_in_emerge_queue.getLock());
886 m_num_blocks_in_emerge_queue.m_value++;*/
888 // Add to queue as an anonymous fetch from disk
889 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
890 server->m_emerge_queue.addBlock(0, p, flags);
891 server->m_emergethread.trigger();
899 writeU16(buf, blockcount);
900 os.write((char*)buf, 2);
902 // Write block objects
909 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
912 std::string s = os.str();
913 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
914 // Send as unreliable
915 server->m_con.Send(peer_id, 0, data, false);
918 void RemoteClient::GotBlock(v3s16 p)
920 JMutexAutoLock lock(m_blocks_sending_mutex);
921 JMutexAutoLock lock2(m_blocks_sent_mutex);
922 if(m_blocks_sending.find(p) != NULL)
923 m_blocks_sending.remove(p);
926 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
927 " m_blocks_sending"<<std::endl;*/
928 m_excess_gotblocks++;
930 m_blocks_sent.insert(p, true);
933 void RemoteClient::SentBlock(v3s16 p)
935 JMutexAutoLock lock(m_blocks_sending_mutex);
936 /*if(m_blocks_sending.size() > 15)
938 dstream<<"RemoteClient::SentBlock(): "
939 <<"m_blocks_sending.size()="
940 <<m_blocks_sending.size()<<std::endl;
942 if(m_blocks_sending.find(p) == NULL)
943 m_blocks_sending.insert(p, 0.0);
945 dstream<<"RemoteClient::SentBlock(): Sent block"
946 " already in m_blocks_sending"<<std::endl;
949 void RemoteClient::SetBlockNotSent(v3s16 p)
951 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
952 JMutexAutoLock sentlock(m_blocks_sent_mutex);
954 m_nearest_unsent_d = 0;
956 if(m_blocks_sending.find(p) != NULL)
957 m_blocks_sending.remove(p);
958 if(m_blocks_sent.find(p) != NULL)
959 m_blocks_sent.remove(p);
962 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
964 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
965 JMutexAutoLock sentlock(m_blocks_sent_mutex);
967 m_nearest_unsent_d = 0;
969 for(core::map<v3s16, MapBlock*>::Iterator
970 i = blocks.getIterator();
971 i.atEnd()==false; i++)
973 v3s16 p = i.getNode()->getKey();
975 if(m_blocks_sending.find(p) != NULL)
976 m_blocks_sending.remove(p);
977 if(m_blocks_sent.find(p) != NULL)
978 m_blocks_sent.remove(p);
986 PlayerInfo::PlayerInfo()
991 void PlayerInfo::PrintLine(std::ostream *s)
994 (*s)<<"\""<<name<<"\" ("
995 <<position.X<<","<<position.Y
996 <<","<<position.Z<<") ";
998 (*s)<<" avg_rtt="<<avg_rtt;
1002 u32 PIChecksum(core::list<PlayerInfo> &l)
1004 core::list<PlayerInfo>::Iterator i;
1007 for(i=l.begin(); i!=l.end(); i++)
1009 checksum += a * (i->id+1);
1010 checksum ^= 0x435aafcd;
1021 std::string mapsavedir,
1023 MapParams map_params
1025 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
1026 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1028 m_emergethread(this),
1029 m_time_of_day(9000),
1031 m_time_of_day_send_timer(0),
1033 m_mapsavedir(mapsavedir)
1035 //m_flowwater_timer = 0.0;
1036 m_liquid_transform_timer = 0.0;
1037 m_print_info_timer = 0.0;
1038 m_objectdata_timer = 0.0;
1039 m_emergethread_trigger_timer = 0.0;
1040 m_savemap_timer = 0.0;
1044 m_step_dtime_mutex.Init();
1048 m_env.deSerializePlayers(m_mapsavedir);
1054 m_env.serializePlayers(m_mapsavedir);
1059 JMutexAutoLock clientslock(m_con_mutex);
1061 for(core::map<u16, RemoteClient*>::Iterator
1062 i = m_clients.getIterator();
1063 i.atEnd() == false; i++)
1066 // NOTE: These are removed by env destructor
1068 u16 peer_id = i.getNode()->getKey();
1069 JMutexAutoLock envlock(m_env_mutex);
1070 m_env.removePlayer(peer_id);
1074 delete i.getNode()->getValue();
1078 void Server::start(unsigned short port)
1080 DSTACK(__FUNCTION_NAME);
1081 // Stop thread if already running
1084 // Initialize connection
1085 m_con.setTimeoutMs(30);
1089 m_thread.setRun(true);
1092 dout_server<<"Server started on port "<<port<<std::endl;
1097 DSTACK(__FUNCTION_NAME);
1098 // Stop threads (set run=false first so both start stopping)
1099 m_thread.setRun(false);
1100 m_emergethread.setRun(false);
1102 m_emergethread.stop();
1104 dout_server<<"Server threads stopped"<<std::endl;
1107 void Server::step(float dtime)
1109 DSTACK(__FUNCTION_NAME);
1114 JMutexAutoLock lock(m_step_dtime_mutex);
1115 m_step_dtime += dtime;
1119 void Server::AsyncRunStep()
1121 DSTACK(__FUNCTION_NAME);
1125 JMutexAutoLock lock1(m_step_dtime_mutex);
1126 dtime = m_step_dtime;
1129 // Send blocks to clients
1135 //dstream<<"Server steps "<<dtime<<std::endl;
1136 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1139 JMutexAutoLock lock1(m_step_dtime_mutex);
1140 m_step_dtime -= dtime;
1147 m_uptime.set(m_uptime.get() + dtime);
1151 Update m_time_of_day
1154 m_time_counter += dtime;
1155 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1156 u32 units = (u32)(m_time_counter*speed);
1157 m_time_counter -= (f32)units / speed;
1158 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1160 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1163 Send to clients at constant intervals
1166 m_time_of_day_send_timer -= dtime;
1167 if(m_time_of_day_send_timer < 0.0)
1169 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1171 //JMutexAutoLock envlock(m_env_mutex);
1172 JMutexAutoLock conlock(m_con_mutex);
1174 for(core::map<u16, RemoteClient*>::Iterator
1175 i = m_clients.getIterator();
1176 i.atEnd() == false; i++)
1178 RemoteClient *client = i.getNode()->getValue();
1179 //Player *player = m_env.getPlayer(client->peer_id);
1181 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1182 m_time_of_day.get());
1184 m_con.Send(client->peer_id, 0, data, true);
1190 // Process connection's timeouts
1191 JMutexAutoLock lock2(m_con_mutex);
1192 m_con.RunTimeouts(dtime);
1196 // This has to be called so that the client list gets synced
1197 // with the peer list of the connection
1198 handlePeerChanges();
1203 // This also runs Map's timers
1204 JMutexAutoLock lock(m_env_mutex);
1215 m_liquid_transform_timer += dtime;
1216 if(m_liquid_transform_timer >= 1.00)
1218 m_liquid_transform_timer -= 1.00;
1220 JMutexAutoLock lock(m_env_mutex);
1222 core::map<v3s16, MapBlock*> modified_blocks;
1223 m_env.getMap().transformLiquids(modified_blocks);
1228 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1229 ServerMap &map = ((ServerMap&)m_env.getMap());
1230 map.updateLighting(modified_blocks, lighting_modified_blocks);
1232 // Add blocks modified by lighting to modified_blocks
1233 for(core::map<v3s16, MapBlock*>::Iterator
1234 i = lighting_modified_blocks.getIterator();
1235 i.atEnd() == false; i++)
1237 MapBlock *block = i.getNode()->getValue();
1238 modified_blocks.insert(block->getPos(), block);
1242 Set the modified blocks unsent for all the clients
1245 JMutexAutoLock lock2(m_con_mutex);
1247 for(core::map<u16, RemoteClient*>::Iterator
1248 i = m_clients.getIterator();
1249 i.atEnd() == false; i++)
1251 RemoteClient *client = i.getNode()->getValue();
1253 if(modified_blocks.size() > 0)
1255 // Remove block from sent history
1256 client->SetBlocksNotSent(modified_blocks);
1261 // Periodically print some info
1263 float &counter = m_print_info_timer;
1269 JMutexAutoLock lock2(m_con_mutex);
1271 for(core::map<u16, RemoteClient*>::Iterator
1272 i = m_clients.getIterator();
1273 i.atEnd() == false; i++)
1275 //u16 peer_id = i.getNode()->getKey();
1276 RemoteClient *client = i.getNode()->getValue();
1277 client->PrintInfo(std::cout);
1285 NOTE: Some of this could be moved to RemoteClient
1289 JMutexAutoLock envlock(m_env_mutex);
1290 JMutexAutoLock conlock(m_con_mutex);
1292 for(core::map<u16, RemoteClient*>::Iterator
1293 i = m_clients.getIterator();
1294 i.atEnd() == false; i++)
1296 RemoteClient *client = i.getNode()->getValue();
1297 Player *player = m_env.getPlayer(client->peer_id);
1299 JMutexAutoLock digmutex(client->m_dig_mutex);
1301 if(client->m_dig_tool_item == -1)
1304 client->m_dig_time_remaining -= dtime;
1306 if(client->m_dig_time_remaining > 0)
1308 client->m_time_from_building.set(0.0);
1312 v3s16 p_under = client->m_dig_position;
1314 // Mandatory parameter; actually used for nothing
1315 core::map<v3s16, MapBlock*> modified_blocks;
1321 // Get material at position
1322 material = m_env.getMap().getNode(p_under).d;
1323 // If it's not diggable, do nothing
1324 if(content_diggable(material) == false)
1326 derr_server<<"Server: Not finishing digging: Node not diggable"
1328 client->m_dig_tool_item = -1;
1332 catch(InvalidPositionException &e)
1334 derr_server<<"Server: Not finishing digging: Node not found"
1336 client->m_dig_tool_item = -1;
1342 SharedBuffer<u8> reply(replysize);
1343 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1344 writeS16(&reply[2], p_under.X);
1345 writeS16(&reply[4], p_under.Y);
1346 writeS16(&reply[6], p_under.Z);
1348 m_con.SendToAll(0, reply, true);
1350 if(g_settings.getBool("creative_mode") == false)
1352 // Add to inventory and send inventory
1353 InventoryItem *item = new MaterialItem(material, 1);
1354 player->inventory.addItem("main", item);
1355 SendInventory(player->peer_id);
1360 (this takes some time so it is done after the quick stuff)
1362 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1368 // Update water pressure around modification
1369 // This also adds it to m_flow_active_nodes if appropriate
1371 MapVoxelManipulator v(&m_env.getMap());
1372 v.m_disable_water_climb =
1373 g_settings.getBool("disable_water_climb");
1375 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1379 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1381 catch(ProcessingLimitException &e)
1383 dstream<<"Processing limit reached (1)"<<std::endl;
1386 v.blitBack(modified_blocks);
1391 // Send object positions
1393 float &counter = m_objectdata_timer;
1395 if(counter >= g_settings.getFloat("objectdata_interval"))
1397 JMutexAutoLock lock1(m_env_mutex);
1398 JMutexAutoLock lock2(m_con_mutex);
1399 SendObjectData(counter);
1405 // Trigger emergethread (it gets somehow gets to a
1406 // non-triggered but bysy state sometimes)
1408 float &counter = m_emergethread_trigger_timer;
1414 m_emergethread.trigger();
1420 float &counter = m_savemap_timer;
1422 if(counter >= g_settings.getFloat("server_map_save_interval"))
1426 JMutexAutoLock lock(m_env_mutex);
1428 // Save only changed parts
1429 m_env.getMap().save(true);
1431 // Delete unused sectors
1432 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1433 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1434 if(deleted_count > 0)
1436 dout_server<<"Server: Unloaded "<<deleted_count
1437 <<" sectors from memory"<<std::endl;
1441 m_env.serializePlayers(m_mapsavedir);
1446 void Server::Receive()
1448 DSTACK(__FUNCTION_NAME);
1449 u32 data_maxsize = 10000;
1450 Buffer<u8> data(data_maxsize);
1455 JMutexAutoLock conlock(m_con_mutex);
1456 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1459 // This has to be called so that the client list gets synced
1460 // with the peer list of the connection
1461 handlePeerChanges();
1463 ProcessData(*data, datasize, peer_id);
1465 catch(con::InvalidIncomingDataException &e)
1467 derr_server<<"Server::Receive(): "
1468 "InvalidIncomingDataException: what()="
1469 <<e.what()<<std::endl;
1471 catch(con::PeerNotFoundException &e)
1473 //NOTE: This is not needed anymore
1475 // The peer has been disconnected.
1476 // Find the associated player and remove it.
1478 /*JMutexAutoLock envlock(m_env_mutex);
1480 dout_server<<"ServerThread: peer_id="<<peer_id
1481 <<" has apparently closed connection. "
1482 <<"Removing player."<<std::endl;
1484 m_env.removePlayer(peer_id);*/
1488 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1490 DSTACK(__FUNCTION_NAME);
1491 // Environment is locked first.
1492 JMutexAutoLock envlock(m_env_mutex);
1493 JMutexAutoLock conlock(m_con_mutex);
1497 peer = m_con.GetPeer(peer_id);
1499 catch(con::PeerNotFoundException &e)
1501 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1502 <<peer_id<<" not found"<<std::endl;
1506 //u8 peer_ser_ver = peer->serialization_version;
1507 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1515 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1517 if(command == TOSERVER_INIT)
1519 // [0] u16 TOSERVER_INIT
1520 // [2] u8 SER_FMT_VER_HIGHEST
1521 // [3] u8[20] player_name
1526 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1527 <<peer->id<<std::endl;
1529 // First byte after command is maximum supported
1530 // serialization version
1531 u8 client_max = data[2];
1532 u8 our_max = SER_FMT_VER_HIGHEST;
1533 // Use the highest version supported by both
1534 u8 deployed = core::min_(client_max, our_max);
1535 // If it's lower than the lowest supported, give up.
1536 if(deployed < SER_FMT_VER_LOWEST)
1537 deployed = SER_FMT_VER_INVALID;
1539 //peer->serialization_version = deployed;
1540 getClient(peer->id)->pending_serialization_version = deployed;
1542 if(deployed == SER_FMT_VER_INVALID)
1544 derr_server<<DTIME<<"Server: Cannot negotiate "
1545 "serialization version with peer "
1546 <<peer_id<<std::endl;
1555 const u32 playername_size = 20;
1556 char playername[playername_size];
1557 for(u32 i=0; i<playername_size-1; i++)
1559 playername[i] = data[3+i];
1561 playername[playername_size-1] = 0;
1564 Player *player = emergePlayer(playername, "", peer_id);
1565 //Player *player = m_env.getPlayer(peer_id);
1568 // DEBUG: Test serialization
1569 std::ostringstream test_os;
1570 player->serialize(test_os);
1571 dstream<<"Player serialization test: \""<<test_os.str()
1573 std::istringstream test_is(test_os.str());
1574 player->deSerialize(test_is);
1577 // If failed, cancel
1580 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1581 <<": failed to emerge player"<<std::endl;
1586 // If a client is already connected to the player, cancel
1587 if(player->peer_id != 0)
1589 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1590 <<" tried to connect to "
1591 "an already connected player (peer_id="
1592 <<player->peer_id<<")"<<std::endl;
1595 // Set client of player
1596 player->peer_id = peer_id;
1599 // Check if player doesn't exist
1601 throw con::InvalidIncomingDataException
1602 ("Server::ProcessData(): INIT: Player doesn't exist");
1604 /*// update name if it was supplied
1605 if(datasize >= 20+3)
1608 player->updateName((const char*)&data[3]);
1611 // Now answer with a TOCLIENT_INIT
1613 SharedBuffer<u8> reply(2+1+6);
1614 writeU16(&reply[0], TOCLIENT_INIT);
1615 writeU8(&reply[2], deployed);
1616 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1618 m_con.Send(peer_id, 0, reply, true);
1622 if(command == TOSERVER_INIT2)
1624 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1625 <<peer->id<<std::endl;
1628 getClient(peer->id)->serialization_version
1629 = getClient(peer->id)->pending_serialization_version;
1632 Send some initialization data
1635 // Send player info to all players
1638 // Send inventory to player
1639 SendInventory(peer->id);
1643 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1644 m_time_of_day.get());
1645 m_con.Send(peer->id, 0, data, true);
1648 // Send information about server to player in chat
1650 std::wostringstream os(std::ios_base::binary);
1653 os<<L"uptime="<<m_uptime.get();
1654 // Information about clients
1656 for(core::map<u16, RemoteClient*>::Iterator
1657 i = m_clients.getIterator();
1658 i.atEnd() == false; i++)
1660 // Get client and check that it is valid
1661 RemoteClient *client = i.getNode()->getValue();
1662 assert(client->peer_id == i.getNode()->getKey());
1663 if(client->serialization_version == SER_FMT_VER_INVALID)
1666 Player *player = m_env.getPlayer(client->peer_id);
1667 // Get name of player
1668 std::wstring name = L"unknown";
1670 name = narrow_to_wide(player->getName());
1671 // Add name to information string
1676 SendChatMessage(peer_id, os.str());
1679 // Send information about joining in chat
1681 std::wstring name = L"unknown";
1682 Player *player = m_env.getPlayer(peer_id);
1684 name = narrow_to_wide(player->getName());
1686 std::wstring message;
1689 message += L" joined game";
1690 BroadcastChatMessage(message);
1696 if(peer_ser_ver == SER_FMT_VER_INVALID)
1698 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1699 " serialization format invalid or not initialized."
1700 " Skipping incoming command="<<command<<std::endl;
1704 Player *player = m_env.getPlayer(peer_id);
1707 derr_server<<"Server::ProcessData(): Cancelling: "
1708 "No player for peer_id="<<peer_id
1712 if(command == TOSERVER_PLAYERPOS)
1714 if(datasize < 2+12+12+4+4)
1718 v3s32 ps = readV3S32(&data[start+2]);
1719 v3s32 ss = readV3S32(&data[start+2+12]);
1720 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1721 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1722 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1723 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1724 pitch = wrapDegrees(pitch);
1725 yaw = wrapDegrees(yaw);
1726 player->setPosition(position);
1727 player->setSpeed(speed);
1728 player->setPitch(pitch);
1729 player->setYaw(yaw);
1731 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1732 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1733 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1735 else if(command == TOSERVER_GOTBLOCKS)
1748 u16 count = data[2];
1749 for(u16 i=0; i<count; i++)
1751 if((s16)datasize < 2+1+(i+1)*6)
1752 throw con::InvalidIncomingDataException
1753 ("GOTBLOCKS length is too short");
1754 v3s16 p = readV3S16(&data[2+1+i*6]);
1755 /*dstream<<"Server: GOTBLOCKS ("
1756 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1757 RemoteClient *client = getClient(peer_id);
1758 client->GotBlock(p);
1761 else if(command == TOSERVER_DELETEDBLOCKS)
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 ("DELETEDBLOCKS length is too short");
1780 v3s16 p = readV3S16(&data[2+1+i*6]);
1781 /*dstream<<"Server: DELETEDBLOCKS ("
1782 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1783 RemoteClient *client = getClient(peer_id);
1784 client->SetBlockNotSent(p);
1787 else if(command == TOSERVER_CLICK_OBJECT)
1794 [2] u8 button (0=left, 1=right)
1799 u8 button = readU8(&data[2]);
1801 p.X = readS16(&data[3]);
1802 p.Y = readS16(&data[5]);
1803 p.Z = readS16(&data[7]);
1804 s16 id = readS16(&data[9]);
1805 //u16 item_i = readU16(&data[11]);
1807 MapBlock *block = NULL;
1810 block = m_env.getMap().getBlockNoCreate(p);
1812 catch(InvalidPositionException &e)
1814 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1818 MapBlockObject *obj = block->getObject(id);
1822 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1826 //TODO: Check that object is reasonably close
1831 InventoryList *ilist = player->inventory.getList("main");
1832 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1835 // Skip if inventory has no free space
1836 if(ilist->getUsedSlots() == ilist->getSize())
1838 dout_server<<"Player inventory has no free space"<<std::endl;
1843 Create the inventory item
1845 InventoryItem *item = NULL;
1846 // If it is an item-object, take the item from it
1847 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1849 item = ((ItemObject*)obj)->createInventoryItem();
1851 // Else create an item of the object
1854 item = new MapBlockObjectItem
1855 (obj->getInventoryString());
1858 // Add to inventory and send inventory
1859 ilist->addItem(item);
1860 SendInventory(player->peer_id);
1863 // Remove from block
1864 block->removeObject(id);
1867 else if(command == TOSERVER_GROUND_ACTION)
1875 [3] v3s16 nodepos_undersurface
1876 [9] v3s16 nodepos_abovesurface
1881 2: stop digging (all parameters ignored)
1883 u8 action = readU8(&data[2]);
1885 p_under.X = readS16(&data[3]);
1886 p_under.Y = readS16(&data[5]);
1887 p_under.Z = readS16(&data[7]);
1889 p_over.X = readS16(&data[9]);
1890 p_over.Y = readS16(&data[11]);
1891 p_over.Z = readS16(&data[13]);
1892 u16 item_i = readU16(&data[15]);
1894 //TODO: Check that target is reasonably close
1902 NOTE: This can be used in the future to check if
1903 somebody is cheating, by checking the timing.
1910 else if(action == 2)
1913 RemoteClient *client = getClient(peer->id);
1914 JMutexAutoLock digmutex(client->m_dig_mutex);
1915 client->m_dig_tool_item = -1;
1920 3: Digging completed
1922 else if(action == 3)
1924 // Mandatory parameter; actually used for nothing
1925 core::map<v3s16, MapBlock*> modified_blocks;
1928 u8 mineral = MINERAL_NONE;
1932 MapNode n = m_env.getMap().getNode(p_under);
1933 // Get material at position
1935 // If it's not diggable, do nothing
1936 if(content_diggable(material) == false)
1938 derr_server<<"Server: Not finishing digging: Node not diggable"
1943 mineral = n.getMineral();
1945 catch(InvalidPositionException &e)
1947 derr_server<<"Server: Not finishing digging: Node not found."
1948 <<" Adding block to emerge queue."
1950 m_emerge_queue.addBlock(peer_id,
1951 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1956 Send the removal to all other clients
1961 SharedBuffer<u8> reply(replysize);
1962 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1963 writeS16(&reply[2], p_under.X);
1964 writeS16(&reply[4], p_under.Y);
1965 writeS16(&reply[6], p_under.Z);
1967 for(core::map<u16, RemoteClient*>::Iterator
1968 i = m_clients.getIterator();
1969 i.atEnd() == false; i++)
1971 // Get client and check that it is valid
1972 RemoteClient *client = i.getNode()->getValue();
1973 assert(client->peer_id == i.getNode()->getKey());
1974 if(client->serialization_version == SER_FMT_VER_INVALID)
1977 // Don't send if it's the same one
1978 if(peer_id == client->peer_id)
1982 m_con.Send(client->peer_id, 0, reply, true);
1986 Update and send inventory
1989 if(g_settings.getBool("creative_mode") == false)
1994 InventoryList *mlist = player->inventory.getList("main");
1997 InventoryItem *item = mlist->getItem(item_i);
1998 if(item && (std::string)item->getName() == "ToolItem")
2000 ToolItem *titem = (ToolItem*)item;
2001 std::string toolname = titem->getToolName();
2003 // Get digging properties for material and tool
2004 DiggingProperties prop =
2005 getDiggingProperties(material, toolname);
2007 if(prop.diggable == false)
2009 derr_server<<"Server: WARNING: Player digged"
2010 <<" with impossible material + tool"
2011 <<" combination"<<std::endl;
2014 bool weared_out = titem->addWear(prop.wear);
2018 mlist->deleteItem(item_i);
2024 Add digged item to inventory
2027 InventoryItem *item = NULL;
2029 if(mineral != MINERAL_NONE)
2030 item = getDiggedMineralItem(mineral);
2033 item = new MaterialItem(material, 1);
2035 player->inventory.addItem("main", item);
2040 SendInventory(player->peer_id);
2045 (this takes some time so it is done after the quick stuff)
2047 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2054 // Update water pressure around modification
2055 // This also adds it to m_flow_active_nodes if appropriate
2057 MapVoxelManipulator v(&m_env.getMap());
2058 v.m_disable_water_climb =
2059 g_settings.getBool("disable_water_climb");
2061 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2065 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2067 catch(ProcessingLimitException &e)
2069 dstream<<"Processing limit reached (1)"<<std::endl;
2072 v.blitBack(modified_blocks);
2079 else if(action == 1)
2082 InventoryList *ilist = player->inventory.getList("main");
2087 InventoryItem *item = ilist->getItem(item_i);
2089 // If there is no item, it is not possible to add it anywhere
2094 Handle material items
2096 if(std::string("MaterialItem") == item->getName())
2099 // Don't add a node if this is not a free space
2100 MapNode n2 = m_env.getMap().getNode(p_over);
2101 if(content_buildable_to(n2.d) == false)
2103 // Client probably has wrong data.
2104 // Set block not sent, so that client will get
2106 dstream<<"Client "<<peer_id<<" tried to place"
2107 <<" node in invalid position; setting"
2108 <<" MapBlock not sent."<<std::endl;
2109 RemoteClient *client = getClient(peer_id);
2110 v3s16 blockpos = getNodeBlockPos(p_over);
2111 client->SetBlockNotSent(blockpos);
2115 catch(InvalidPositionException &e)
2117 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2118 <<" Adding block to emerge queue."
2120 m_emerge_queue.addBlock(peer_id,
2121 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2125 // Reset build time counter
2126 getClient(peer->id)->m_time_from_building.set(0.0);
2129 MaterialItem *mitem = (MaterialItem*)item;
2131 n.d = mitem->getMaterial();
2132 if(content_features(n.d).wall_mounted)
2133 n.dir = packDir(p_under - p_over);
2137 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2138 SharedBuffer<u8> reply(replysize);
2139 writeU16(&reply[0], TOCLIENT_ADDNODE);
2140 writeS16(&reply[2], p_over.X);
2141 writeS16(&reply[4], p_over.Y);
2142 writeS16(&reply[6], p_over.Z);
2143 n.serialize(&reply[8], peer_ser_ver);
2145 m_con.SendToAll(0, reply, true);
2150 InventoryList *ilist = player->inventory.getList("main");
2151 if(g_settings.getBool("creative_mode") == false && ilist)
2153 // Remove from inventory and send inventory
2154 if(mitem->getCount() == 1)
2155 ilist->deleteItem(item_i);
2159 SendInventory(peer_id);
2165 This takes some time so it is done after the quick stuff
2167 core::map<v3s16, MapBlock*> modified_blocks;
2168 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2174 InventoryList *ilist = player->inventory.getList("main");
2175 if(g_settings.getBool("creative_mode") == false && ilist)
2177 // Remove from inventory and send inventory
2178 if(mitem->getCount() == 1)
2179 ilist->deleteItem(item_i);
2183 SendInventory(peer_id);
2189 This takes some time so it is done after the quick stuff
2191 core::map<v3s16, MapBlock*> modified_blocks;
2192 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2195 Set the modified blocks unsent for all the clients
2198 //JMutexAutoLock lock2(m_con_mutex);
2200 for(core::map<u16, RemoteClient*>::Iterator
2201 i = m_clients.getIterator();
2202 i.atEnd() == false; i++)
2204 RemoteClient *client = i.getNode()->getValue();
2206 if(modified_blocks.size() > 0)
2208 // Remove block from sent history
2209 client->SetBlocksNotSent(modified_blocks);
2219 // Update water pressure around modification
2220 // This also adds it to m_flow_active_nodes if appropriate
2222 MapVoxelManipulator v(&m_env.getMap());
2223 v.m_disable_water_climb =
2224 g_settings.getBool("disable_water_climb");
2226 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2230 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2232 catch(ProcessingLimitException &e)
2234 dstream<<"Processing limit reached (1)"<<std::endl;
2237 v.blitBack(modified_blocks);
2245 v3s16 blockpos = getNodeBlockPos(p_over);
2247 MapBlock *block = NULL;
2250 block = m_env.getMap().getBlockNoCreate(blockpos);
2252 catch(InvalidPositionException &e)
2254 derr_server<<"Error while placing object: "
2255 "block not found"<<std::endl;
2259 v3s16 block_pos_i_on_map = block->getPosRelative();
2260 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2262 v3f pos = intToFloat(p_over);
2263 pos -= block_pos_f_on_map;
2265 /*dout_server<<"pos="
2266 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2269 MapBlockObject *obj = NULL;
2272 Handle block object items
2274 if(std::string("MBOItem") == item->getName())
2276 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2278 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2279 "inventorystring=\""
2280 <<oitem->getInventoryString()
2281 <<"\""<<std::endl;*/
2283 obj = oitem->createObject
2284 (pos, player->getYaw(), player->getPitch());
2291 dout_server<<"Placing a miscellaneous item on map"
2294 Create an ItemObject that contains the item.
2296 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2297 std::ostringstream os(std::ios_base::binary);
2298 item->serialize(os);
2299 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2300 iobj->setItemString(os.str());
2306 derr_server<<"WARNING: item resulted in NULL object, "
2307 <<"not placing onto map"
2312 block->addObject(obj);
2314 dout_server<<"Placed object"<<std::endl;
2316 InventoryList *ilist = player->inventory.getList("main");
2317 if(g_settings.getBool("creative_mode") == false && ilist)
2319 // Remove from inventory and send inventory
2320 ilist->deleteItem(item_i);
2322 SendInventory(peer_id);
2330 Catch invalid actions
2334 derr_server<<"WARNING: Server: Invalid action "
2335 <<action<<std::endl;
2339 else if(command == TOSERVER_RELEASE)
2348 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2351 else if(command == TOSERVER_SIGNTEXT)
2360 std::string datastring((char*)&data[2], datasize-2);
2361 std::istringstream is(datastring, std::ios_base::binary);
2364 is.read((char*)buf, 6);
2365 v3s16 blockpos = readV3S16(buf);
2366 is.read((char*)buf, 2);
2367 s16 id = readS16(buf);
2368 is.read((char*)buf, 2);
2369 u16 textlen = readU16(buf);
2371 for(u16 i=0; i<textlen; i++)
2373 is.read((char*)buf, 1);
2374 text += (char)buf[0];
2377 MapBlock *block = NULL;
2380 block = m_env.getMap().getBlockNoCreate(blockpos);
2382 catch(InvalidPositionException &e)
2384 derr_server<<"Error while setting sign text: "
2385 "block not found"<<std::endl;
2389 MapBlockObject *obj = block->getObject(id);
2392 derr_server<<"Error while setting sign text: "
2393 "object not found"<<std::endl;
2397 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2399 derr_server<<"Error while setting sign text: "
2400 "object is not a sign"<<std::endl;
2404 ((SignObject*)obj)->setText(text);
2406 obj->getBlock()->setChangedFlag();
2408 else if(command == TOSERVER_INVENTORY_ACTION)
2410 /*// Ignore inventory changes if in creative mode
2411 if(g_settings.getBool("creative_mode") == true)
2413 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2417 // Strip command and create a stream
2418 std::string datastring((char*)&data[2], datasize-2);
2419 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2420 std::istringstream is(datastring, std::ios_base::binary);
2422 InventoryAction *a = InventoryAction::deSerialize(is);
2426 Handle craftresult specially if not in creative mode
2428 bool disable_action = false;
2429 if(a->getType() == IACTION_MOVE
2430 && g_settings.getBool("creative_mode") == false)
2432 IMoveAction *ma = (IMoveAction*)a;
2433 // Don't allow moving anything to craftresult
2434 if(ma->to_name == "craftresult")
2437 disable_action = true;
2439 // When something is removed from craftresult
2440 if(ma->from_name == "craftresult")
2442 disable_action = true;
2443 // Remove stuff from craft
2444 InventoryList *clist = player->inventory.getList("craft");
2447 u16 count = ma->count;
2450 clist->decrementMaterials(count);
2453 // Feed action to player inventory
2454 a->apply(&player->inventory);
2457 // If something appeared in craftresult, throw it
2459 InventoryList *rlist = player->inventory.getList("craftresult");
2460 InventoryList *mlist = player->inventory.getList("main");
2461 if(rlist && mlist && rlist->getUsedSlots() == 1)
2463 InventoryItem *item1 = rlist->changeItem(0, NULL);
2464 mlist->addItem(item1);
2468 if(disable_action == false)
2470 // Feed action to player inventory
2471 a->apply(&player->inventory);
2476 SendInventory(player->peer_id);
2480 dstream<<"TOSERVER_INVENTORY_ACTION: "
2481 <<"InventoryAction::deSerialize() returned NULL"
2485 else if(command == TOSERVER_CHAT_MESSAGE)
2493 std::string datastring((char*)&data[2], datasize-2);
2494 std::istringstream is(datastring, std::ios_base::binary);
2497 is.read((char*)buf, 2);
2498 u16 len = readU16(buf);
2500 std::wstring message;
2501 for(u16 i=0; i<len; i++)
2503 is.read((char*)buf, 2);
2504 message += (wchar_t)readU16(buf);
2507 // Get player name of this client
2508 std::wstring name = narrow_to_wide(player->getName());
2510 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2512 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2515 Send the message to all other clients
2517 for(core::map<u16, RemoteClient*>::Iterator
2518 i = m_clients.getIterator();
2519 i.atEnd() == false; i++)
2521 // Get client and check that it is valid
2522 RemoteClient *client = i.getNode()->getValue();
2523 assert(client->peer_id == i.getNode()->getKey());
2524 if(client->serialization_version == SER_FMT_VER_INVALID)
2527 // Don't send if it's the same one
2528 if(peer_id == client->peer_id)
2531 SendChatMessage(client->peer_id, line);
2536 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2537 "unknown command "<<command<<std::endl;
2541 catch(SendFailedException &e)
2543 derr_server<<"Server::ProcessData(): SendFailedException: "
2549 /*void Server::Send(u16 peer_id, u16 channelnum,
2550 SharedBuffer<u8> data, bool reliable)
2552 JMutexAutoLock lock(m_con_mutex);
2553 m_con.Send(peer_id, channelnum, data, reliable);
2556 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2558 DSTACK(__FUNCTION_NAME);
2560 Create a packet with the block in the right format
2563 std::ostringstream os(std::ios_base::binary);
2564 block->serialize(os, ver);
2565 std::string s = os.str();
2566 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2568 u32 replysize = 8 + blockdata.getSize();
2569 SharedBuffer<u8> reply(replysize);
2570 v3s16 p = block->getPos();
2571 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2572 writeS16(&reply[2], p.X);
2573 writeS16(&reply[4], p.Y);
2574 writeS16(&reply[6], p.Z);
2575 memcpy(&reply[8], *blockdata, blockdata.getSize());
2577 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2578 <<": \tpacket size: "<<replysize<<std::endl;*/
2583 m_con.Send(peer_id, 1, reply, true);
2586 core::list<PlayerInfo> Server::getPlayerInfo()
2588 DSTACK(__FUNCTION_NAME);
2589 JMutexAutoLock envlock(m_env_mutex);
2590 JMutexAutoLock conlock(m_con_mutex);
2592 core::list<PlayerInfo> list;
2594 core::list<Player*> players = m_env.getPlayers();
2596 core::list<Player*>::Iterator i;
2597 for(i = players.begin();
2598 i != players.end(); i++)
2602 Player *player = *i;
2605 con::Peer *peer = m_con.GetPeer(player->peer_id);
2606 // Copy info from peer to info struct
2608 info.address = peer->address;
2609 info.avg_rtt = peer->avg_rtt;
2611 catch(con::PeerNotFoundException &e)
2613 // Set dummy peer info
2615 info.address = Address(0,0,0,0,0);
2619 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2620 info.position = player->getPosition();
2622 list.push_back(info);
2628 void Server::peerAdded(con::Peer *peer)
2630 DSTACK(__FUNCTION_NAME);
2631 dout_server<<"Server::peerAdded(): peer->id="
2632 <<peer->id<<std::endl;
2635 c.type = PEER_ADDED;
2636 c.peer_id = peer->id;
2638 m_peer_change_queue.push_back(c);
2641 void Server::deletingPeer(con::Peer *peer, bool timeout)
2643 DSTACK(__FUNCTION_NAME);
2644 dout_server<<"Server::deletingPeer(): peer->id="
2645 <<peer->id<<", timeout="<<timeout<<std::endl;
2648 c.type = PEER_REMOVED;
2649 c.peer_id = peer->id;
2650 c.timeout = timeout;
2651 m_peer_change_queue.push_back(c);
2654 void Server::SendObjectData(float dtime)
2656 DSTACK(__FUNCTION_NAME);
2658 core::map<v3s16, bool> stepped_blocks;
2660 for(core::map<u16, RemoteClient*>::Iterator
2661 i = m_clients.getIterator();
2662 i.atEnd() == false; i++)
2664 u16 peer_id = i.getNode()->getKey();
2665 RemoteClient *client = i.getNode()->getValue();
2666 assert(client->peer_id == peer_id);
2668 if(client->serialization_version == SER_FMT_VER_INVALID)
2671 client->SendObjectData(this, dtime, stepped_blocks);
2675 void Server::SendPlayerInfos()
2677 DSTACK(__FUNCTION_NAME);
2679 //JMutexAutoLock envlock(m_env_mutex);
2681 // Get connected players
2682 core::list<Player*> players = m_env.getPlayers(true);
2684 u32 player_count = players.getSize();
2685 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2687 SharedBuffer<u8> data(datasize);
2688 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2691 core::list<Player*>::Iterator i;
2692 for(i = players.begin();
2693 i != players.end(); i++)
2695 Player *player = *i;
2697 /*dstream<<"Server sending player info for player with "
2698 "peer_id="<<player->peer_id<<std::endl;*/
2700 writeU16(&data[start], player->peer_id);
2701 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2702 start += 2+PLAYERNAME_SIZE;
2705 //JMutexAutoLock conlock(m_con_mutex);
2708 m_con.SendToAll(0, data, true);
2726 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2732 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2738 enum ItemSpecType type;
2739 // Only other one of these is used
2745 items: a pointer to an array of 9 pointers to items
2746 specs: a pointer to an array of 9 ItemSpecs
2748 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2750 u16 items_min_x = 100;
2751 u16 items_max_x = 100;
2752 u16 items_min_y = 100;
2753 u16 items_max_y = 100;
2754 for(u16 y=0; y<3; y++)
2755 for(u16 x=0; x<3; x++)
2757 if(items[y*3 + x] == NULL)
2759 if(items_min_x == 100 || x < items_min_x)
2761 if(items_min_y == 100 || y < items_min_y)
2763 if(items_max_x == 100 || x > items_max_x)
2765 if(items_max_y == 100 || y > items_max_y)
2768 // No items at all, just return false
2769 if(items_min_x == 100)
2772 u16 items_w = items_max_x - items_min_x + 1;
2773 u16 items_h = items_max_y - items_min_y + 1;
2775 u16 specs_min_x = 100;
2776 u16 specs_max_x = 100;
2777 u16 specs_min_y = 100;
2778 u16 specs_max_y = 100;
2779 for(u16 y=0; y<3; y++)
2780 for(u16 x=0; x<3; x++)
2782 if(specs[y*3 + x].type == ITEM_NONE)
2784 if(specs_min_x == 100 || x < specs_min_x)
2786 if(specs_min_y == 100 || y < specs_min_y)
2788 if(specs_max_x == 100 || x > specs_max_x)
2790 if(specs_max_y == 100 || y > specs_max_y)
2793 // No specs at all, just return false
2794 if(specs_min_x == 100)
2797 u16 specs_w = specs_max_x - specs_min_x + 1;
2798 u16 specs_h = specs_max_y - specs_min_y + 1;
2801 if(items_w != specs_w || items_h != specs_h)
2804 for(u16 y=0; y<specs_h; y++)
2805 for(u16 x=0; x<specs_w; x++)
2807 u16 items_x = items_min_x + x;
2808 u16 items_y = items_min_y + y;
2809 u16 specs_x = specs_min_x + x;
2810 u16 specs_y = specs_min_y + y;
2811 InventoryItem *item = items[items_y * 3 + items_x];
2812 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2814 if(spec.type == ITEM_NONE)
2816 // Has to be no item
2822 // There should be an item
2826 std::string itemname = item->getName();
2828 if(spec.type == ITEM_MATERIAL)
2830 if(itemname != "MaterialItem")
2832 MaterialItem *mitem = (MaterialItem*)item;
2833 if(mitem->getMaterial() != spec.num)
2836 else if(spec.type == ITEM_CRAFT)
2838 if(itemname != "CraftItem")
2840 CraftItem *mitem = (CraftItem*)item;
2841 if(mitem->getSubName() != spec.name)
2844 else if(spec.type == ITEM_TOOL)
2846 // Not supported yet
2849 else if(spec.type == ITEM_MBO)
2851 // Not supported yet
2856 // Not supported yet
2864 void Server::SendInventory(u16 peer_id)
2866 DSTACK(__FUNCTION_NAME);
2868 Player* player = m_env.getPlayer(peer_id);
2871 Calculate crafting stuff
2873 if(g_settings.getBool("creative_mode") == false)
2875 InventoryList *clist = player->inventory.getList("craft");
2876 InventoryList *rlist = player->inventory.getList("craftresult");
2879 rlist->clearItems();
2883 InventoryItem *items[9];
2884 for(u16 i=0; i<9; i++)
2886 items[i] = clist->getItem(i);
2895 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2896 if(checkItemCombination(items, specs))
2898 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2907 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2908 if(checkItemCombination(items, specs))
2910 rlist->addItem(new CraftItem("Stick", 4));
2919 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2920 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2921 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2922 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2923 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2925 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2926 if(checkItemCombination(items, specs))
2928 rlist->addItem(new MapBlockObjectItem("Sign"));
2937 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2938 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2939 if(checkItemCombination(items, specs))
2941 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2950 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2951 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2952 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2953 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2954 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2955 if(checkItemCombination(items, specs))
2957 rlist->addItem(new ToolItem("WPick", 0));
2966 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2967 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2968 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2969 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2970 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2971 if(checkItemCombination(items, specs))
2973 rlist->addItem(new ToolItem("STPick", 0));
2982 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2983 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2984 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2985 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2986 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2987 if(checkItemCombination(items, specs))
2989 rlist->addItem(new ToolItem("MesePick", 0));
2994 } // if creative_mode == false
3000 std::ostringstream os;
3001 //os.imbue(std::locale("C"));
3003 player->inventory.serialize(os);
3005 std::string s = os.str();
3007 SharedBuffer<u8> data(s.size()+2);
3008 writeU16(&data[0], TOCLIENT_INVENTORY);
3009 memcpy(&data[2], s.c_str(), s.size());
3012 m_con.Send(peer_id, 0, data, true);
3015 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3017 DSTACK(__FUNCTION_NAME);
3019 std::ostringstream os(std::ios_base::binary);
3023 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3024 os.write((char*)buf, 2);
3027 writeU16(buf, message.size());
3028 os.write((char*)buf, 2);
3031 for(u32 i=0; i<message.size(); i++)
3035 os.write((char*)buf, 2);
3039 std::string s = os.str();
3040 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3042 m_con.Send(peer_id, 0, data, true);
3045 void Server::BroadcastChatMessage(const std::wstring &message)
3047 for(core::map<u16, RemoteClient*>::Iterator
3048 i = m_clients.getIterator();
3049 i.atEnd() == false; i++)
3051 // Get client and check that it is valid
3052 RemoteClient *client = i.getNode()->getValue();
3053 assert(client->peer_id == i.getNode()->getKey());
3054 if(client->serialization_version == SER_FMT_VER_INVALID)
3057 SendChatMessage(client->peer_id, message);
3061 void Server::SendBlocks(float dtime)
3063 DSTACK(__FUNCTION_NAME);
3065 JMutexAutoLock envlock(m_env_mutex);
3067 core::array<PrioritySortedBlockTransfer> queue;
3069 s32 total_sending = 0;
3071 for(core::map<u16, RemoteClient*>::Iterator
3072 i = m_clients.getIterator();
3073 i.atEnd() == false; i++)
3075 RemoteClient *client = i.getNode()->getValue();
3076 assert(client->peer_id == i.getNode()->getKey());
3078 total_sending += client->SendingCount();
3080 if(client->serialization_version == SER_FMT_VER_INVALID)
3083 client->GetNextBlocks(this, dtime, queue);
3087 // Lowest priority number comes first.
3088 // Lowest is most important.
3091 JMutexAutoLock conlock(m_con_mutex);
3093 for(u32 i=0; i<queue.size(); i++)
3095 //TODO: Calculate limit dynamically
3096 if(total_sending >= g_settings.getS32
3097 ("max_simultaneous_block_sends_server_total"))
3100 PrioritySortedBlockTransfer q = queue[i];
3102 MapBlock *block = NULL;
3105 block = m_env.getMap().getBlockNoCreate(q.pos);
3107 catch(InvalidPositionException &e)
3112 RemoteClient *client = getClient(q.peer_id);
3114 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3116 client->SentBlock(q.pos);
3123 RemoteClient* Server::getClient(u16 peer_id)
3125 DSTACK(__FUNCTION_NAME);
3126 //JMutexAutoLock lock(m_con_mutex);
3127 core::map<u16, RemoteClient*>::Node *n;
3128 n = m_clients.find(peer_id);
3129 // A client should exist for all peers
3131 return n->getValue();
3134 void setCreativeInventory(Player *player)
3136 player->resetInventory();
3138 // Give some good picks
3140 InventoryItem *item = new ToolItem("STPick", 0);
3141 void* r = player->inventory.addItem("main", item);
3145 InventoryItem *item = new ToolItem("MesePick", 0);
3146 void* r = player->inventory.addItem("main", item);
3153 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3156 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3157 player->inventory.addItem("main", item);
3160 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3162 // Skip some materials
3163 if(i == CONTENT_WATER || i == CONTENT_TORCH
3164 || i == CONTENT_COALSTONE)
3167 InventoryItem *item = new MaterialItem(i, 1);
3168 player->inventory.addItem("main", item);
3172 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3173 void* r = player->inventory.addItem("main", item);
3178 Player *Server::emergePlayer(const char *name, const char *password,
3182 Try to get an existing player
3184 Player *player = m_env.getPlayer(name);
3187 // If player is already connected, cancel
3188 if(player->peer_id != 0)
3190 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3195 player->peer_id = peer_id;
3197 // Reset inventory to creative if in creative mode
3198 if(g_settings.getBool("creative_mode"))
3200 setCreativeInventory(player);
3204 With new map generator the map is regenerated anyway,
3205 so start at somewhere where you probably don't get underground
3207 player->setPosition(intToFloat(v3s16(
3217 If player with the wanted peer_id already exists, cancel.
3219 if(m_env.getPlayer(peer_id) != NULL)
3221 dstream<<"emergePlayer(): Player with wrong name but same"
3222 " peer_id already exists"<<std::endl;
3230 player = new ServerRemotePlayer();
3231 //player->peer_id = c.peer_id;
3232 //player->peer_id = PEER_ID_INEXISTENT;
3233 player->peer_id = peer_id;
3234 player->updateName(name);
3240 dstream<<"Server: Finding spawn place for player \""
3241 <<player->getName()<<"\""<<std::endl;
3245 player->setPosition(intToFloat(v3s16(
3252 f32 groundheight = 0;
3254 // Try to find a good place a few times
3255 for(s32 i=0; i<500; i++)
3258 // We're going to try to throw the player to this position
3259 nodepos = v2s16(-range + (myrand()%(range*2)),
3260 -range + (myrand()%(range*2)));
3261 v2s16 sectorpos = getNodeSectorPos(nodepos);
3263 m_env.getMap().emergeSector(sectorpos);
3264 // Get ground height at point
3265 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3266 // The sector should have been generated -> groundheight exists
3267 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3268 // Don't go underwater
3269 if(groundheight < WATER_LEVEL)
3271 //dstream<<"-> Underwater"<<std::endl;
3274 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3275 // Get block at point
3277 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3278 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3279 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3280 // Don't go inside ground
3282 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3283 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3284 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3285 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3286 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3287 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3289 dstream<<"-> Inside ground"<<std::endl;
3293 }catch(InvalidPositionException &e)
3295 dstream<<"-> Invalid position"<<std::endl;
3296 // Ignore invalid position
3300 // Found a good place
3301 dstream<<"Searched through "<<i<<" places."<<std::endl;
3306 // If no suitable place was not found, go above water at least.
3307 if(groundheight < WATER_LEVEL)
3308 groundheight = WATER_LEVEL;
3310 player->setPosition(intToFloat(v3s16(
3319 Add player to environment
3322 m_env.addPlayer(player);
3325 Add stuff to inventory
3328 if(g_settings.getBool("creative_mode"))
3330 setCreativeInventory(player);
3335 InventoryItem *item = new ToolItem("WPick", 32000);
3336 void* r = player->inventory.addItem("main", item);
3340 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3341 void* r = player->inventory.addItem("main", item);
3345 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3346 void* r = player->inventory.addItem("main", item);
3350 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3351 void* r = player->inventory.addItem("main", item);
3355 InventoryItem *item = new CraftItem("Stick", 4);
3356 void* r = player->inventory.addItem("main", item);
3360 InventoryItem *item = new ToolItem("WPick", 32000);
3361 void* r = player->inventory.addItem("main", item);
3365 InventoryItem *item = new ToolItem("STPick", 32000);
3366 void* r = player->inventory.addItem("main", item);
3369 /*// Give some lights
3371 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3372 bool r = player->inventory.addItem("main", item);
3376 for(u16 i=0; i<4; i++)
3378 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3379 bool r = player->inventory.addItem("main", item);
3382 /*// Give some other stuff
3384 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3385 bool r = player->inventory.addItem("main", item);
3392 } // create new player
3396 void Server::UpdateBlockWaterPressure(MapBlock *block,
3397 core::map<v3s16, MapBlock*> &modified_blocks)
3399 MapVoxelManipulator v(&m_env.getMap());
3400 v.m_disable_water_climb =
3401 g_settings.getBool("disable_water_climb");
3403 VoxelArea area(block->getPosRelative(),
3404 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3408 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3410 catch(ProcessingLimitException &e)
3412 dstream<<"Processing limit reached (1)"<<std::endl;
3415 v.blitBack(modified_blocks);
3419 void Server::handlePeerChange(PeerChange &c)
3421 JMutexAutoLock envlock(m_env_mutex);
3422 JMutexAutoLock conlock(m_con_mutex);
3424 if(c.type == PEER_ADDED)
3431 core::map<u16, RemoteClient*>::Node *n;
3432 n = m_clients.find(c.peer_id);
3433 // The client shouldn't already exist
3437 RemoteClient *client = new RemoteClient();
3438 client->peer_id = c.peer_id;
3439 m_clients.insert(client->peer_id, client);
3442 else if(c.type == PEER_REMOVED)
3449 core::map<u16, RemoteClient*>::Node *n;
3450 n = m_clients.find(c.peer_id);
3451 // The client should exist
3454 // Collect information about leaving in chat
3455 std::wstring message;
3457 std::wstring name = L"unknown";
3458 Player *player = m_env.getPlayer(c.peer_id);
3460 name = narrow_to_wide(player->getName());
3464 message += L" left game";
3466 message += L" (timed out)";
3471 m_env.removePlayer(c.peer_id);
3474 // Set player client disconnected
3476 Player *player = m_env.getPlayer(c.peer_id);
3478 player->peer_id = 0;
3482 delete m_clients[c.peer_id];
3483 m_clients.remove(c.peer_id);
3485 // Send player info to all remaining clients
3488 // Send leave chat message to all remaining clients
3489 BroadcastChatMessage(message);
3498 void Server::handlePeerChanges()
3500 while(m_peer_change_queue.size() > 0)
3502 PeerChange c = m_peer_change_queue.pop_front();
3504 dout_server<<"Server: Handling peer change: "
3505 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3508 handlePeerChange(c);
3512 void dedicated_server_loop(Server &server)
3514 DSTACK(__FUNCTION_NAME);
3516 std::cout<<std::endl;
3517 std::cout<<"========================"<<std::endl;
3518 std::cout<<"Running dedicated server"<<std::endl;
3519 std::cout<<"========================"<<std::endl;
3520 std::cout<<std::endl;
3524 // This is kind of a hack but can be done like this
3525 // because server.step() is very light
3529 static int counter = 0;
3535 core::list<PlayerInfo> list = server.getPlayerInfo();
3536 core::list<PlayerInfo>::Iterator i;
3537 static u32 sum_old = 0;
3538 u32 sum = PIChecksum(list);
3541 std::cout<<DTIME<<"Player info:"<<std::endl;
3542 for(i=list.begin(); i!=list.end(); i++)
3544 i->PrintLine(&std::cout);