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)
517 // Limit the send area vertically to 2/3
518 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
524 If block is far away, don't generate it unless it is
527 NOTE: We can't know the ground level this way with the
533 MapSector *sector = NULL;
536 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
538 catch(InvalidPositionException &e)
544 // Get center ground height in nodes
545 f32 gh = sector->getGroundHeight(
546 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
547 // Block center y in nodes
548 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
549 // If differs a lot, don't generate
550 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
557 Don't draw if not in sight
560 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
566 Don't send already sent blocks
569 JMutexAutoLock lock(m_blocks_sent_mutex);
571 if(m_blocks_sent.find(p) != NULL)
577 NOTE: We can't know the ground level this way with the
583 Ignore block if it is not at ground surface
584 but don't ignore water surface blocks
586 v2s16 p2d(p.X*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2,
587 p.Z*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
588 f32 y = server->m_env.getMap().getGroundHeight(p2d);
589 // The sector might not exist yet, thus no heightmap
590 if(y > GROUNDHEIGHT_VALID_MINVALUE)
592 f32 by = p.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE/2;
593 if(fabs(by - y) > MAP_BLOCKSIZE + MAP_BLOCKSIZE/3
594 && fabs(by - WATER_LEVEL) >= MAP_BLOCKSIZE)
601 Check if map has this block
603 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
605 bool surely_not_found_on_disk = false;
606 bool block_is_invalid = false;
609 /*if(block->isIncomplete())
611 has_incomplete_blocks = true;
617 surely_not_found_on_disk = true;
620 if(block->isValid() == false)
622 block_is_invalid = true;
626 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
627 v2s16 chunkpos = map->sector_to_chunk(p2d);
628 if(map->chunkNonVolatile(chunkpos) == false)
629 block_is_invalid = true;
630 /*MapChunk *chunk = map->getChunk(chunkpos);
632 block_is_invalid = true;
633 else if(chunk->getIsVolatile() == true)
634 block_is_invalid = true;*/
638 If block has been marked to not exist on disk (dummy)
639 and generating new ones is not wanted, skip block.
641 if(generate == false && surely_not_found_on_disk == true)
648 Record the lowest d from which a a block has been
649 found being not sent and possibly to exist
651 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
653 new_nearest_unsent_d = d;
657 Add inexistent block to emerge queue.
659 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
661 //dstream<<"asd"<<std::endl;
663 /*SharedPtr<JMutexAutoLock> lock
664 (m_num_blocks_in_emerge_queue.getLock());*/
666 //TODO: Get value from somewhere
667 // Allow only one block in emerge queue
668 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
670 //dstream<<"Adding block to emerge queue"<<std::endl;
672 // Add it to the emerge queue and trigger the thread
675 if(generate == false)
676 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
678 server->m_emerge_queue.addBlock(peer_id, p, flags);
679 server->m_emergethread.trigger();
690 PrioritySortedBlockTransfer q((float)d, p, peer_id);
694 num_blocks_selected += 1;
699 if(new_nearest_unsent_d != -1)
701 JMutexAutoLock lock(m_blocks_sent_mutex);
702 m_nearest_unsent_d = new_nearest_unsent_d;
706 void RemoteClient::SendObjectData(
709 core::map<v3s16, bool> &stepped_blocks
712 DSTACK(__FUNCTION_NAME);
714 // Can't send anything without knowing version
715 if(serialization_version == SER_FMT_VER_INVALID)
717 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
723 Send a TOCLIENT_OBJECTDATA packet.
727 u16 number of player positions
738 std::ostringstream os(std::ios_base::binary);
742 writeU16(buf, TOCLIENT_OBJECTDATA);
743 os.write((char*)buf, 2);
746 Get and write player data
749 // Get connected players
750 core::list<Player*> players = server->m_env.getPlayers(true);
752 // Write player count
753 u16 playercount = players.size();
754 writeU16(buf, playercount);
755 os.write((char*)buf, 2);
757 core::list<Player*>::Iterator i;
758 for(i = players.begin();
759 i != players.end(); i++)
763 v3f pf = player->getPosition();
764 v3f sf = player->getSpeed();
766 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
767 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
768 s32 pitch_i (player->getPitch() * 100);
769 s32 yaw_i (player->getYaw() * 100);
771 writeU16(buf, player->peer_id);
772 os.write((char*)buf, 2);
773 writeV3S32(buf, position_i);
774 os.write((char*)buf, 12);
775 writeV3S32(buf, speed_i);
776 os.write((char*)buf, 12);
777 writeS32(buf, pitch_i);
778 os.write((char*)buf, 4);
779 writeS32(buf, yaw_i);
780 os.write((char*)buf, 4);
784 Get and write object data
790 For making players to be able to build to their nearby
791 environment (building is not possible on blocks that are not
794 - Add blocks to emerge queue if they are not found
796 SUGGESTION: These could be ignored from the backside of the player
799 Player *player = server->m_env.getPlayer(peer_id);
803 v3f playerpos = player->getPosition();
804 v3f playerspeed = player->getSpeed();
806 v3s16 center_nodepos = floatToInt(playerpos);
807 v3s16 center = getNodeBlockPos(center_nodepos);
809 s16 d_max = g_settings.getS16("active_object_range");
811 // Number of blocks whose objects were written to bos
814 std::ostringstream bos(std::ios_base::binary);
816 for(s16 d = 0; d <= d_max; d++)
818 core::list<v3s16> list;
819 getFacePositions(list, d);
821 core::list<v3s16>::Iterator li;
822 for(li=list.begin(); li!=list.end(); li++)
824 v3s16 p = *li + center;
827 Ignore blocks that haven't been sent to the client
830 JMutexAutoLock sentlock(m_blocks_sent_mutex);
831 if(m_blocks_sent.find(p) == NULL)
835 // Try stepping block and add it to a send queue
840 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
843 Step block if not in stepped_blocks and add to stepped_blocks.
845 if(stepped_blocks.find(p) == NULL)
847 block->stepObjects(dtime, true, server->getDayNightRatio());
848 stepped_blocks.insert(p, true);
849 block->setChangedFlag();
852 // Skip block if there are no objects
853 if(block->getObjectCount() == 0)
862 bos.write((char*)buf, 6);
865 block->serializeObjects(bos, serialization_version);
870 Stop collecting objects if data is already too big
872 // Sum of player and object data sizes
873 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
874 // break out if data too big
875 if(sum > MAX_OBJECTDATA_SIZE)
877 goto skip_subsequent;
881 catch(InvalidPositionException &e)
884 // Add it to the emerge queue and trigger the thread.
885 // Fetch the block only if it is on disk.
887 // Grab and increment counter
888 /*SharedPtr<JMutexAutoLock> lock
889 (m_num_blocks_in_emerge_queue.getLock());
890 m_num_blocks_in_emerge_queue.m_value++;*/
892 // Add to queue as an anonymous fetch from disk
893 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
894 server->m_emerge_queue.addBlock(0, p, flags);
895 server->m_emergethread.trigger();
903 writeU16(buf, blockcount);
904 os.write((char*)buf, 2);
906 // Write block objects
913 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
916 std::string s = os.str();
917 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
918 // Send as unreliable
919 server->m_con.Send(peer_id, 0, data, false);
922 void RemoteClient::GotBlock(v3s16 p)
924 JMutexAutoLock lock(m_blocks_sending_mutex);
925 JMutexAutoLock lock2(m_blocks_sent_mutex);
926 if(m_blocks_sending.find(p) != NULL)
927 m_blocks_sending.remove(p);
930 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
931 " m_blocks_sending"<<std::endl;*/
932 m_excess_gotblocks++;
934 m_blocks_sent.insert(p, true);
937 void RemoteClient::SentBlock(v3s16 p)
939 JMutexAutoLock lock(m_blocks_sending_mutex);
940 /*if(m_blocks_sending.size() > 15)
942 dstream<<"RemoteClient::SentBlock(): "
943 <<"m_blocks_sending.size()="
944 <<m_blocks_sending.size()<<std::endl;
946 if(m_blocks_sending.find(p) == NULL)
947 m_blocks_sending.insert(p, 0.0);
949 dstream<<"RemoteClient::SentBlock(): Sent block"
950 " already in m_blocks_sending"<<std::endl;
953 void RemoteClient::SetBlockNotSent(v3s16 p)
955 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
956 JMutexAutoLock sentlock(m_blocks_sent_mutex);
958 m_nearest_unsent_d = 0;
960 if(m_blocks_sending.find(p) != NULL)
961 m_blocks_sending.remove(p);
962 if(m_blocks_sent.find(p) != NULL)
963 m_blocks_sent.remove(p);
966 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
968 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
969 JMutexAutoLock sentlock(m_blocks_sent_mutex);
971 m_nearest_unsent_d = 0;
973 for(core::map<v3s16, MapBlock*>::Iterator
974 i = blocks.getIterator();
975 i.atEnd()==false; i++)
977 v3s16 p = i.getNode()->getKey();
979 if(m_blocks_sending.find(p) != NULL)
980 m_blocks_sending.remove(p);
981 if(m_blocks_sent.find(p) != NULL)
982 m_blocks_sent.remove(p);
990 PlayerInfo::PlayerInfo()
995 void PlayerInfo::PrintLine(std::ostream *s)
998 (*s)<<"\""<<name<<"\" ("
999 <<position.X<<","<<position.Y
1000 <<","<<position.Z<<") ";
1002 (*s)<<" avg_rtt="<<avg_rtt;
1006 u32 PIChecksum(core::list<PlayerInfo> &l)
1008 core::list<PlayerInfo>::Iterator i;
1011 for(i=l.begin(); i!=l.end(); i++)
1013 checksum += a * (i->id+1);
1014 checksum ^= 0x435aafcd;
1025 std::string mapsavedir,
1027 MapParams map_params
1029 m_env(new ServerMap(mapsavedir, hm_params, map_params), dout_server),
1030 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1032 m_emergethread(this),
1033 m_time_of_day(9000),
1035 m_time_of_day_send_timer(0),
1037 m_mapsavedir(mapsavedir)
1039 //m_flowwater_timer = 0.0;
1040 m_liquid_transform_timer = 0.0;
1041 m_print_info_timer = 0.0;
1042 m_objectdata_timer = 0.0;
1043 m_emergethread_trigger_timer = 0.0;
1044 m_savemap_timer = 0.0;
1048 m_step_dtime_mutex.Init();
1052 m_env.deSerializePlayers(m_mapsavedir);
1058 m_env.serializePlayers(m_mapsavedir);
1063 JMutexAutoLock clientslock(m_con_mutex);
1065 for(core::map<u16, RemoteClient*>::Iterator
1066 i = m_clients.getIterator();
1067 i.atEnd() == false; i++)
1070 // NOTE: These are removed by env destructor
1072 u16 peer_id = i.getNode()->getKey();
1073 JMutexAutoLock envlock(m_env_mutex);
1074 m_env.removePlayer(peer_id);
1078 delete i.getNode()->getValue();
1082 void Server::start(unsigned short port)
1084 DSTACK(__FUNCTION_NAME);
1085 // Stop thread if already running
1088 // Initialize connection
1089 m_con.setTimeoutMs(30);
1093 m_thread.setRun(true);
1096 dout_server<<"Server started on port "<<port<<std::endl;
1101 DSTACK(__FUNCTION_NAME);
1102 // Stop threads (set run=false first so both start stopping)
1103 m_thread.setRun(false);
1104 m_emergethread.setRun(false);
1106 m_emergethread.stop();
1108 dout_server<<"Server threads stopped"<<std::endl;
1111 void Server::step(float dtime)
1113 DSTACK(__FUNCTION_NAME);
1118 JMutexAutoLock lock(m_step_dtime_mutex);
1119 m_step_dtime += dtime;
1123 void Server::AsyncRunStep()
1125 DSTACK(__FUNCTION_NAME);
1129 JMutexAutoLock lock1(m_step_dtime_mutex);
1130 dtime = m_step_dtime;
1133 // Send blocks to clients
1139 //dstream<<"Server steps "<<dtime<<std::endl;
1140 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1143 JMutexAutoLock lock1(m_step_dtime_mutex);
1144 m_step_dtime -= dtime;
1151 m_uptime.set(m_uptime.get() + dtime);
1155 Update m_time_of_day
1158 m_time_counter += dtime;
1159 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1160 u32 units = (u32)(m_time_counter*speed);
1161 m_time_counter -= (f32)units / speed;
1162 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1164 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1167 Send to clients at constant intervals
1170 m_time_of_day_send_timer -= dtime;
1171 if(m_time_of_day_send_timer < 0.0)
1173 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1175 //JMutexAutoLock envlock(m_env_mutex);
1176 JMutexAutoLock conlock(m_con_mutex);
1178 for(core::map<u16, RemoteClient*>::Iterator
1179 i = m_clients.getIterator();
1180 i.atEnd() == false; i++)
1182 RemoteClient *client = i.getNode()->getValue();
1183 //Player *player = m_env.getPlayer(client->peer_id);
1185 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1186 m_time_of_day.get());
1188 m_con.Send(client->peer_id, 0, data, true);
1194 // Process connection's timeouts
1195 JMutexAutoLock lock2(m_con_mutex);
1196 m_con.RunTimeouts(dtime);
1200 // This has to be called so that the client list gets synced
1201 // with the peer list of the connection
1202 handlePeerChanges();
1207 // This also runs Map's timers
1208 JMutexAutoLock lock(m_env_mutex);
1219 m_liquid_transform_timer += dtime;
1220 if(m_liquid_transform_timer >= 1.00)
1222 m_liquid_transform_timer -= 1.00;
1224 JMutexAutoLock lock(m_env_mutex);
1226 core::map<v3s16, MapBlock*> modified_blocks;
1227 m_env.getMap().transformLiquids(modified_blocks);
1232 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1233 ServerMap &map = ((ServerMap&)m_env.getMap());
1234 map.updateLighting(modified_blocks, lighting_modified_blocks);
1236 // Add blocks modified by lighting to modified_blocks
1237 for(core::map<v3s16, MapBlock*>::Iterator
1238 i = lighting_modified_blocks.getIterator();
1239 i.atEnd() == false; i++)
1241 MapBlock *block = i.getNode()->getValue();
1242 modified_blocks.insert(block->getPos(), block);
1246 Set the modified blocks unsent for all the clients
1249 JMutexAutoLock lock2(m_con_mutex);
1251 for(core::map<u16, RemoteClient*>::Iterator
1252 i = m_clients.getIterator();
1253 i.atEnd() == false; i++)
1255 RemoteClient *client = i.getNode()->getValue();
1257 if(modified_blocks.size() > 0)
1259 // Remove block from sent history
1260 client->SetBlocksNotSent(modified_blocks);
1265 // Periodically print some info
1267 float &counter = m_print_info_timer;
1273 JMutexAutoLock lock2(m_con_mutex);
1275 for(core::map<u16, RemoteClient*>::Iterator
1276 i = m_clients.getIterator();
1277 i.atEnd() == false; i++)
1279 //u16 peer_id = i.getNode()->getKey();
1280 RemoteClient *client = i.getNode()->getValue();
1281 client->PrintInfo(std::cout);
1289 NOTE: Some of this could be moved to RemoteClient
1293 JMutexAutoLock envlock(m_env_mutex);
1294 JMutexAutoLock conlock(m_con_mutex);
1296 for(core::map<u16, RemoteClient*>::Iterator
1297 i = m_clients.getIterator();
1298 i.atEnd() == false; i++)
1300 RemoteClient *client = i.getNode()->getValue();
1301 Player *player = m_env.getPlayer(client->peer_id);
1303 JMutexAutoLock digmutex(client->m_dig_mutex);
1305 if(client->m_dig_tool_item == -1)
1308 client->m_dig_time_remaining -= dtime;
1310 if(client->m_dig_time_remaining > 0)
1312 client->m_time_from_building.set(0.0);
1316 v3s16 p_under = client->m_dig_position;
1318 // Mandatory parameter; actually used for nothing
1319 core::map<v3s16, MapBlock*> modified_blocks;
1325 // Get material at position
1326 material = m_env.getMap().getNode(p_under).d;
1327 // If it's not diggable, do nothing
1328 if(content_diggable(material) == false)
1330 derr_server<<"Server: Not finishing digging: Node not diggable"
1332 client->m_dig_tool_item = -1;
1336 catch(InvalidPositionException &e)
1338 derr_server<<"Server: Not finishing digging: Node not found"
1340 client->m_dig_tool_item = -1;
1346 SharedBuffer<u8> reply(replysize);
1347 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1348 writeS16(&reply[2], p_under.X);
1349 writeS16(&reply[4], p_under.Y);
1350 writeS16(&reply[6], p_under.Z);
1352 m_con.SendToAll(0, reply, true);
1354 if(g_settings.getBool("creative_mode") == false)
1356 // Add to inventory and send inventory
1357 InventoryItem *item = new MaterialItem(material, 1);
1358 player->inventory.addItem("main", item);
1359 SendInventory(player->peer_id);
1364 (this takes some time so it is done after the quick stuff)
1366 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1372 // Update water pressure around modification
1373 // This also adds it to m_flow_active_nodes if appropriate
1375 MapVoxelManipulator v(&m_env.getMap());
1376 v.m_disable_water_climb =
1377 g_settings.getBool("disable_water_climb");
1379 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1383 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1385 catch(ProcessingLimitException &e)
1387 dstream<<"Processing limit reached (1)"<<std::endl;
1390 v.blitBack(modified_blocks);
1395 // Send object positions
1397 float &counter = m_objectdata_timer;
1399 if(counter >= g_settings.getFloat("objectdata_interval"))
1401 JMutexAutoLock lock1(m_env_mutex);
1402 JMutexAutoLock lock2(m_con_mutex);
1403 SendObjectData(counter);
1409 // Trigger emergethread (it gets somehow gets to a
1410 // non-triggered but bysy state sometimes)
1412 float &counter = m_emergethread_trigger_timer;
1418 m_emergethread.trigger();
1424 float &counter = m_savemap_timer;
1426 if(counter >= g_settings.getFloat("server_map_save_interval"))
1430 JMutexAutoLock lock(m_env_mutex);
1432 // Save only changed parts
1433 m_env.getMap().save(true);
1435 // Delete unused sectors
1436 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1437 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1438 if(deleted_count > 0)
1440 dout_server<<"Server: Unloaded "<<deleted_count
1441 <<" sectors from memory"<<std::endl;
1445 m_env.serializePlayers(m_mapsavedir);
1450 void Server::Receive()
1452 DSTACK(__FUNCTION_NAME);
1453 u32 data_maxsize = 10000;
1454 Buffer<u8> data(data_maxsize);
1459 JMutexAutoLock conlock(m_con_mutex);
1460 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1463 // This has to be called so that the client list gets synced
1464 // with the peer list of the connection
1465 handlePeerChanges();
1467 ProcessData(*data, datasize, peer_id);
1469 catch(con::InvalidIncomingDataException &e)
1471 derr_server<<"Server::Receive(): "
1472 "InvalidIncomingDataException: what()="
1473 <<e.what()<<std::endl;
1475 catch(con::PeerNotFoundException &e)
1477 //NOTE: This is not needed anymore
1479 // The peer has been disconnected.
1480 // Find the associated player and remove it.
1482 /*JMutexAutoLock envlock(m_env_mutex);
1484 dout_server<<"ServerThread: peer_id="<<peer_id
1485 <<" has apparently closed connection. "
1486 <<"Removing player."<<std::endl;
1488 m_env.removePlayer(peer_id);*/
1492 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1494 DSTACK(__FUNCTION_NAME);
1495 // Environment is locked first.
1496 JMutexAutoLock envlock(m_env_mutex);
1497 JMutexAutoLock conlock(m_con_mutex);
1501 peer = m_con.GetPeer(peer_id);
1503 catch(con::PeerNotFoundException &e)
1505 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1506 <<peer_id<<" not found"<<std::endl;
1510 //u8 peer_ser_ver = peer->serialization_version;
1511 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1519 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1521 if(command == TOSERVER_INIT)
1523 // [0] u16 TOSERVER_INIT
1524 // [2] u8 SER_FMT_VER_HIGHEST
1525 // [3] u8[20] player_name
1530 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1531 <<peer->id<<std::endl;
1533 // First byte after command is maximum supported
1534 // serialization version
1535 u8 client_max = data[2];
1536 u8 our_max = SER_FMT_VER_HIGHEST;
1537 // Use the highest version supported by both
1538 u8 deployed = core::min_(client_max, our_max);
1539 // If it's lower than the lowest supported, give up.
1540 if(deployed < SER_FMT_VER_LOWEST)
1541 deployed = SER_FMT_VER_INVALID;
1543 //peer->serialization_version = deployed;
1544 getClient(peer->id)->pending_serialization_version = deployed;
1546 if(deployed == SER_FMT_VER_INVALID)
1548 derr_server<<DTIME<<"Server: Cannot negotiate "
1549 "serialization version with peer "
1550 <<peer_id<<std::endl;
1559 const u32 playername_size = 20;
1560 char playername[playername_size];
1561 for(u32 i=0; i<playername_size-1; i++)
1563 playername[i] = data[3+i];
1565 playername[playername_size-1] = 0;
1568 Player *player = emergePlayer(playername, "", peer_id);
1569 //Player *player = m_env.getPlayer(peer_id);
1572 // DEBUG: Test serialization
1573 std::ostringstream test_os;
1574 player->serialize(test_os);
1575 dstream<<"Player serialization test: \""<<test_os.str()
1577 std::istringstream test_is(test_os.str());
1578 player->deSerialize(test_is);
1581 // If failed, cancel
1584 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1585 <<": failed to emerge player"<<std::endl;
1590 // If a client is already connected to the player, cancel
1591 if(player->peer_id != 0)
1593 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1594 <<" tried to connect to "
1595 "an already connected player (peer_id="
1596 <<player->peer_id<<")"<<std::endl;
1599 // Set client of player
1600 player->peer_id = peer_id;
1603 // Check if player doesn't exist
1605 throw con::InvalidIncomingDataException
1606 ("Server::ProcessData(): INIT: Player doesn't exist");
1608 /*// update name if it was supplied
1609 if(datasize >= 20+3)
1612 player->updateName((const char*)&data[3]);
1615 // Now answer with a TOCLIENT_INIT
1617 SharedBuffer<u8> reply(2+1+6);
1618 writeU16(&reply[0], TOCLIENT_INIT);
1619 writeU8(&reply[2], deployed);
1620 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1622 m_con.Send(peer_id, 0, reply, true);
1626 if(command == TOSERVER_INIT2)
1628 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1629 <<peer->id<<std::endl;
1632 getClient(peer->id)->serialization_version
1633 = getClient(peer->id)->pending_serialization_version;
1636 Send some initialization data
1639 // Send player info to all players
1642 // Send inventory to player
1643 SendInventory(peer->id);
1647 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1648 m_time_of_day.get());
1649 m_con.Send(peer->id, 0, data, true);
1652 // Send information about server to player in chat
1654 std::wostringstream os(std::ios_base::binary);
1657 os<<L"uptime="<<m_uptime.get();
1658 // Information about clients
1660 for(core::map<u16, RemoteClient*>::Iterator
1661 i = m_clients.getIterator();
1662 i.atEnd() == false; i++)
1664 // Get client and check that it is valid
1665 RemoteClient *client = i.getNode()->getValue();
1666 assert(client->peer_id == i.getNode()->getKey());
1667 if(client->serialization_version == SER_FMT_VER_INVALID)
1670 Player *player = m_env.getPlayer(client->peer_id);
1671 // Get name of player
1672 std::wstring name = L"unknown";
1674 name = narrow_to_wide(player->getName());
1675 // Add name to information string
1680 SendChatMessage(peer_id, os.str());
1683 // Send information about joining in chat
1685 std::wstring name = L"unknown";
1686 Player *player = m_env.getPlayer(peer_id);
1688 name = narrow_to_wide(player->getName());
1690 std::wstring message;
1693 message += L" joined game";
1694 BroadcastChatMessage(message);
1700 if(peer_ser_ver == SER_FMT_VER_INVALID)
1702 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1703 " serialization format invalid or not initialized."
1704 " Skipping incoming command="<<command<<std::endl;
1708 Player *player = m_env.getPlayer(peer_id);
1711 derr_server<<"Server::ProcessData(): Cancelling: "
1712 "No player for peer_id="<<peer_id
1716 if(command == TOSERVER_PLAYERPOS)
1718 if(datasize < 2+12+12+4+4)
1722 v3s32 ps = readV3S32(&data[start+2]);
1723 v3s32 ss = readV3S32(&data[start+2+12]);
1724 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1725 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1726 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1727 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1728 pitch = wrapDegrees(pitch);
1729 yaw = wrapDegrees(yaw);
1730 player->setPosition(position);
1731 player->setSpeed(speed);
1732 player->setPitch(pitch);
1733 player->setYaw(yaw);
1735 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1736 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1737 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1739 else if(command == TOSERVER_GOTBLOCKS)
1752 u16 count = data[2];
1753 for(u16 i=0; i<count; i++)
1755 if((s16)datasize < 2+1+(i+1)*6)
1756 throw con::InvalidIncomingDataException
1757 ("GOTBLOCKS length is too short");
1758 v3s16 p = readV3S16(&data[2+1+i*6]);
1759 /*dstream<<"Server: GOTBLOCKS ("
1760 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1761 RemoteClient *client = getClient(peer_id);
1762 client->GotBlock(p);
1765 else if(command == TOSERVER_DELETEDBLOCKS)
1778 u16 count = data[2];
1779 for(u16 i=0; i<count; i++)
1781 if((s16)datasize < 2+1+(i+1)*6)
1782 throw con::InvalidIncomingDataException
1783 ("DELETEDBLOCKS length is too short");
1784 v3s16 p = readV3S16(&data[2+1+i*6]);
1785 /*dstream<<"Server: DELETEDBLOCKS ("
1786 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1787 RemoteClient *client = getClient(peer_id);
1788 client->SetBlockNotSent(p);
1791 else if(command == TOSERVER_CLICK_OBJECT)
1798 [2] u8 button (0=left, 1=right)
1803 u8 button = readU8(&data[2]);
1805 p.X = readS16(&data[3]);
1806 p.Y = readS16(&data[5]);
1807 p.Z = readS16(&data[7]);
1808 s16 id = readS16(&data[9]);
1809 //u16 item_i = readU16(&data[11]);
1811 MapBlock *block = NULL;
1814 block = m_env.getMap().getBlockNoCreate(p);
1816 catch(InvalidPositionException &e)
1818 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1822 MapBlockObject *obj = block->getObject(id);
1826 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1830 //TODO: Check that object is reasonably close
1835 InventoryList *ilist = player->inventory.getList("main");
1836 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1839 // Skip if inventory has no free space
1840 if(ilist->getUsedSlots() == ilist->getSize())
1842 dout_server<<"Player inventory has no free space"<<std::endl;
1847 Create the inventory item
1849 InventoryItem *item = NULL;
1850 // If it is an item-object, take the item from it
1851 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1853 item = ((ItemObject*)obj)->createInventoryItem();
1855 // Else create an item of the object
1858 item = new MapBlockObjectItem
1859 (obj->getInventoryString());
1862 // Add to inventory and send inventory
1863 ilist->addItem(item);
1864 SendInventory(player->peer_id);
1867 // Remove from block
1868 block->removeObject(id);
1871 else if(command == TOSERVER_GROUND_ACTION)
1879 [3] v3s16 nodepos_undersurface
1880 [9] v3s16 nodepos_abovesurface
1885 2: stop digging (all parameters ignored)
1887 u8 action = readU8(&data[2]);
1889 p_under.X = readS16(&data[3]);
1890 p_under.Y = readS16(&data[5]);
1891 p_under.Z = readS16(&data[7]);
1893 p_over.X = readS16(&data[9]);
1894 p_over.Y = readS16(&data[11]);
1895 p_over.Z = readS16(&data[13]);
1896 u16 item_i = readU16(&data[15]);
1898 //TODO: Check that target is reasonably close
1906 NOTE: This can be used in the future to check if
1907 somebody is cheating, by checking the timing.
1914 else if(action == 2)
1917 RemoteClient *client = getClient(peer->id);
1918 JMutexAutoLock digmutex(client->m_dig_mutex);
1919 client->m_dig_tool_item = -1;
1924 3: Digging completed
1926 else if(action == 3)
1928 // Mandatory parameter; actually used for nothing
1929 core::map<v3s16, MapBlock*> modified_blocks;
1932 u8 mineral = MINERAL_NONE;
1936 MapNode n = m_env.getMap().getNode(p_under);
1937 // Get material at position
1939 // If it's not diggable, do nothing
1940 if(content_diggable(material) == false)
1942 derr_server<<"Server: Not finishing digging: Node not diggable"
1947 mineral = n.getMineral();
1949 catch(InvalidPositionException &e)
1951 derr_server<<"Server: Not finishing digging: Node not found."
1952 <<" Adding block to emerge queue."
1954 m_emerge_queue.addBlock(peer_id,
1955 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1960 Send the removal to all other clients
1965 SharedBuffer<u8> reply(replysize);
1966 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1967 writeS16(&reply[2], p_under.X);
1968 writeS16(&reply[4], p_under.Y);
1969 writeS16(&reply[6], p_under.Z);
1971 for(core::map<u16, RemoteClient*>::Iterator
1972 i = m_clients.getIterator();
1973 i.atEnd() == false; i++)
1975 // Get client and check that it is valid
1976 RemoteClient *client = i.getNode()->getValue();
1977 assert(client->peer_id == i.getNode()->getKey());
1978 if(client->serialization_version == SER_FMT_VER_INVALID)
1981 // Don't send if it's the same one
1982 if(peer_id == client->peer_id)
1986 m_con.Send(client->peer_id, 0, reply, true);
1990 Update and send inventory
1993 if(g_settings.getBool("creative_mode") == false)
1998 InventoryList *mlist = player->inventory.getList("main");
2001 InventoryItem *item = mlist->getItem(item_i);
2002 if(item && (std::string)item->getName() == "ToolItem")
2004 ToolItem *titem = (ToolItem*)item;
2005 std::string toolname = titem->getToolName();
2007 // Get digging properties for material and tool
2008 DiggingProperties prop =
2009 getDiggingProperties(material, toolname);
2011 if(prop.diggable == false)
2013 derr_server<<"Server: WARNING: Player digged"
2014 <<" with impossible material + tool"
2015 <<" combination"<<std::endl;
2018 bool weared_out = titem->addWear(prop.wear);
2022 mlist->deleteItem(item_i);
2028 Add digged item to inventory
2031 InventoryItem *item = NULL;
2033 if(mineral != MINERAL_NONE)
2034 item = getDiggedMineralItem(mineral);
2037 item = new MaterialItem(material, 1);
2039 player->inventory.addItem("main", item);
2044 SendInventory(player->peer_id);
2049 (this takes some time so it is done after the quick stuff)
2051 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2058 // Update water pressure around modification
2059 // This also adds it to m_flow_active_nodes if appropriate
2061 MapVoxelManipulator v(&m_env.getMap());
2062 v.m_disable_water_climb =
2063 g_settings.getBool("disable_water_climb");
2065 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2069 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2071 catch(ProcessingLimitException &e)
2073 dstream<<"Processing limit reached (1)"<<std::endl;
2076 v.blitBack(modified_blocks);
2083 else if(action == 1)
2086 InventoryList *ilist = player->inventory.getList("main");
2091 InventoryItem *item = ilist->getItem(item_i);
2093 // If there is no item, it is not possible to add it anywhere
2098 Handle material items
2100 if(std::string("MaterialItem") == item->getName())
2103 // Don't add a node if this is not a free space
2104 MapNode n2 = m_env.getMap().getNode(p_over);
2105 if(content_buildable_to(n2.d) == false)
2107 // Client probably has wrong data.
2108 // Set block not sent, so that client will get
2110 dstream<<"Client "<<peer_id<<" tried to place"
2111 <<" node in invalid position; setting"
2112 <<" MapBlock not sent."<<std::endl;
2113 RemoteClient *client = getClient(peer_id);
2114 v3s16 blockpos = getNodeBlockPos(p_over);
2115 client->SetBlockNotSent(blockpos);
2119 catch(InvalidPositionException &e)
2121 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2122 <<" Adding block to emerge queue."
2124 m_emerge_queue.addBlock(peer_id,
2125 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2129 // Reset build time counter
2130 getClient(peer->id)->m_time_from_building.set(0.0);
2133 MaterialItem *mitem = (MaterialItem*)item;
2135 n.d = mitem->getMaterial();
2136 if(content_features(n.d).wall_mounted)
2137 n.dir = packDir(p_under - p_over);
2141 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2142 SharedBuffer<u8> reply(replysize);
2143 writeU16(&reply[0], TOCLIENT_ADDNODE);
2144 writeS16(&reply[2], p_over.X);
2145 writeS16(&reply[4], p_over.Y);
2146 writeS16(&reply[6], p_over.Z);
2147 n.serialize(&reply[8], peer_ser_ver);
2149 m_con.SendToAll(0, reply, true);
2154 InventoryList *ilist = player->inventory.getList("main");
2155 if(g_settings.getBool("creative_mode") == false && ilist)
2157 // Remove from inventory and send inventory
2158 if(mitem->getCount() == 1)
2159 ilist->deleteItem(item_i);
2163 SendInventory(peer_id);
2169 This takes some time so it is done after the quick stuff
2171 core::map<v3s16, MapBlock*> modified_blocks;
2172 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2178 InventoryList *ilist = player->inventory.getList("main");
2179 if(g_settings.getBool("creative_mode") == false && ilist)
2181 // Remove from inventory and send inventory
2182 if(mitem->getCount() == 1)
2183 ilist->deleteItem(item_i);
2187 SendInventory(peer_id);
2193 This takes some time so it is done after the quick stuff
2195 core::map<v3s16, MapBlock*> modified_blocks;
2196 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2199 Set the modified blocks unsent for all the clients
2202 //JMutexAutoLock lock2(m_con_mutex);
2204 for(core::map<u16, RemoteClient*>::Iterator
2205 i = m_clients.getIterator();
2206 i.atEnd() == false; i++)
2208 RemoteClient *client = i.getNode()->getValue();
2210 if(modified_blocks.size() > 0)
2212 // Remove block from sent history
2213 client->SetBlocksNotSent(modified_blocks);
2223 // Update water pressure around modification
2224 // This also adds it to m_flow_active_nodes if appropriate
2226 MapVoxelManipulator v(&m_env.getMap());
2227 v.m_disable_water_climb =
2228 g_settings.getBool("disable_water_climb");
2230 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2234 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2236 catch(ProcessingLimitException &e)
2238 dstream<<"Processing limit reached (1)"<<std::endl;
2241 v.blitBack(modified_blocks);
2249 v3s16 blockpos = getNodeBlockPos(p_over);
2251 MapBlock *block = NULL;
2254 block = m_env.getMap().getBlockNoCreate(blockpos);
2256 catch(InvalidPositionException &e)
2258 derr_server<<"Error while placing object: "
2259 "block not found"<<std::endl;
2263 v3s16 block_pos_i_on_map = block->getPosRelative();
2264 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2266 v3f pos = intToFloat(p_over);
2267 pos -= block_pos_f_on_map;
2269 /*dout_server<<"pos="
2270 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2273 MapBlockObject *obj = NULL;
2276 Handle block object items
2278 if(std::string("MBOItem") == item->getName())
2280 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2282 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2283 "inventorystring=\""
2284 <<oitem->getInventoryString()
2285 <<"\""<<std::endl;*/
2287 obj = oitem->createObject
2288 (pos, player->getYaw(), player->getPitch());
2295 dout_server<<"Placing a miscellaneous item on map"
2298 Create an ItemObject that contains the item.
2300 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2301 std::ostringstream os(std::ios_base::binary);
2302 item->serialize(os);
2303 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2304 iobj->setItemString(os.str());
2310 derr_server<<"WARNING: item resulted in NULL object, "
2311 <<"not placing onto map"
2316 block->addObject(obj);
2318 dout_server<<"Placed object"<<std::endl;
2320 InventoryList *ilist = player->inventory.getList("main");
2321 if(g_settings.getBool("creative_mode") == false && ilist)
2323 // Remove from inventory and send inventory
2324 ilist->deleteItem(item_i);
2326 SendInventory(peer_id);
2334 Catch invalid actions
2338 derr_server<<"WARNING: Server: Invalid action "
2339 <<action<<std::endl;
2343 else if(command == TOSERVER_RELEASE)
2352 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2355 else if(command == TOSERVER_SIGNTEXT)
2364 std::string datastring((char*)&data[2], datasize-2);
2365 std::istringstream is(datastring, std::ios_base::binary);
2368 is.read((char*)buf, 6);
2369 v3s16 blockpos = readV3S16(buf);
2370 is.read((char*)buf, 2);
2371 s16 id = readS16(buf);
2372 is.read((char*)buf, 2);
2373 u16 textlen = readU16(buf);
2375 for(u16 i=0; i<textlen; i++)
2377 is.read((char*)buf, 1);
2378 text += (char)buf[0];
2381 MapBlock *block = NULL;
2384 block = m_env.getMap().getBlockNoCreate(blockpos);
2386 catch(InvalidPositionException &e)
2388 derr_server<<"Error while setting sign text: "
2389 "block not found"<<std::endl;
2393 MapBlockObject *obj = block->getObject(id);
2396 derr_server<<"Error while setting sign text: "
2397 "object not found"<<std::endl;
2401 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2403 derr_server<<"Error while setting sign text: "
2404 "object is not a sign"<<std::endl;
2408 ((SignObject*)obj)->setText(text);
2410 obj->getBlock()->setChangedFlag();
2412 else if(command == TOSERVER_INVENTORY_ACTION)
2414 /*// Ignore inventory changes if in creative mode
2415 if(g_settings.getBool("creative_mode") == true)
2417 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2421 // Strip command and create a stream
2422 std::string datastring((char*)&data[2], datasize-2);
2423 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2424 std::istringstream is(datastring, std::ios_base::binary);
2426 InventoryAction *a = InventoryAction::deSerialize(is);
2430 Handle craftresult specially if not in creative mode
2432 bool disable_action = false;
2433 if(a->getType() == IACTION_MOVE
2434 && g_settings.getBool("creative_mode") == false)
2436 IMoveAction *ma = (IMoveAction*)a;
2437 // Don't allow moving anything to craftresult
2438 if(ma->to_name == "craftresult")
2441 disable_action = true;
2443 // When something is removed from craftresult
2444 if(ma->from_name == "craftresult")
2446 disable_action = true;
2447 // Remove stuff from craft
2448 InventoryList *clist = player->inventory.getList("craft");
2451 u16 count = ma->count;
2454 clist->decrementMaterials(count);
2457 // Feed action to player inventory
2458 a->apply(&player->inventory);
2461 // If something appeared in craftresult, throw it
2463 InventoryList *rlist = player->inventory.getList("craftresult");
2464 InventoryList *mlist = player->inventory.getList("main");
2465 if(rlist && mlist && rlist->getUsedSlots() == 1)
2467 InventoryItem *item1 = rlist->changeItem(0, NULL);
2468 mlist->addItem(item1);
2472 if(disable_action == false)
2474 // Feed action to player inventory
2475 a->apply(&player->inventory);
2480 SendInventory(player->peer_id);
2484 dstream<<"TOSERVER_INVENTORY_ACTION: "
2485 <<"InventoryAction::deSerialize() returned NULL"
2489 else if(command == TOSERVER_CHAT_MESSAGE)
2497 std::string datastring((char*)&data[2], datasize-2);
2498 std::istringstream is(datastring, std::ios_base::binary);
2501 is.read((char*)buf, 2);
2502 u16 len = readU16(buf);
2504 std::wstring message;
2505 for(u16 i=0; i<len; i++)
2507 is.read((char*)buf, 2);
2508 message += (wchar_t)readU16(buf);
2511 // Get player name of this client
2512 std::wstring name = narrow_to_wide(player->getName());
2514 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2516 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2519 Send the message to all other clients
2521 for(core::map<u16, RemoteClient*>::Iterator
2522 i = m_clients.getIterator();
2523 i.atEnd() == false; i++)
2525 // Get client and check that it is valid
2526 RemoteClient *client = i.getNode()->getValue();
2527 assert(client->peer_id == i.getNode()->getKey());
2528 if(client->serialization_version == SER_FMT_VER_INVALID)
2531 // Don't send if it's the same one
2532 if(peer_id == client->peer_id)
2535 SendChatMessage(client->peer_id, line);
2540 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2541 "unknown command "<<command<<std::endl;
2545 catch(SendFailedException &e)
2547 derr_server<<"Server::ProcessData(): SendFailedException: "
2553 /*void Server::Send(u16 peer_id, u16 channelnum,
2554 SharedBuffer<u8> data, bool reliable)
2556 JMutexAutoLock lock(m_con_mutex);
2557 m_con.Send(peer_id, channelnum, data, reliable);
2560 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2562 DSTACK(__FUNCTION_NAME);
2564 Create a packet with the block in the right format
2567 std::ostringstream os(std::ios_base::binary);
2568 block->serialize(os, ver);
2569 std::string s = os.str();
2570 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2572 u32 replysize = 8 + blockdata.getSize();
2573 SharedBuffer<u8> reply(replysize);
2574 v3s16 p = block->getPos();
2575 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2576 writeS16(&reply[2], p.X);
2577 writeS16(&reply[4], p.Y);
2578 writeS16(&reply[6], p.Z);
2579 memcpy(&reply[8], *blockdata, blockdata.getSize());
2581 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2582 <<": \tpacket size: "<<replysize<<std::endl;*/
2587 m_con.Send(peer_id, 1, reply, true);
2590 core::list<PlayerInfo> Server::getPlayerInfo()
2592 DSTACK(__FUNCTION_NAME);
2593 JMutexAutoLock envlock(m_env_mutex);
2594 JMutexAutoLock conlock(m_con_mutex);
2596 core::list<PlayerInfo> list;
2598 core::list<Player*> players = m_env.getPlayers();
2600 core::list<Player*>::Iterator i;
2601 for(i = players.begin();
2602 i != players.end(); i++)
2606 Player *player = *i;
2609 con::Peer *peer = m_con.GetPeer(player->peer_id);
2610 // Copy info from peer to info struct
2612 info.address = peer->address;
2613 info.avg_rtt = peer->avg_rtt;
2615 catch(con::PeerNotFoundException &e)
2617 // Set dummy peer info
2619 info.address = Address(0,0,0,0,0);
2623 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2624 info.position = player->getPosition();
2626 list.push_back(info);
2632 void Server::peerAdded(con::Peer *peer)
2634 DSTACK(__FUNCTION_NAME);
2635 dout_server<<"Server::peerAdded(): peer->id="
2636 <<peer->id<<std::endl;
2639 c.type = PEER_ADDED;
2640 c.peer_id = peer->id;
2642 m_peer_change_queue.push_back(c);
2645 void Server::deletingPeer(con::Peer *peer, bool timeout)
2647 DSTACK(__FUNCTION_NAME);
2648 dout_server<<"Server::deletingPeer(): peer->id="
2649 <<peer->id<<", timeout="<<timeout<<std::endl;
2652 c.type = PEER_REMOVED;
2653 c.peer_id = peer->id;
2654 c.timeout = timeout;
2655 m_peer_change_queue.push_back(c);
2658 void Server::SendObjectData(float dtime)
2660 DSTACK(__FUNCTION_NAME);
2662 core::map<v3s16, bool> stepped_blocks;
2664 for(core::map<u16, RemoteClient*>::Iterator
2665 i = m_clients.getIterator();
2666 i.atEnd() == false; i++)
2668 u16 peer_id = i.getNode()->getKey();
2669 RemoteClient *client = i.getNode()->getValue();
2670 assert(client->peer_id == peer_id);
2672 if(client->serialization_version == SER_FMT_VER_INVALID)
2675 client->SendObjectData(this, dtime, stepped_blocks);
2679 void Server::SendPlayerInfos()
2681 DSTACK(__FUNCTION_NAME);
2683 //JMutexAutoLock envlock(m_env_mutex);
2685 // Get connected players
2686 core::list<Player*> players = m_env.getPlayers(true);
2688 u32 player_count = players.getSize();
2689 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2691 SharedBuffer<u8> data(datasize);
2692 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2695 core::list<Player*>::Iterator i;
2696 for(i = players.begin();
2697 i != players.end(); i++)
2699 Player *player = *i;
2701 /*dstream<<"Server sending player info for player with "
2702 "peer_id="<<player->peer_id<<std::endl;*/
2704 writeU16(&data[start], player->peer_id);
2705 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2706 start += 2+PLAYERNAME_SIZE;
2709 //JMutexAutoLock conlock(m_con_mutex);
2712 m_con.SendToAll(0, data, true);
2730 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2736 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2742 enum ItemSpecType type;
2743 // Only other one of these is used
2749 items: a pointer to an array of 9 pointers to items
2750 specs: a pointer to an array of 9 ItemSpecs
2752 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2754 u16 items_min_x = 100;
2755 u16 items_max_x = 100;
2756 u16 items_min_y = 100;
2757 u16 items_max_y = 100;
2758 for(u16 y=0; y<3; y++)
2759 for(u16 x=0; x<3; x++)
2761 if(items[y*3 + x] == NULL)
2763 if(items_min_x == 100 || x < items_min_x)
2765 if(items_min_y == 100 || y < items_min_y)
2767 if(items_max_x == 100 || x > items_max_x)
2769 if(items_max_y == 100 || y > items_max_y)
2772 // No items at all, just return false
2773 if(items_min_x == 100)
2776 u16 items_w = items_max_x - items_min_x + 1;
2777 u16 items_h = items_max_y - items_min_y + 1;
2779 u16 specs_min_x = 100;
2780 u16 specs_max_x = 100;
2781 u16 specs_min_y = 100;
2782 u16 specs_max_y = 100;
2783 for(u16 y=0; y<3; y++)
2784 for(u16 x=0; x<3; x++)
2786 if(specs[y*3 + x].type == ITEM_NONE)
2788 if(specs_min_x == 100 || x < specs_min_x)
2790 if(specs_min_y == 100 || y < specs_min_y)
2792 if(specs_max_x == 100 || x > specs_max_x)
2794 if(specs_max_y == 100 || y > specs_max_y)
2797 // No specs at all, just return false
2798 if(specs_min_x == 100)
2801 u16 specs_w = specs_max_x - specs_min_x + 1;
2802 u16 specs_h = specs_max_y - specs_min_y + 1;
2805 if(items_w != specs_w || items_h != specs_h)
2808 for(u16 y=0; y<specs_h; y++)
2809 for(u16 x=0; x<specs_w; x++)
2811 u16 items_x = items_min_x + x;
2812 u16 items_y = items_min_y + y;
2813 u16 specs_x = specs_min_x + x;
2814 u16 specs_y = specs_min_y + y;
2815 InventoryItem *item = items[items_y * 3 + items_x];
2816 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2818 if(spec.type == ITEM_NONE)
2820 // Has to be no item
2826 // There should be an item
2830 std::string itemname = item->getName();
2832 if(spec.type == ITEM_MATERIAL)
2834 if(itemname != "MaterialItem")
2836 MaterialItem *mitem = (MaterialItem*)item;
2837 if(mitem->getMaterial() != spec.num)
2840 else if(spec.type == ITEM_CRAFT)
2842 if(itemname != "CraftItem")
2844 CraftItem *mitem = (CraftItem*)item;
2845 if(mitem->getSubName() != spec.name)
2848 else if(spec.type == ITEM_TOOL)
2850 // Not supported yet
2853 else if(spec.type == ITEM_MBO)
2855 // Not supported yet
2860 // Not supported yet
2868 void Server::SendInventory(u16 peer_id)
2870 DSTACK(__FUNCTION_NAME);
2872 Player* player = m_env.getPlayer(peer_id);
2875 Calculate crafting stuff
2877 if(g_settings.getBool("creative_mode") == false)
2879 InventoryList *clist = player->inventory.getList("craft");
2880 InventoryList *rlist = player->inventory.getList("craftresult");
2883 rlist->clearItems();
2887 InventoryItem *items[9];
2888 for(u16 i=0; i<9; i++)
2890 items[i] = clist->getItem(i);
2899 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2900 if(checkItemCombination(items, specs))
2902 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2911 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2912 if(checkItemCombination(items, specs))
2914 rlist->addItem(new CraftItem("Stick", 4));
2923 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2925 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2926 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2927 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2928 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2929 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2930 if(checkItemCombination(items, specs))
2932 rlist->addItem(new MapBlockObjectItem("Sign"));
2941 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2942 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2943 if(checkItemCombination(items, specs))
2945 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2954 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2955 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2956 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2957 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2958 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2959 if(checkItemCombination(items, specs))
2961 rlist->addItem(new ToolItem("WPick", 0));
2970 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2971 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2972 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2973 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2974 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2975 if(checkItemCombination(items, specs))
2977 rlist->addItem(new ToolItem("STPick", 0));
2986 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2987 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2988 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2989 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2990 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2991 if(checkItemCombination(items, specs))
2993 rlist->addItem(new ToolItem("MesePick", 0));
2998 } // if creative_mode == false
3004 std::ostringstream os;
3005 //os.imbue(std::locale("C"));
3007 player->inventory.serialize(os);
3009 std::string s = os.str();
3011 SharedBuffer<u8> data(s.size()+2);
3012 writeU16(&data[0], TOCLIENT_INVENTORY);
3013 memcpy(&data[2], s.c_str(), s.size());
3016 m_con.Send(peer_id, 0, data, true);
3019 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3021 DSTACK(__FUNCTION_NAME);
3023 std::ostringstream os(std::ios_base::binary);
3027 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3028 os.write((char*)buf, 2);
3031 writeU16(buf, message.size());
3032 os.write((char*)buf, 2);
3035 for(u32 i=0; i<message.size(); i++)
3039 os.write((char*)buf, 2);
3043 std::string s = os.str();
3044 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3046 m_con.Send(peer_id, 0, data, true);
3049 void Server::BroadcastChatMessage(const std::wstring &message)
3051 for(core::map<u16, RemoteClient*>::Iterator
3052 i = m_clients.getIterator();
3053 i.atEnd() == false; i++)
3055 // Get client and check that it is valid
3056 RemoteClient *client = i.getNode()->getValue();
3057 assert(client->peer_id == i.getNode()->getKey());
3058 if(client->serialization_version == SER_FMT_VER_INVALID)
3061 SendChatMessage(client->peer_id, message);
3065 void Server::SendBlocks(float dtime)
3067 DSTACK(__FUNCTION_NAME);
3069 JMutexAutoLock envlock(m_env_mutex);
3071 core::array<PrioritySortedBlockTransfer> queue;
3073 s32 total_sending = 0;
3075 for(core::map<u16, RemoteClient*>::Iterator
3076 i = m_clients.getIterator();
3077 i.atEnd() == false; i++)
3079 RemoteClient *client = i.getNode()->getValue();
3080 assert(client->peer_id == i.getNode()->getKey());
3082 total_sending += client->SendingCount();
3084 if(client->serialization_version == SER_FMT_VER_INVALID)
3087 client->GetNextBlocks(this, dtime, queue);
3091 // Lowest priority number comes first.
3092 // Lowest is most important.
3095 JMutexAutoLock conlock(m_con_mutex);
3097 for(u32 i=0; i<queue.size(); i++)
3099 //TODO: Calculate limit dynamically
3100 if(total_sending >= g_settings.getS32
3101 ("max_simultaneous_block_sends_server_total"))
3104 PrioritySortedBlockTransfer q = queue[i];
3106 MapBlock *block = NULL;
3109 block = m_env.getMap().getBlockNoCreate(q.pos);
3111 catch(InvalidPositionException &e)
3116 RemoteClient *client = getClient(q.peer_id);
3118 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3120 client->SentBlock(q.pos);
3127 RemoteClient* Server::getClient(u16 peer_id)
3129 DSTACK(__FUNCTION_NAME);
3130 //JMutexAutoLock lock(m_con_mutex);
3131 core::map<u16, RemoteClient*>::Node *n;
3132 n = m_clients.find(peer_id);
3133 // A client should exist for all peers
3135 return n->getValue();
3138 void setCreativeInventory(Player *player)
3140 player->resetInventory();
3142 // Give some good picks
3144 InventoryItem *item = new ToolItem("STPick", 0);
3145 void* r = player->inventory.addItem("main", item);
3149 InventoryItem *item = new ToolItem("MesePick", 0);
3150 void* r = player->inventory.addItem("main", item);
3157 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3160 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3161 player->inventory.addItem("main", item);
3164 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3166 // Skip some materials
3167 if(i == CONTENT_WATER || i == CONTENT_TORCH
3168 || i == CONTENT_COALSTONE)
3171 InventoryItem *item = new MaterialItem(i, 1);
3172 player->inventory.addItem("main", item);
3176 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3177 void* r = player->inventory.addItem("main", item);
3182 Player *Server::emergePlayer(const char *name, const char *password,
3186 Try to get an existing player
3188 Player *player = m_env.getPlayer(name);
3191 // If player is already connected, cancel
3192 if(player->peer_id != 0)
3194 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3199 player->peer_id = peer_id;
3201 // Reset inventory to creative if in creative mode
3202 if(g_settings.getBool("creative_mode"))
3204 setCreativeInventory(player);
3208 With new map generator the map is regenerated anyway,
3209 so start at somewhere where you probably don't get underground
3211 player->setPosition(intToFloat(v3s16(
3221 If player with the wanted peer_id already exists, cancel.
3223 if(m_env.getPlayer(peer_id) != NULL)
3225 dstream<<"emergePlayer(): Player with wrong name but same"
3226 " peer_id already exists"<<std::endl;
3234 player = new ServerRemotePlayer();
3235 //player->peer_id = c.peer_id;
3236 //player->peer_id = PEER_ID_INEXISTENT;
3237 player->peer_id = peer_id;
3238 player->updateName(name);
3244 dstream<<"Server: Finding spawn place for player \""
3245 <<player->getName()<<"\""<<std::endl;
3249 player->setPosition(intToFloat(v3s16(
3256 f32 groundheight = 0;
3258 // Try to find a good place a few times
3259 for(s32 i=0; i<500; i++)
3262 // We're going to try to throw the player to this position
3263 nodepos = v2s16(-range + (myrand()%(range*2)),
3264 -range + (myrand()%(range*2)));
3265 v2s16 sectorpos = getNodeSectorPos(nodepos);
3267 m_env.getMap().emergeSector(sectorpos);
3268 // Get ground height at point
3269 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3270 // The sector should have been generated -> groundheight exists
3271 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3272 // Don't go underwater
3273 if(groundheight < WATER_LEVEL)
3275 //dstream<<"-> Underwater"<<std::endl;
3278 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3279 // Get block at point
3281 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3282 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3283 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3284 // Don't go inside ground
3286 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3287 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3288 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3289 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3290 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3291 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3293 dstream<<"-> Inside ground"<<std::endl;
3297 }catch(InvalidPositionException &e)
3299 dstream<<"-> Invalid position"<<std::endl;
3300 // Ignore invalid position
3304 // Found a good place
3305 dstream<<"Searched through "<<i<<" places."<<std::endl;
3310 // If no suitable place was not found, go above water at least.
3311 if(groundheight < WATER_LEVEL)
3312 groundheight = WATER_LEVEL;
3314 player->setPosition(intToFloat(v3s16(
3323 Add player to environment
3326 m_env.addPlayer(player);
3329 Add stuff to inventory
3332 if(g_settings.getBool("creative_mode"))
3334 setCreativeInventory(player);
3339 InventoryItem *item = new ToolItem("WPick", 32000);
3340 void* r = player->inventory.addItem("main", item);
3344 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3345 void* r = player->inventory.addItem("main", item);
3349 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3350 void* r = player->inventory.addItem("main", item);
3354 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3355 void* r = player->inventory.addItem("main", item);
3359 InventoryItem *item = new CraftItem("Stick", 4);
3360 void* r = player->inventory.addItem("main", item);
3364 InventoryItem *item = new ToolItem("WPick", 32000);
3365 void* r = player->inventory.addItem("main", item);
3369 InventoryItem *item = new ToolItem("STPick", 32000);
3370 void* r = player->inventory.addItem("main", item);
3373 /*// Give some lights
3375 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3376 bool r = player->inventory.addItem("main", item);
3380 for(u16 i=0; i<4; i++)
3382 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3383 bool r = player->inventory.addItem("main", item);
3386 /*// Give some other stuff
3388 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3389 bool r = player->inventory.addItem("main", item);
3396 } // create new player
3400 void Server::UpdateBlockWaterPressure(MapBlock *block,
3401 core::map<v3s16, MapBlock*> &modified_blocks)
3403 MapVoxelManipulator v(&m_env.getMap());
3404 v.m_disable_water_climb =
3405 g_settings.getBool("disable_water_climb");
3407 VoxelArea area(block->getPosRelative(),
3408 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3412 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3414 catch(ProcessingLimitException &e)
3416 dstream<<"Processing limit reached (1)"<<std::endl;
3419 v.blitBack(modified_blocks);
3423 void Server::handlePeerChange(PeerChange &c)
3425 JMutexAutoLock envlock(m_env_mutex);
3426 JMutexAutoLock conlock(m_con_mutex);
3428 if(c.type == PEER_ADDED)
3435 core::map<u16, RemoteClient*>::Node *n;
3436 n = m_clients.find(c.peer_id);
3437 // The client shouldn't already exist
3441 RemoteClient *client = new RemoteClient();
3442 client->peer_id = c.peer_id;
3443 m_clients.insert(client->peer_id, client);
3446 else if(c.type == PEER_REMOVED)
3453 core::map<u16, RemoteClient*>::Node *n;
3454 n = m_clients.find(c.peer_id);
3455 // The client should exist
3458 // Collect information about leaving in chat
3459 std::wstring message;
3461 std::wstring name = L"unknown";
3462 Player *player = m_env.getPlayer(c.peer_id);
3464 name = narrow_to_wide(player->getName());
3468 message += L" left game";
3470 message += L" (timed out)";
3475 m_env.removePlayer(c.peer_id);
3478 // Set player client disconnected
3480 Player *player = m_env.getPlayer(c.peer_id);
3482 player->peer_id = 0;
3486 delete m_clients[c.peer_id];
3487 m_clients.remove(c.peer_id);
3489 // Send player info to all remaining clients
3492 // Send leave chat message to all remaining clients
3493 BroadcastChatMessage(message);
3502 void Server::handlePeerChanges()
3504 while(m_peer_change_queue.size() > 0)
3506 PeerChange c = m_peer_change_queue.pop_front();
3508 dout_server<<"Server: Handling peer change: "
3509 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3512 handlePeerChange(c);
3516 void dedicated_server_loop(Server &server)
3518 DSTACK(__FUNCTION_NAME);
3520 std::cout<<std::endl;
3521 std::cout<<"========================"<<std::endl;
3522 std::cout<<"Running dedicated server"<<std::endl;
3523 std::cout<<"========================"<<std::endl;
3524 std::cout<<std::endl;
3528 // This is kind of a hack but can be done like this
3529 // because server.step() is very light
3533 static int counter = 0;
3539 core::list<PlayerInfo> list = server.getPlayerInfo();
3540 core::list<PlayerInfo>::Iterator i;
3541 static u32 sum_old = 0;
3542 u32 sum = PIChecksum(list);
3545 std::cout<<DTIME<<"Player info:"<<std::endl;
3546 for(i=list.begin(); i!=list.end(); i++)
3548 i->PrintLine(&std::cout);