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)
574 Ignore block if it is not at ground surface
575 but don't ignore water surface blocks
577 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
578 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
579 f32 y = server->m_env.getMap().getGroundHeight(p2d);
580 // The sector might not exist yet, thus no heightmap
581 if(y > GROUNDHEIGHT_VALID_MINVALUE)
583 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
584 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
585 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
591 Check if map has this block
593 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
595 bool surely_not_found_on_disk = false;
596 bool block_is_invalid = false;
599 /*if(block->isIncomplete())
601 has_incomplete_blocks = true;
607 surely_not_found_on_disk = true;
610 if(block->isValid() == false)
612 block_is_invalid = true;
616 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
617 v2s16 chunkpos = map->sector_to_chunk(p2d);
618 if(map->chunkNonVolatile(chunkpos) == false)
619 block_is_invalid = true;
620 /*MapChunk *chunk = map->getChunk(chunkpos);
622 block_is_invalid = true;
623 else if(chunk->getIsVolatile() == true)
624 block_is_invalid = true;*/
628 If block has been marked to not exist on disk (dummy)
629 and generating new ones is not wanted, skip block.
631 if(generate == false && surely_not_found_on_disk == true)
638 Record the lowest d from which a a block has been
639 found being not sent and possibly to exist
641 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
643 new_nearest_unsent_d = d;
647 Add inexistent block to emerge queue.
649 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
651 //dstream<<"asd"<<std::endl;
653 /*SharedPtr<JMutexAutoLock> lock
654 (m_num_blocks_in_emerge_queue.getLock());*/
656 //TODO: Get value from somewhere
657 // Allow only one block in emerge queue
658 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
660 //dstream<<"Adding block to emerge queue"<<std::endl;
662 // Add it to the emerge queue and trigger the thread
665 if(generate == false)
666 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
668 server->m_emerge_queue.addBlock(peer_id, p, flags);
669 server->m_emergethread.trigger();
680 PrioritySortedBlockTransfer q((float)d, p, peer_id);
684 num_blocks_selected += 1;
689 if(new_nearest_unsent_d != -1)
691 JMutexAutoLock lock(m_blocks_sent_mutex);
692 m_nearest_unsent_d = new_nearest_unsent_d;
696 void RemoteClient::SendObjectData(
699 core::map<v3s16, bool> &stepped_blocks
702 DSTACK(__FUNCTION_NAME);
704 // Can't send anything without knowing version
705 if(serialization_version == SER_FMT_VER_INVALID)
707 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
713 Send a TOCLIENT_OBJECTDATA packet.
717 u16 number of player positions
728 std::ostringstream os(std::ios_base::binary);
732 writeU16(buf, TOCLIENT_OBJECTDATA);
733 os.write((char*)buf, 2);
736 Get and write player data
739 // Get connected players
740 core::list<Player*> players = server->m_env.getPlayers(true);
742 // Write player count
743 u16 playercount = players.size();
744 writeU16(buf, playercount);
745 os.write((char*)buf, 2);
747 core::list<Player*>::Iterator i;
748 for(i = players.begin();
749 i != players.end(); i++)
753 v3f pf = player->getPosition();
754 v3f sf = player->getSpeed();
756 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
757 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
758 s32 pitch_i (player->getPitch() * 100);
759 s32 yaw_i (player->getYaw() * 100);
761 writeU16(buf, player->peer_id);
762 os.write((char*)buf, 2);
763 writeV3S32(buf, position_i);
764 os.write((char*)buf, 12);
765 writeV3S32(buf, speed_i);
766 os.write((char*)buf, 12);
767 writeS32(buf, pitch_i);
768 os.write((char*)buf, 4);
769 writeS32(buf, yaw_i);
770 os.write((char*)buf, 4);
774 Get and write object data
780 For making players to be able to build to their nearby
781 environment (building is not possible on blocks that are not
784 - Add blocks to emerge queue if they are not found
786 SUGGESTION: These could be ignored from the backside of the player
789 Player *player = server->m_env.getPlayer(peer_id);
793 v3f playerpos = player->getPosition();
794 v3f playerspeed = player->getSpeed();
796 v3s16 center_nodepos = floatToInt(playerpos);
797 v3s16 center = getNodeBlockPos(center_nodepos);
799 s16 d_max = g_settings.getS16("active_object_range");
801 // Number of blocks whose objects were written to bos
804 std::ostringstream bos(std::ios_base::binary);
806 for(s16 d = 0; d <= d_max; d++)
808 core::list<v3s16> list;
809 getFacePositions(list, d);
811 core::list<v3s16>::Iterator li;
812 for(li=list.begin(); li!=list.end(); li++)
814 v3s16 p = *li + center;
817 Ignore blocks that haven't been sent to the client
820 JMutexAutoLock sentlock(m_blocks_sent_mutex);
821 if(m_blocks_sent.find(p) == NULL)
825 // Try stepping block and add it to a send queue
830 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
833 Step block if not in stepped_blocks and add to stepped_blocks.
835 if(stepped_blocks.find(p) == NULL)
837 block->stepObjects(dtime, true, server->getDayNightRatio());
838 stepped_blocks.insert(p, true);
839 block->setChangedFlag();
842 // Skip block if there are no objects
843 if(block->getObjectCount() == 0)
852 bos.write((char*)buf, 6);
855 block->serializeObjects(bos, serialization_version);
860 Stop collecting objects if data is already too big
862 // Sum of player and object data sizes
863 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
864 // break out if data too big
865 if(sum > MAX_OBJECTDATA_SIZE)
867 goto skip_subsequent;
871 catch(InvalidPositionException &e)
874 // Add it to the emerge queue and trigger the thread.
875 // Fetch the block only if it is on disk.
877 // Grab and increment counter
878 /*SharedPtr<JMutexAutoLock> lock
879 (m_num_blocks_in_emerge_queue.getLock());
880 m_num_blocks_in_emerge_queue.m_value++;*/
882 // Add to queue as an anonymous fetch from disk
883 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
884 server->m_emerge_queue.addBlock(0, p, flags);
885 server->m_emergethread.trigger();
893 writeU16(buf, blockcount);
894 os.write((char*)buf, 2);
896 // Write block objects
903 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
906 std::string s = os.str();
907 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
908 // Send as unreliable
909 server->m_con.Send(peer_id, 0, data, false);
912 void RemoteClient::GotBlock(v3s16 p)
914 JMutexAutoLock lock(m_blocks_sending_mutex);
915 JMutexAutoLock lock2(m_blocks_sent_mutex);
916 if(m_blocks_sending.find(p) != NULL)
917 m_blocks_sending.remove(p);
920 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
921 " m_blocks_sending"<<std::endl;*/
922 m_excess_gotblocks++;
924 m_blocks_sent.insert(p, true);
927 void RemoteClient::SentBlock(v3s16 p)
929 JMutexAutoLock lock(m_blocks_sending_mutex);
930 /*if(m_blocks_sending.size() > 15)
932 dstream<<"RemoteClient::SentBlock(): "
933 <<"m_blocks_sending.size()="
934 <<m_blocks_sending.size()<<std::endl;
936 if(m_blocks_sending.find(p) == NULL)
937 m_blocks_sending.insert(p, 0.0);
939 dstream<<"RemoteClient::SentBlock(): Sent block"
940 " already in m_blocks_sending"<<std::endl;
943 void RemoteClient::SetBlockNotSent(v3s16 p)
945 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
946 JMutexAutoLock sentlock(m_blocks_sent_mutex);
948 m_nearest_unsent_d = 0;
950 if(m_blocks_sending.find(p) != NULL)
951 m_blocks_sending.remove(p);
952 if(m_blocks_sent.find(p) != NULL)
953 m_blocks_sent.remove(p);
956 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
958 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
959 JMutexAutoLock sentlock(m_blocks_sent_mutex);
961 m_nearest_unsent_d = 0;
963 for(core::map<v3s16, MapBlock*>::Iterator
964 i = blocks.getIterator();
965 i.atEnd()==false; i++)
967 v3s16 p = i.getNode()->getKey();
969 if(m_blocks_sending.find(p) != NULL)
970 m_blocks_sending.remove(p);
971 if(m_blocks_sent.find(p) != NULL)
972 m_blocks_sent.remove(p);
980 PlayerInfo::PlayerInfo()
985 void PlayerInfo::PrintLine(std::ostream *s)
988 (*s)<<"\""<<name<<"\" ("
989 <<position.X<<","<<position.Y
990 <<","<<position.Z<<") ";
992 (*s)<<" avg_rtt="<<avg_rtt;
996 u32 PIChecksum(core::list<PlayerInfo> &l)
998 core::list<PlayerInfo>::Iterator i;
1001 for(i=l.begin(); i!=l.end(); i++)
1003 checksum += a * (i->id+1);
1004 checksum ^= 0x435aafcd;
1015 std::string mapsavedir,
1017 MapParams map_params
1019 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
1020 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1022 m_emergethread(this),
1023 m_time_of_day(9000),
1025 m_time_of_day_send_timer(0),
1027 m_mapsavedir(mapsavedir)
1029 //m_flowwater_timer = 0.0;
1030 m_liquid_transform_timer = 0.0;
1031 m_print_info_timer = 0.0;
1032 m_objectdata_timer = 0.0;
1033 m_emergethread_trigger_timer = 0.0;
1034 m_savemap_timer = 0.0;
1038 m_step_dtime_mutex.Init();
1042 m_env.deSerializePlayers(m_mapsavedir);
1048 m_env.serializePlayers(m_mapsavedir);
1053 JMutexAutoLock clientslock(m_con_mutex);
1055 for(core::map<u16, RemoteClient*>::Iterator
1056 i = m_clients.getIterator();
1057 i.atEnd() == false; i++)
1060 // NOTE: These are removed by env destructor
1062 u16 peer_id = i.getNode()->getKey();
1063 JMutexAutoLock envlock(m_env_mutex);
1064 m_env.removePlayer(peer_id);
1068 delete i.getNode()->getValue();
1072 void Server::start(unsigned short port)
1074 DSTACK(__FUNCTION_NAME);
1075 // Stop thread if already running
1078 // Initialize connection
1079 m_con.setTimeoutMs(30);
1083 m_thread.setRun(true);
1086 dout_server<<"Server started on port "<<port<<std::endl;
1091 DSTACK(__FUNCTION_NAME);
1092 // Stop threads (set run=false first so both start stopping)
1093 m_thread.setRun(false);
1094 m_emergethread.setRun(false);
1096 m_emergethread.stop();
1098 dout_server<<"Server threads stopped"<<std::endl;
1101 void Server::step(float dtime)
1103 DSTACK(__FUNCTION_NAME);
1108 JMutexAutoLock lock(m_step_dtime_mutex);
1109 m_step_dtime += dtime;
1113 void Server::AsyncRunStep()
1115 DSTACK(__FUNCTION_NAME);
1119 JMutexAutoLock lock1(m_step_dtime_mutex);
1120 dtime = m_step_dtime;
1123 // Send blocks to clients
1129 //dstream<<"Server steps "<<dtime<<std::endl;
1130 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1133 JMutexAutoLock lock1(m_step_dtime_mutex);
1134 m_step_dtime -= dtime;
1141 m_uptime.set(m_uptime.get() + dtime);
1145 Update m_time_of_day
1148 m_time_counter += dtime;
1149 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1150 u32 units = (u32)(m_time_counter*speed);
1151 m_time_counter -= (f32)units / speed;
1152 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1154 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1157 Send to clients at constant intervals
1160 m_time_of_day_send_timer -= dtime;
1161 if(m_time_of_day_send_timer < 0.0)
1163 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1165 //JMutexAutoLock envlock(m_env_mutex);
1166 JMutexAutoLock conlock(m_con_mutex);
1168 for(core::map<u16, RemoteClient*>::Iterator
1169 i = m_clients.getIterator();
1170 i.atEnd() == false; i++)
1172 RemoteClient *client = i.getNode()->getValue();
1173 //Player *player = m_env.getPlayer(client->peer_id);
1175 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1176 m_time_of_day.get());
1178 m_con.Send(client->peer_id, 0, data, true);
1184 // Process connection's timeouts
1185 JMutexAutoLock lock2(m_con_mutex);
1186 m_con.RunTimeouts(dtime);
1190 // This has to be called so that the client list gets synced
1191 // with the peer list of the connection
1192 handlePeerChanges();
1197 // This also runs Map's timers
1198 JMutexAutoLock lock(m_env_mutex);
1209 m_liquid_transform_timer += dtime;
1210 if(m_liquid_transform_timer >= 1.00)
1212 m_liquid_transform_timer -= 1.00;
1214 JMutexAutoLock lock(m_env_mutex);
1216 core::map<v3s16, MapBlock*> modified_blocks;
1217 m_env.getMap().transformLiquids(modified_blocks);
1222 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1223 ServerMap &map = ((ServerMap&)m_env.getMap());
1224 map.updateLighting(modified_blocks, lighting_modified_blocks);
1226 // Add blocks modified by lighting to modified_blocks
1227 for(core::map<v3s16, MapBlock*>::Iterator
1228 i = lighting_modified_blocks.getIterator();
1229 i.atEnd() == false; i++)
1231 MapBlock *block = i.getNode()->getValue();
1232 modified_blocks.insert(block->getPos(), block);
1236 Set the modified blocks unsent for all the clients
1239 JMutexAutoLock lock2(m_con_mutex);
1241 for(core::map<u16, RemoteClient*>::Iterator
1242 i = m_clients.getIterator();
1243 i.atEnd() == false; i++)
1245 RemoteClient *client = i.getNode()->getValue();
1247 if(modified_blocks.size() > 0)
1249 // Remove block from sent history
1250 client->SetBlocksNotSent(modified_blocks);
1255 // Periodically print some info
1257 float &counter = m_print_info_timer;
1263 JMutexAutoLock lock2(m_con_mutex);
1265 for(core::map<u16, RemoteClient*>::Iterator
1266 i = m_clients.getIterator();
1267 i.atEnd() == false; i++)
1269 //u16 peer_id = i.getNode()->getKey();
1270 RemoteClient *client = i.getNode()->getValue();
1271 client->PrintInfo(std::cout);
1279 NOTE: Some of this could be moved to RemoteClient
1283 JMutexAutoLock envlock(m_env_mutex);
1284 JMutexAutoLock conlock(m_con_mutex);
1286 for(core::map<u16, RemoteClient*>::Iterator
1287 i = m_clients.getIterator();
1288 i.atEnd() == false; i++)
1290 RemoteClient *client = i.getNode()->getValue();
1291 Player *player = m_env.getPlayer(client->peer_id);
1293 JMutexAutoLock digmutex(client->m_dig_mutex);
1295 if(client->m_dig_tool_item == -1)
1298 client->m_dig_time_remaining -= dtime;
1300 if(client->m_dig_time_remaining > 0)
1302 client->m_time_from_building.set(0.0);
1306 v3s16 p_under = client->m_dig_position;
1308 // Mandatory parameter; actually used for nothing
1309 core::map<v3s16, MapBlock*> modified_blocks;
1315 // Get material at position
1316 material = m_env.getMap().getNode(p_under).d;
1317 // If it's not diggable, do nothing
1318 if(content_diggable(material) == false)
1320 derr_server<<"Server: Not finishing digging: Node not diggable"
1322 client->m_dig_tool_item = -1;
1326 catch(InvalidPositionException &e)
1328 derr_server<<"Server: Not finishing digging: Node not found"
1330 client->m_dig_tool_item = -1;
1336 SharedBuffer<u8> reply(replysize);
1337 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1338 writeS16(&reply[2], p_under.X);
1339 writeS16(&reply[4], p_under.Y);
1340 writeS16(&reply[6], p_under.Z);
1342 m_con.SendToAll(0, reply, true);
1344 if(g_settings.getBool("creative_mode") == false)
1346 // Add to inventory and send inventory
1347 InventoryItem *item = new MaterialItem(material, 1);
1348 player->inventory.addItem("main", item);
1349 SendInventory(player->peer_id);
1354 (this takes some time so it is done after the quick stuff)
1356 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1362 // Update water pressure around modification
1363 // This also adds it to m_flow_active_nodes if appropriate
1365 MapVoxelManipulator v(&m_env.getMap());
1366 v.m_disable_water_climb =
1367 g_settings.getBool("disable_water_climb");
1369 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1373 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1375 catch(ProcessingLimitException &e)
1377 dstream<<"Processing limit reached (1)"<<std::endl;
1380 v.blitBack(modified_blocks);
1385 // Send object positions
1387 float &counter = m_objectdata_timer;
1389 if(counter >= g_settings.getFloat("objectdata_interval"))
1391 JMutexAutoLock lock1(m_env_mutex);
1392 JMutexAutoLock lock2(m_con_mutex);
1393 SendObjectData(counter);
1399 // Trigger emergethread (it gets somehow gets to a
1400 // non-triggered but bysy state sometimes)
1402 float &counter = m_emergethread_trigger_timer;
1408 m_emergethread.trigger();
1414 float &counter = m_savemap_timer;
1416 if(counter >= g_settings.getFloat("server_map_save_interval"))
1420 JMutexAutoLock lock(m_env_mutex);
1422 // Save only changed parts
1423 m_env.getMap().save(true);
1425 // Delete unused sectors
1426 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1427 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1428 if(deleted_count > 0)
1430 dout_server<<"Server: Unloaded "<<deleted_count
1431 <<" sectors from memory"<<std::endl;
1435 m_env.serializePlayers(m_mapsavedir);
1440 void Server::Receive()
1442 DSTACK(__FUNCTION_NAME);
1443 u32 data_maxsize = 10000;
1444 Buffer<u8> data(data_maxsize);
1449 JMutexAutoLock conlock(m_con_mutex);
1450 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1453 // This has to be called so that the client list gets synced
1454 // with the peer list of the connection
1455 handlePeerChanges();
1457 ProcessData(*data, datasize, peer_id);
1459 catch(con::InvalidIncomingDataException &e)
1461 derr_server<<"Server::Receive(): "
1462 "InvalidIncomingDataException: what()="
1463 <<e.what()<<std::endl;
1465 catch(con::PeerNotFoundException &e)
1467 //NOTE: This is not needed anymore
1469 // The peer has been disconnected.
1470 // Find the associated player and remove it.
1472 /*JMutexAutoLock envlock(m_env_mutex);
1474 dout_server<<"ServerThread: peer_id="<<peer_id
1475 <<" has apparently closed connection. "
1476 <<"Removing player."<<std::endl;
1478 m_env.removePlayer(peer_id);*/
1482 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1484 DSTACK(__FUNCTION_NAME);
1485 // Environment is locked first.
1486 JMutexAutoLock envlock(m_env_mutex);
1487 JMutexAutoLock conlock(m_con_mutex);
1491 peer = m_con.GetPeer(peer_id);
1493 catch(con::PeerNotFoundException &e)
1495 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1496 <<peer_id<<" not found"<<std::endl;
1500 //u8 peer_ser_ver = peer->serialization_version;
1501 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1509 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1511 if(command == TOSERVER_INIT)
1513 // [0] u16 TOSERVER_INIT
1514 // [2] u8 SER_FMT_VER_HIGHEST
1515 // [3] u8[20] player_name
1520 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1521 <<peer->id<<std::endl;
1523 // First byte after command is maximum supported
1524 // serialization version
1525 u8 client_max = data[2];
1526 u8 our_max = SER_FMT_VER_HIGHEST;
1527 // Use the highest version supported by both
1528 u8 deployed = core::min_(client_max, our_max);
1529 // If it's lower than the lowest supported, give up.
1530 if(deployed < SER_FMT_VER_LOWEST)
1531 deployed = SER_FMT_VER_INVALID;
1533 //peer->serialization_version = deployed;
1534 getClient(peer->id)->pending_serialization_version = deployed;
1536 if(deployed == SER_FMT_VER_INVALID)
1538 derr_server<<DTIME<<"Server: Cannot negotiate "
1539 "serialization version with peer "
1540 <<peer_id<<std::endl;
1549 const u32 playername_size = 20;
1550 char playername[playername_size];
1551 for(u32 i=0; i<playername_size-1; i++)
1553 playername[i] = data[3+i];
1555 playername[playername_size-1] = 0;
1558 Player *player = emergePlayer(playername, "", peer_id);
1559 //Player *player = m_env.getPlayer(peer_id);
1562 // DEBUG: Test serialization
1563 std::ostringstream test_os;
1564 player->serialize(test_os);
1565 dstream<<"Player serialization test: \""<<test_os.str()
1567 std::istringstream test_is(test_os.str());
1568 player->deSerialize(test_is);
1571 // If failed, cancel
1574 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1575 <<": failed to emerge player"<<std::endl;
1580 // If a client is already connected to the player, cancel
1581 if(player->peer_id != 0)
1583 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1584 <<" tried to connect to "
1585 "an already connected player (peer_id="
1586 <<player->peer_id<<")"<<std::endl;
1589 // Set client of player
1590 player->peer_id = peer_id;
1593 // Check if player doesn't exist
1595 throw con::InvalidIncomingDataException
1596 ("Server::ProcessData(): INIT: Player doesn't exist");
1598 /*// update name if it was supplied
1599 if(datasize >= 20+3)
1602 player->updateName((const char*)&data[3]);
1605 // Now answer with a TOCLIENT_INIT
1607 SharedBuffer<u8> reply(2+1+6);
1608 writeU16(&reply[0], TOCLIENT_INIT);
1609 writeU8(&reply[2], deployed);
1610 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1612 m_con.Send(peer_id, 0, reply, true);
1616 if(command == TOSERVER_INIT2)
1618 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1619 <<peer->id<<std::endl;
1622 getClient(peer->id)->serialization_version
1623 = getClient(peer->id)->pending_serialization_version;
1626 Send some initialization data
1629 // Send player info to all players
1632 // Send inventory to player
1633 SendInventory(peer->id);
1637 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1638 m_time_of_day.get());
1639 m_con.Send(peer->id, 0, data, true);
1642 // Send information about server to player in chat
1644 std::wostringstream os(std::ios_base::binary);
1647 os<<L"uptime="<<m_uptime.get();
1648 // Information about clients
1650 for(core::map<u16, RemoteClient*>::Iterator
1651 i = m_clients.getIterator();
1652 i.atEnd() == false; i++)
1654 // Get client and check that it is valid
1655 RemoteClient *client = i.getNode()->getValue();
1656 assert(client->peer_id == i.getNode()->getKey());
1657 if(client->serialization_version == SER_FMT_VER_INVALID)
1660 Player *player = m_env.getPlayer(client->peer_id);
1661 // Get name of player
1662 std::wstring name = L"unknown";
1664 name = narrow_to_wide(player->getName());
1665 // Add name to information string
1670 SendChatMessage(peer_id, os.str());
1673 // Send information about joining in chat
1675 std::wstring name = L"unknown";
1676 Player *player = m_env.getPlayer(peer_id);
1678 name = narrow_to_wide(player->getName());
1680 std::wstring message;
1683 message += L" joined game";
1684 BroadcastChatMessage(message);
1690 if(peer_ser_ver == SER_FMT_VER_INVALID)
1692 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1693 " serialization format invalid or not initialized."
1694 " Skipping incoming command="<<command<<std::endl;
1698 Player *player = m_env.getPlayer(peer_id);
1701 derr_server<<"Server::ProcessData(): Cancelling: "
1702 "No player for peer_id="<<peer_id
1706 if(command == TOSERVER_PLAYERPOS)
1708 if(datasize < 2+12+12+4+4)
1712 v3s32 ps = readV3S32(&data[start+2]);
1713 v3s32 ss = readV3S32(&data[start+2+12]);
1714 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1715 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1716 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1717 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1718 pitch = wrapDegrees(pitch);
1719 yaw = wrapDegrees(yaw);
1720 player->setPosition(position);
1721 player->setSpeed(speed);
1722 player->setPitch(pitch);
1723 player->setYaw(yaw);
1725 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1726 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1727 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1729 else if(command == TOSERVER_GOTBLOCKS)
1742 u16 count = data[2];
1743 for(u16 i=0; i<count; i++)
1745 if((s16)datasize < 2+1+(i+1)*6)
1746 throw con::InvalidIncomingDataException
1747 ("GOTBLOCKS length is too short");
1748 v3s16 p = readV3S16(&data[2+1+i*6]);
1749 /*dstream<<"Server: GOTBLOCKS ("
1750 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1751 RemoteClient *client = getClient(peer_id);
1752 client->GotBlock(p);
1755 else if(command == TOSERVER_DELETEDBLOCKS)
1768 u16 count = data[2];
1769 for(u16 i=0; i<count; i++)
1771 if((s16)datasize < 2+1+(i+1)*6)
1772 throw con::InvalidIncomingDataException
1773 ("DELETEDBLOCKS length is too short");
1774 v3s16 p = readV3S16(&data[2+1+i*6]);
1775 /*dstream<<"Server: DELETEDBLOCKS ("
1776 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1777 RemoteClient *client = getClient(peer_id);
1778 client->SetBlockNotSent(p);
1781 else if(command == TOSERVER_CLICK_OBJECT)
1788 [2] u8 button (0=left, 1=right)
1793 u8 button = readU8(&data[2]);
1795 p.X = readS16(&data[3]);
1796 p.Y = readS16(&data[5]);
1797 p.Z = readS16(&data[7]);
1798 s16 id = readS16(&data[9]);
1799 //u16 item_i = readU16(&data[11]);
1801 MapBlock *block = NULL;
1804 block = m_env.getMap().getBlockNoCreate(p);
1806 catch(InvalidPositionException &e)
1808 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1812 MapBlockObject *obj = block->getObject(id);
1816 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1820 //TODO: Check that object is reasonably close
1825 InventoryList *ilist = player->inventory.getList("main");
1826 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1829 // Skip if inventory has no free space
1830 if(ilist->getUsedSlots() == ilist->getSize())
1832 dout_server<<"Player inventory has no free space"<<std::endl;
1837 Create the inventory item
1839 InventoryItem *item = NULL;
1840 // If it is an item-object, take the item from it
1841 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1843 item = ((ItemObject*)obj)->createInventoryItem();
1845 // Else create an item of the object
1848 item = new MapBlockObjectItem
1849 (obj->getInventoryString());
1852 // Add to inventory and send inventory
1853 ilist->addItem(item);
1854 SendInventory(player->peer_id);
1857 // Remove from block
1858 block->removeObject(id);
1861 else if(command == TOSERVER_GROUND_ACTION)
1869 [3] v3s16 nodepos_undersurface
1870 [9] v3s16 nodepos_abovesurface
1875 2: stop digging (all parameters ignored)
1877 u8 action = readU8(&data[2]);
1879 p_under.X = readS16(&data[3]);
1880 p_under.Y = readS16(&data[5]);
1881 p_under.Z = readS16(&data[7]);
1883 p_over.X = readS16(&data[9]);
1884 p_over.Y = readS16(&data[11]);
1885 p_over.Z = readS16(&data[13]);
1886 u16 item_i = readU16(&data[15]);
1888 //TODO: Check that target is reasonably close
1896 NOTE: This can be used in the future to check if
1897 somebody is cheating, by checking the timing.
1904 else if(action == 2)
1907 RemoteClient *client = getClient(peer->id);
1908 JMutexAutoLock digmutex(client->m_dig_mutex);
1909 client->m_dig_tool_item = -1;
1914 3: Digging completed
1916 else if(action == 3)
1918 // Mandatory parameter; actually used for nothing
1919 core::map<v3s16, MapBlock*> modified_blocks;
1922 u8 mineral = MINERAL_NONE;
1926 MapNode n = m_env.getMap().getNode(p_under);
1927 // Get material at position
1929 // If it's not diggable, do nothing
1930 if(content_diggable(material) == false)
1932 derr_server<<"Server: Not finishing digging: Node not diggable"
1937 mineral = n.getMineral();
1939 catch(InvalidPositionException &e)
1941 derr_server<<"Server: Not finishing digging: Node not found."
1942 <<" Adding block to emerge queue."
1944 m_emerge_queue.addBlock(peer_id,
1945 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1950 Send the removal to all other clients
1955 SharedBuffer<u8> reply(replysize);
1956 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1957 writeS16(&reply[2], p_under.X);
1958 writeS16(&reply[4], p_under.Y);
1959 writeS16(&reply[6], p_under.Z);
1961 for(core::map<u16, RemoteClient*>::Iterator
1962 i = m_clients.getIterator();
1963 i.atEnd() == false; i++)
1965 // Get client and check that it is valid
1966 RemoteClient *client = i.getNode()->getValue();
1967 assert(client->peer_id == i.getNode()->getKey());
1968 if(client->serialization_version == SER_FMT_VER_INVALID)
1971 // Don't send if it's the same one
1972 if(peer_id == client->peer_id)
1976 m_con.Send(client->peer_id, 0, reply, true);
1980 Update and send inventory
1983 if(g_settings.getBool("creative_mode") == false)
1988 InventoryList *mlist = player->inventory.getList("main");
1991 InventoryItem *item = mlist->getItem(item_i);
1992 if(item && (std::string)item->getName() == "ToolItem")
1994 ToolItem *titem = (ToolItem*)item;
1995 std::string toolname = titem->getToolName();
1997 // Get digging properties for material and tool
1998 DiggingProperties prop =
1999 getDiggingProperties(material, toolname);
2001 if(prop.diggable == false)
2003 derr_server<<"Server: WARNING: Player digged"
2004 <<" with impossible material + tool"
2005 <<" combination"<<std::endl;
2008 bool weared_out = titem->addWear(prop.wear);
2012 mlist->deleteItem(item_i);
2018 Add digged item to inventory
2021 InventoryItem *item = NULL;
2023 if(mineral != MINERAL_NONE)
2024 item = getDiggedMineralItem(mineral);
2027 item = new MaterialItem(material, 1);
2029 player->inventory.addItem("main", item);
2034 SendInventory(player->peer_id);
2039 (this takes some time so it is done after the quick stuff)
2041 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2048 // Update water pressure around modification
2049 // This also adds it to m_flow_active_nodes if appropriate
2051 MapVoxelManipulator v(&m_env.getMap());
2052 v.m_disable_water_climb =
2053 g_settings.getBool("disable_water_climb");
2055 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2059 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2061 catch(ProcessingLimitException &e)
2063 dstream<<"Processing limit reached (1)"<<std::endl;
2066 v.blitBack(modified_blocks);
2073 else if(action == 1)
2076 InventoryList *ilist = player->inventory.getList("main");
2081 InventoryItem *item = ilist->getItem(item_i);
2083 // If there is no item, it is not possible to add it anywhere
2088 Handle material items
2090 if(std::string("MaterialItem") == item->getName())
2093 // Don't add a node if this is not a free space
2094 MapNode n2 = m_env.getMap().getNode(p_over);
2095 if(content_buildable_to(n2.d) == false)
2098 catch(InvalidPositionException &e)
2100 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2101 <<" Adding block to emerge queue."
2103 m_emerge_queue.addBlock(peer_id,
2104 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2108 // Reset build time counter
2109 getClient(peer->id)->m_time_from_building.set(0.0);
2112 MaterialItem *mitem = (MaterialItem*)item;
2114 n.d = mitem->getMaterial();
2115 if(content_features(n.d).wall_mounted)
2116 n.dir = packDir(p_under - p_over);
2120 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2121 SharedBuffer<u8> reply(replysize);
2122 writeU16(&reply[0], TOCLIENT_ADDNODE);
2123 writeS16(&reply[2], p_over.X);
2124 writeS16(&reply[4], p_over.Y);
2125 writeS16(&reply[6], p_over.Z);
2126 n.serialize(&reply[8], peer_ser_ver);
2128 m_con.SendToAll(0, reply, true);
2133 InventoryList *ilist = player->inventory.getList("main");
2134 if(g_settings.getBool("creative_mode") == false && ilist)
2136 // Remove from inventory and send inventory
2137 if(mitem->getCount() == 1)
2138 ilist->deleteItem(item_i);
2142 SendInventory(peer_id);
2148 This takes some time so it is done after the quick stuff
2150 core::map<v3s16, MapBlock*> modified_blocks;
2151 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2157 InventoryList *ilist = player->inventory.getList("main");
2158 if(g_settings.getBool("creative_mode") == false && ilist)
2160 // Remove from inventory and send inventory
2161 if(mitem->getCount() == 1)
2162 ilist->deleteItem(item_i);
2166 SendInventory(peer_id);
2172 This takes some time so it is done after the quick stuff
2174 core::map<v3s16, MapBlock*> modified_blocks;
2175 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2178 Set the modified blocks unsent for all the clients
2181 //JMutexAutoLock lock2(m_con_mutex);
2183 for(core::map<u16, RemoteClient*>::Iterator
2184 i = m_clients.getIterator();
2185 i.atEnd() == false; i++)
2187 RemoteClient *client = i.getNode()->getValue();
2189 if(modified_blocks.size() > 0)
2191 // Remove block from sent history
2192 client->SetBlocksNotSent(modified_blocks);
2202 // Update water pressure around modification
2203 // This also adds it to m_flow_active_nodes if appropriate
2205 MapVoxelManipulator v(&m_env.getMap());
2206 v.m_disable_water_climb =
2207 g_settings.getBool("disable_water_climb");
2209 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2213 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2215 catch(ProcessingLimitException &e)
2217 dstream<<"Processing limit reached (1)"<<std::endl;
2220 v.blitBack(modified_blocks);
2228 v3s16 blockpos = getNodeBlockPos(p_over);
2230 MapBlock *block = NULL;
2233 block = m_env.getMap().getBlockNoCreate(blockpos);
2235 catch(InvalidPositionException &e)
2237 derr_server<<"Error while placing object: "
2238 "block not found"<<std::endl;
2242 v3s16 block_pos_i_on_map = block->getPosRelative();
2243 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2245 v3f pos = intToFloat(p_over);
2246 pos -= block_pos_f_on_map;
2248 /*dout_server<<"pos="
2249 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2252 MapBlockObject *obj = NULL;
2255 Handle block object items
2257 if(std::string("MBOItem") == item->getName())
2259 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2261 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2262 "inventorystring=\""
2263 <<oitem->getInventoryString()
2264 <<"\""<<std::endl;*/
2266 obj = oitem->createObject
2267 (pos, player->getYaw(), player->getPitch());
2274 dout_server<<"Placing a miscellaneous item on map"
2277 Create an ItemObject that contains the item.
2279 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2280 std::ostringstream os(std::ios_base::binary);
2281 item->serialize(os);
2282 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2283 iobj->setItemString(os.str());
2289 derr_server<<"WARNING: item resulted in NULL object, "
2290 <<"not placing onto map"
2295 block->addObject(obj);
2297 dout_server<<"Placed object"<<std::endl;
2299 InventoryList *ilist = player->inventory.getList("main");
2300 if(g_settings.getBool("creative_mode") == false && ilist)
2302 // Remove from inventory and send inventory
2303 ilist->deleteItem(item_i);
2305 SendInventory(peer_id);
2313 Catch invalid actions
2317 derr_server<<"WARNING: Server: Invalid action "
2318 <<action<<std::endl;
2322 else if(command == TOSERVER_RELEASE)
2331 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2334 else if(command == TOSERVER_SIGNTEXT)
2343 std::string datastring((char*)&data[2], datasize-2);
2344 std::istringstream is(datastring, std::ios_base::binary);
2347 is.read((char*)buf, 6);
2348 v3s16 blockpos = readV3S16(buf);
2349 is.read((char*)buf, 2);
2350 s16 id = readS16(buf);
2351 is.read((char*)buf, 2);
2352 u16 textlen = readU16(buf);
2354 for(u16 i=0; i<textlen; i++)
2356 is.read((char*)buf, 1);
2357 text += (char)buf[0];
2360 MapBlock *block = NULL;
2363 block = m_env.getMap().getBlockNoCreate(blockpos);
2365 catch(InvalidPositionException &e)
2367 derr_server<<"Error while setting sign text: "
2368 "block not found"<<std::endl;
2372 MapBlockObject *obj = block->getObject(id);
2375 derr_server<<"Error while setting sign text: "
2376 "object not found"<<std::endl;
2380 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2382 derr_server<<"Error while setting sign text: "
2383 "object is not a sign"<<std::endl;
2387 ((SignObject*)obj)->setText(text);
2389 obj->getBlock()->setChangedFlag();
2391 else if(command == TOSERVER_INVENTORY_ACTION)
2393 /*// Ignore inventory changes if in creative mode
2394 if(g_settings.getBool("creative_mode") == true)
2396 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2400 // Strip command and create a stream
2401 std::string datastring((char*)&data[2], datasize-2);
2402 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2403 std::istringstream is(datastring, std::ios_base::binary);
2405 InventoryAction *a = InventoryAction::deSerialize(is);
2409 Handle craftresult specially if not in creative mode
2411 bool disable_action = false;
2412 if(a->getType() == IACTION_MOVE
2413 && g_settings.getBool("creative_mode") == false)
2415 IMoveAction *ma = (IMoveAction*)a;
2416 // Don't allow moving anything to craftresult
2417 if(ma->to_name == "craftresult")
2420 disable_action = true;
2422 // When something is removed from craftresult
2423 if(ma->from_name == "craftresult")
2425 disable_action = true;
2426 // Remove stuff from craft
2427 InventoryList *clist = player->inventory.getList("craft");
2430 u16 count = ma->count;
2433 clist->decrementMaterials(count);
2436 // Feed action to player inventory
2437 a->apply(&player->inventory);
2440 // If something appeared in craftresult, throw it
2442 InventoryList *rlist = player->inventory.getList("craftresult");
2443 InventoryList *mlist = player->inventory.getList("main");
2444 if(rlist && mlist && rlist->getUsedSlots() == 1)
2446 InventoryItem *item1 = rlist->changeItem(0, NULL);
2447 mlist->addItem(item1);
2451 if(disable_action == false)
2453 // Feed action to player inventory
2454 a->apply(&player->inventory);
2459 SendInventory(player->peer_id);
2463 dstream<<"TOSERVER_INVENTORY_ACTION: "
2464 <<"InventoryAction::deSerialize() returned NULL"
2468 else if(command == TOSERVER_CHAT_MESSAGE)
2476 std::string datastring((char*)&data[2], datasize-2);
2477 std::istringstream is(datastring, std::ios_base::binary);
2480 is.read((char*)buf, 2);
2481 u16 len = readU16(buf);
2483 std::wstring message;
2484 for(u16 i=0; i<len; i++)
2486 is.read((char*)buf, 2);
2487 message += (wchar_t)readU16(buf);
2490 // Get player name of this client
2491 std::wstring name = narrow_to_wide(player->getName());
2493 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2495 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2498 Send the message to all other clients
2500 for(core::map<u16, RemoteClient*>::Iterator
2501 i = m_clients.getIterator();
2502 i.atEnd() == false; i++)
2504 // Get client and check that it is valid
2505 RemoteClient *client = i.getNode()->getValue();
2506 assert(client->peer_id == i.getNode()->getKey());
2507 if(client->serialization_version == SER_FMT_VER_INVALID)
2510 // Don't send if it's the same one
2511 if(peer_id == client->peer_id)
2514 SendChatMessage(client->peer_id, line);
2519 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2520 "unknown command "<<command<<std::endl;
2524 catch(SendFailedException &e)
2526 derr_server<<"Server::ProcessData(): SendFailedException: "
2532 /*void Server::Send(u16 peer_id, u16 channelnum,
2533 SharedBuffer<u8> data, bool reliable)
2535 JMutexAutoLock lock(m_con_mutex);
2536 m_con.Send(peer_id, channelnum, data, reliable);
2539 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2541 DSTACK(__FUNCTION_NAME);
2543 Create a packet with the block in the right format
2546 std::ostringstream os(std::ios_base::binary);
2547 block->serialize(os, ver);
2548 std::string s = os.str();
2549 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2551 u32 replysize = 8 + blockdata.getSize();
2552 SharedBuffer<u8> reply(replysize);
2553 v3s16 p = block->getPos();
2554 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2555 writeS16(&reply[2], p.X);
2556 writeS16(&reply[4], p.Y);
2557 writeS16(&reply[6], p.Z);
2558 memcpy(&reply[8], *blockdata, blockdata.getSize());
2560 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2561 <<": \tpacket size: "<<replysize<<std::endl;*/
2566 m_con.Send(peer_id, 1, reply, true);
2569 core::list<PlayerInfo> Server::getPlayerInfo()
2571 DSTACK(__FUNCTION_NAME);
2572 JMutexAutoLock envlock(m_env_mutex);
2573 JMutexAutoLock conlock(m_con_mutex);
2575 core::list<PlayerInfo> list;
2577 core::list<Player*> players = m_env.getPlayers();
2579 core::list<Player*>::Iterator i;
2580 for(i = players.begin();
2581 i != players.end(); i++)
2585 Player *player = *i;
2588 con::Peer *peer = m_con.GetPeer(player->peer_id);
2589 // Copy info from peer to info struct
2591 info.address = peer->address;
2592 info.avg_rtt = peer->avg_rtt;
2594 catch(con::PeerNotFoundException &e)
2596 // Set dummy peer info
2598 info.address = Address(0,0,0,0,0);
2602 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2603 info.position = player->getPosition();
2605 list.push_back(info);
2611 void Server::peerAdded(con::Peer *peer)
2613 DSTACK(__FUNCTION_NAME);
2614 dout_server<<"Server::peerAdded(): peer->id="
2615 <<peer->id<<std::endl;
2618 c.type = PEER_ADDED;
2619 c.peer_id = peer->id;
2621 m_peer_change_queue.push_back(c);
2624 void Server::deletingPeer(con::Peer *peer, bool timeout)
2626 DSTACK(__FUNCTION_NAME);
2627 dout_server<<"Server::deletingPeer(): peer->id="
2628 <<peer->id<<", timeout="<<timeout<<std::endl;
2631 c.type = PEER_REMOVED;
2632 c.peer_id = peer->id;
2633 c.timeout = timeout;
2634 m_peer_change_queue.push_back(c);
2637 void Server::SendObjectData(float dtime)
2639 DSTACK(__FUNCTION_NAME);
2641 core::map<v3s16, bool> stepped_blocks;
2643 for(core::map<u16, RemoteClient*>::Iterator
2644 i = m_clients.getIterator();
2645 i.atEnd() == false; i++)
2647 u16 peer_id = i.getNode()->getKey();
2648 RemoteClient *client = i.getNode()->getValue();
2649 assert(client->peer_id == peer_id);
2651 if(client->serialization_version == SER_FMT_VER_INVALID)
2654 client->SendObjectData(this, dtime, stepped_blocks);
2658 void Server::SendPlayerInfos()
2660 DSTACK(__FUNCTION_NAME);
2662 //JMutexAutoLock envlock(m_env_mutex);
2664 // Get connected players
2665 core::list<Player*> players = m_env.getPlayers(true);
2667 u32 player_count = players.getSize();
2668 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2670 SharedBuffer<u8> data(datasize);
2671 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2674 core::list<Player*>::Iterator i;
2675 for(i = players.begin();
2676 i != players.end(); i++)
2678 Player *player = *i;
2680 /*dstream<<"Server sending player info for player with "
2681 "peer_id="<<player->peer_id<<std::endl;*/
2683 writeU16(&data[start], player->peer_id);
2684 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2685 start += 2+PLAYERNAME_SIZE;
2688 //JMutexAutoLock conlock(m_con_mutex);
2691 m_con.SendToAll(0, data, true);
2709 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2715 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2721 enum ItemSpecType type;
2722 // Only other one of these is used
2728 items: a pointer to an array of 9 pointers to items
2729 specs: a pointer to an array of 9 ItemSpecs
2731 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2733 u16 items_min_x = 100;
2734 u16 items_max_x = 100;
2735 u16 items_min_y = 100;
2736 u16 items_max_y = 100;
2737 for(u16 y=0; y<3; y++)
2738 for(u16 x=0; x<3; x++)
2740 if(items[y*3 + x] == NULL)
2742 if(items_min_x == 100 || x < items_min_x)
2744 if(items_min_y == 100 || y < items_min_y)
2746 if(items_max_x == 100 || x > items_max_x)
2748 if(items_max_y == 100 || y > items_max_y)
2751 // No items at all, just return false
2752 if(items_min_x == 100)
2755 u16 items_w = items_max_x - items_min_x + 1;
2756 u16 items_h = items_max_y - items_min_y + 1;
2758 u16 specs_min_x = 100;
2759 u16 specs_max_x = 100;
2760 u16 specs_min_y = 100;
2761 u16 specs_max_y = 100;
2762 for(u16 y=0; y<3; y++)
2763 for(u16 x=0; x<3; x++)
2765 if(specs[y*3 + x].type == ITEM_NONE)
2767 if(specs_min_x == 100 || x < specs_min_x)
2769 if(specs_min_y == 100 || y < specs_min_y)
2771 if(specs_max_x == 100 || x > specs_max_x)
2773 if(specs_max_y == 100 || y > specs_max_y)
2776 // No specs at all, just return false
2777 if(specs_min_x == 100)
2780 u16 specs_w = specs_max_x - specs_min_x + 1;
2781 u16 specs_h = specs_max_y - specs_min_y + 1;
2784 if(items_w != specs_w || items_h != specs_h)
2787 for(u16 y=0; y<specs_h; y++)
2788 for(u16 x=0; x<specs_w; x++)
2790 u16 items_x = items_min_x + x;
2791 u16 items_y = items_min_y + y;
2792 u16 specs_x = specs_min_x + x;
2793 u16 specs_y = specs_min_y + y;
2794 InventoryItem *item = items[items_y * 3 + items_x];
2795 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2797 if(spec.type == ITEM_NONE)
2799 // Has to be no item
2805 // There should be an item
2809 std::string itemname = item->getName();
2811 if(spec.type == ITEM_MATERIAL)
2813 if(itemname != "MaterialItem")
2815 MaterialItem *mitem = (MaterialItem*)item;
2816 if(mitem->getMaterial() != spec.num)
2819 else if(spec.type == ITEM_CRAFT)
2821 if(itemname != "CraftItem")
2823 CraftItem *mitem = (CraftItem*)item;
2824 if(mitem->getSubName() != spec.name)
2827 else if(spec.type == ITEM_TOOL)
2829 // Not supported yet
2832 else if(spec.type == ITEM_MBO)
2834 // Not supported yet
2839 // Not supported yet
2847 void Server::SendInventory(u16 peer_id)
2849 DSTACK(__FUNCTION_NAME);
2851 Player* player = m_env.getPlayer(peer_id);
2854 Calculate crafting stuff
2856 if(g_settings.getBool("creative_mode") == false)
2858 InventoryList *clist = player->inventory.getList("craft");
2859 InventoryList *rlist = player->inventory.getList("craftresult");
2862 rlist->clearItems();
2866 InventoryItem *items[9];
2867 for(u16 i=0; i<9; i++)
2869 items[i] = clist->getItem(i);
2878 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2879 if(checkItemCombination(items, specs))
2881 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2890 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2891 if(checkItemCombination(items, specs))
2893 rlist->addItem(new CraftItem("Stick", 4));
2902 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2903 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2904 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2905 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2906 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2907 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2908 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2909 if(checkItemCombination(items, specs))
2911 rlist->addItem(new MapBlockObjectItem("Sign"));
2920 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2921 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2922 if(checkItemCombination(items, specs))
2924 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2933 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2934 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2935 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2936 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2937 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2938 if(checkItemCombination(items, specs))
2940 rlist->addItem(new ToolItem("WPick", 0));
2949 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2950 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2951 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2952 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2953 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2954 if(checkItemCombination(items, specs))
2956 rlist->addItem(new ToolItem("STPick", 0));
2965 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2966 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2967 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2968 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2969 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2970 if(checkItemCombination(items, specs))
2972 rlist->addItem(new ToolItem("MesePick", 0));
2977 } // if creative_mode == false
2983 std::ostringstream os;
2984 //os.imbue(std::locale("C"));
2986 player->inventory.serialize(os);
2988 std::string s = os.str();
2990 SharedBuffer<u8> data(s.size()+2);
2991 writeU16(&data[0], TOCLIENT_INVENTORY);
2992 memcpy(&data[2], s.c_str(), s.size());
2995 m_con.Send(peer_id, 0, data, true);
2998 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3000 DSTACK(__FUNCTION_NAME);
3002 std::ostringstream os(std::ios_base::binary);
3006 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3007 os.write((char*)buf, 2);
3010 writeU16(buf, message.size());
3011 os.write((char*)buf, 2);
3014 for(u32 i=0; i<message.size(); i++)
3018 os.write((char*)buf, 2);
3022 std::string s = os.str();
3023 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3025 m_con.Send(peer_id, 0, data, true);
3028 void Server::BroadcastChatMessage(const std::wstring &message)
3030 for(core::map<u16, RemoteClient*>::Iterator
3031 i = m_clients.getIterator();
3032 i.atEnd() == false; i++)
3034 // Get client and check that it is valid
3035 RemoteClient *client = i.getNode()->getValue();
3036 assert(client->peer_id == i.getNode()->getKey());
3037 if(client->serialization_version == SER_FMT_VER_INVALID)
3040 SendChatMessage(client->peer_id, message);
3044 void Server::SendBlocks(float dtime)
3046 DSTACK(__FUNCTION_NAME);
3048 JMutexAutoLock envlock(m_env_mutex);
3050 core::array<PrioritySortedBlockTransfer> queue;
3052 s32 total_sending = 0;
3054 for(core::map<u16, RemoteClient*>::Iterator
3055 i = m_clients.getIterator();
3056 i.atEnd() == false; i++)
3058 RemoteClient *client = i.getNode()->getValue();
3059 assert(client->peer_id == i.getNode()->getKey());
3061 total_sending += client->SendingCount();
3063 if(client->serialization_version == SER_FMT_VER_INVALID)
3066 client->GetNextBlocks(this, dtime, queue);
3070 // Lowest priority number comes first.
3071 // Lowest is most important.
3074 JMutexAutoLock conlock(m_con_mutex);
3076 for(u32 i=0; i<queue.size(); i++)
3078 //TODO: Calculate limit dynamically
3079 if(total_sending >= g_settings.getS32
3080 ("max_simultaneous_block_sends_server_total"))
3083 PrioritySortedBlockTransfer q = queue[i];
3085 MapBlock *block = NULL;
3088 block = m_env.getMap().getBlockNoCreate(q.pos);
3090 catch(InvalidPositionException &e)
3095 RemoteClient *client = getClient(q.peer_id);
3097 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3099 client->SentBlock(q.pos);
3106 RemoteClient* Server::getClient(u16 peer_id)
3108 DSTACK(__FUNCTION_NAME);
3109 //JMutexAutoLock lock(m_con_mutex);
3110 core::map<u16, RemoteClient*>::Node *n;
3111 n = m_clients.find(peer_id);
3112 // A client should exist for all peers
3114 return n->getValue();
3117 void setCreativeInventory(Player *player)
3119 player->resetInventory();
3121 // Give some good picks
3123 InventoryItem *item = new ToolItem("STPick", 0);
3124 void* r = player->inventory.addItem("main", item);
3128 InventoryItem *item = new ToolItem("MesePick", 0);
3129 void* r = player->inventory.addItem("main", item);
3136 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3139 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3140 player->inventory.addItem("main", item);
3143 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3145 // Skip some materials
3146 if(i == CONTENT_WATER || i == CONTENT_TORCH
3147 || i == CONTENT_COALSTONE)
3150 InventoryItem *item = new MaterialItem(i, 1);
3151 player->inventory.addItem("main", item);
3155 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3156 void* r = player->inventory.addItem("main", item);
3161 Player *Server::emergePlayer(const char *name, const char *password,
3165 Try to get an existing player
3167 Player *player = m_env.getPlayer(name);
3170 // If player is already connected, cancel
3171 if(player->peer_id != 0)
3173 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3178 player->peer_id = peer_id;
3180 // Reset inventory to creative if in creative mode
3181 if(g_settings.getBool("creative_mode"))
3183 setCreativeInventory(player);
3187 With new map generator the map is regenerated anyway,
3188 so start at somewhere where you probably don't get underground
3190 player->setPosition(intToFloat(v3s16(
3200 If player with the wanted peer_id already exists, cancel.
3202 if(m_env.getPlayer(peer_id) != NULL)
3204 dstream<<"emergePlayer(): Player with wrong name but same"
3205 " peer_id already exists"<<std::endl;
3213 player = new ServerRemotePlayer();
3214 //player->peer_id = c.peer_id;
3215 //player->peer_id = PEER_ID_INEXISTENT;
3216 player->peer_id = peer_id;
3217 player->updateName(name);
3223 dstream<<"Server: Finding spawn place for player \""
3224 <<player->getName()<<"\""<<std::endl;
3228 player->setPosition(intToFloat(v3s16(
3235 f32 groundheight = 0;
3237 // Try to find a good place a few times
3238 for(s32 i=0; i<500; i++)
3241 // We're going to try to throw the player to this position
3242 nodepos = v2s16(-range + (myrand()%(range*2)),
3243 -range + (myrand()%(range*2)));
3244 v2s16 sectorpos = getNodeSectorPos(nodepos);
3246 m_env.getMap().emergeSector(sectorpos);
3247 // Get ground height at point
3248 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3249 // The sector should have been generated -> groundheight exists
3250 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3251 // Don't go underwater
3252 if(groundheight < WATER_LEVEL)
3254 //dstream<<"-> Underwater"<<std::endl;
3257 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3258 // Get block at point
3260 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3261 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3262 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3263 // Don't go inside ground
3265 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3266 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3267 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3268 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3269 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3270 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3272 dstream<<"-> Inside ground"<<std::endl;
3276 }catch(InvalidPositionException &e)
3278 dstream<<"-> Invalid position"<<std::endl;
3279 // Ignore invalid position
3283 // Found a good place
3284 dstream<<"Searched through "<<i<<" places."<<std::endl;
3289 // If no suitable place was not found, go above water at least.
3290 if(groundheight < WATER_LEVEL)
3291 groundheight = WATER_LEVEL;
3293 player->setPosition(intToFloat(v3s16(
3302 Add player to environment
3305 m_env.addPlayer(player);
3308 Add stuff to inventory
3311 if(g_settings.getBool("creative_mode"))
3313 setCreativeInventory(player);
3318 InventoryItem *item = new ToolItem("WPick", 32000);
3319 void* r = player->inventory.addItem("main", item);
3323 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3324 void* r = player->inventory.addItem("main", item);
3328 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3329 void* r = player->inventory.addItem("main", item);
3333 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3334 void* r = player->inventory.addItem("main", item);
3338 InventoryItem *item = new CraftItem("Stick", 4);
3339 void* r = player->inventory.addItem("main", item);
3343 InventoryItem *item = new ToolItem("WPick", 32000);
3344 void* r = player->inventory.addItem("main", item);
3348 InventoryItem *item = new ToolItem("STPick", 32000);
3349 void* r = player->inventory.addItem("main", item);
3352 /*// Give some lights
3354 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3355 bool r = player->inventory.addItem("main", item);
3359 for(u16 i=0; i<4; i++)
3361 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3362 bool r = player->inventory.addItem("main", item);
3365 /*// Give some other stuff
3367 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3368 bool r = player->inventory.addItem("main", item);
3375 } // create new player
3379 void Server::UpdateBlockWaterPressure(MapBlock *block,
3380 core::map<v3s16, MapBlock*> &modified_blocks)
3382 MapVoxelManipulator v(&m_env.getMap());
3383 v.m_disable_water_climb =
3384 g_settings.getBool("disable_water_climb");
3386 VoxelArea area(block->getPosRelative(),
3387 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3391 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3393 catch(ProcessingLimitException &e)
3395 dstream<<"Processing limit reached (1)"<<std::endl;
3398 v.blitBack(modified_blocks);
3402 void Server::handlePeerChange(PeerChange &c)
3404 JMutexAutoLock envlock(m_env_mutex);
3405 JMutexAutoLock conlock(m_con_mutex);
3407 if(c.type == PEER_ADDED)
3414 core::map<u16, RemoteClient*>::Node *n;
3415 n = m_clients.find(c.peer_id);
3416 // The client shouldn't already exist
3420 RemoteClient *client = new RemoteClient();
3421 client->peer_id = c.peer_id;
3422 m_clients.insert(client->peer_id, client);
3425 else if(c.type == PEER_REMOVED)
3432 core::map<u16, RemoteClient*>::Node *n;
3433 n = m_clients.find(c.peer_id);
3434 // The client should exist
3437 // Collect information about leaving in chat
3438 std::wstring message;
3440 std::wstring name = L"unknown";
3441 Player *player = m_env.getPlayer(c.peer_id);
3443 name = narrow_to_wide(player->getName());
3447 message += L" left game";
3449 message += L" (timed out)";
3454 m_env.removePlayer(c.peer_id);
3457 // Set player client disconnected
3459 Player *player = m_env.getPlayer(c.peer_id);
3461 player->peer_id = 0;
3465 delete m_clients[c.peer_id];
3466 m_clients.remove(c.peer_id);
3468 // Send player info to all remaining clients
3471 // Send leave chat message to all remaining clients
3472 BroadcastChatMessage(message);
3481 void Server::handlePeerChanges()
3483 while(m_peer_change_queue.size() > 0)
3485 PeerChange c = m_peer_change_queue.pop_front();
3487 dout_server<<"Server: Handling peer change: "
3488 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3491 handlePeerChange(c);
3495 void dedicated_server_loop(Server &server)
3497 DSTACK(__FUNCTION_NAME);
3499 std::cout<<std::endl;
3500 std::cout<<"========================"<<std::endl;
3501 std::cout<<"Running dedicated server"<<std::endl;
3502 std::cout<<"========================"<<std::endl;
3503 std::cout<<std::endl;
3507 // This is kind of a hack but can be done like this
3508 // because server.step() is very light
3512 static int counter = 0;
3518 core::list<PlayerInfo> list = server.getPlayerInfo();
3519 core::list<PlayerInfo>::Iterator i;
3520 static u32 sum_old = 0;
3521 u32 sum = PIChecksum(list);
3524 std::cout<<DTIME<<"Player info:"<<std::endl;
3525 for(i=list.begin(); i!=list.end(); i++)
3527 i->PrintLine(&std::cout);