3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
27 #include "clientserver.h"
29 #include "jmutexautolock.h"
31 #include "constants.h"
33 #include "materials.h"
36 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
38 void * ServerThread::Thread()
42 DSTACK(__FUNCTION_NAME);
44 BEGIN_DEBUG_EXCEPTION_HANDLER
49 m_server->AsyncRunStep();
51 //dout_server<<"Running m_server->Receive()"<<std::endl;
54 catch(con::NoIncomingDataException &e)
57 catch(con::PeerNotFoundException &e)
59 dout_server<<"Server: PeerNotFoundException"<<std::endl;
63 END_DEBUG_EXCEPTION_HANDLER
68 void * EmergeThread::Thread()
72 DSTACK(__FUNCTION_NAME);
76 BEGIN_DEBUG_EXCEPTION_HANDLER
79 Get block info from queue, emerge them and send them
82 After queue is empty, exit.
86 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
90 SharedPtr<QueuedBlockEmerge> q(qptr);
94 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
96 //TimeTaker timer("block emerge");
99 Try to emerge it from somewhere.
101 If it is only wanted as optional, only loading from disk
106 Check if any peer wants it as non-optional. In that case it
109 Also decrement the emerge queue count in clients.
112 bool optional = true;
115 core::map<u16, u8>::Iterator i;
116 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
118 //u16 peer_id = i.getNode()->getKey();
121 u8 flags = i.getNode()->getValue();
122 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
128 /*dstream<<"EmergeThread: p="
129 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
130 <<"optional="<<optional<<std::endl;*/
132 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
134 core::map<v3s16, MapBlock*> changed_blocks;
135 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
137 MapBlock *block = NULL;
138 bool got_block = true;
139 core::map<v3s16, MapBlock*> modified_blocks;
143 //TimeTaker envlockwaittimer("block emerge envlock wait time");
146 JMutexAutoLock envlock(m_server->m_env_mutex);
148 //envlockwaittimer.stop();
150 //TimeTaker timer("block emerge (while env locked)");
153 bool only_from_disk = false;
156 only_from_disk = true;
158 // First check if the block already exists
159 //block = map.getBlockNoCreate(p);
163 //dstream<<"Calling emergeBlock"<<std::endl;
164 block = map.emergeBlock(
168 lighting_invalidated_blocks);
172 EXPERIMENTAL: Create a few other blocks too
179 lighting_invalidated_blocks);
185 lighting_invalidated_blocks);
191 lighting_invalidated_blocks);
197 lighting_invalidated_blocks);
202 // If it is a dummy, block was not found on disk
205 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
208 if(only_from_disk == false)
210 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
215 catch(InvalidPositionException &e)
218 // This happens when position is over limit.
224 if(debug && changed_blocks.size() > 0)
226 dout_server<<DTIME<<"Got changed_blocks: ";
227 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
228 i.atEnd() == false; i++)
230 MapBlock *block = i.getNode()->getValue();
231 v3s16 p = block->getPos();
232 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
234 dout_server<<std::endl;
238 Collect a list of blocks that have been modified in
239 addition to the fetched one.
242 // Add all the "changed blocks" to modified_blocks
243 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
244 i.atEnd() == false; i++)
246 MapBlock *block = i.getNode()->getValue();
247 modified_blocks.insert(block->getPos(), block);
250 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
251 <<" blocks"<<std::endl;*/
253 //TimeTaker timer("** updateLighting");
255 // Update lighting without locking the environment mutex,
256 // add modified blocks to changed blocks
257 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
259 // If we got no block, there should be no invalidated blocks
262 assert(lighting_invalidated_blocks.size() == 0);
268 Set sent status of modified blocks on clients
271 // NOTE: Server's clients are also behind the connection mutex
272 JMutexAutoLock lock(m_server->m_con_mutex);
275 Add the originally fetched block to the modified list
279 modified_blocks.insert(p, block);
283 Set the modified blocks unsent for all the clients
286 for(core::map<u16, RemoteClient*>::Iterator
287 i = m_server->m_clients.getIterator();
288 i.atEnd() == false; i++)
290 RemoteClient *client = i.getNode()->getValue();
292 if(modified_blocks.size() > 0)
294 // Remove block from sent history
295 client->SetBlocksNotSent(modified_blocks);
301 END_DEBUG_EXCEPTION_HANDLER
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307 core::array<PrioritySortedBlockTransfer> &dest)
309 DSTACK(__FUNCTION_NAME);
313 JMutexAutoLock lock(m_blocks_sent_mutex);
314 m_nearest_unsent_reset_timer += dtime;
317 // Won't send anything if already sending
319 JMutexAutoLock lock(m_blocks_sending_mutex);
321 if(m_blocks_sending.size() >= g_settings.getU16
322 ("max_simultaneous_block_sends_per_client"))
324 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
329 Player *player = server->m_env.getPlayer(peer_id);
331 assert(player != NULL);
333 v3f playerpos = player->getPosition();
334 v3f playerspeed = player->getSpeed();
336 v3s16 center_nodepos = floatToInt(playerpos);
338 v3s16 center = getNodeBlockPos(center_nodepos);
340 // Camera position and direction
342 playerpos + v3f(0, BS+BS/2, 0);
343 v3f camera_dir = v3f(0,0,1);
344 camera_dir.rotateYZBy(player->getPitch());
345 camera_dir.rotateXZBy(player->getYaw());
348 Get the starting value of the block finder radius.
350 s16 last_nearest_unsent_d;
353 JMutexAutoLock lock(m_blocks_sent_mutex);
355 if(m_last_center != center)
357 m_nearest_unsent_d = 0;
358 m_last_center = center;
361 /*dstream<<"m_nearest_unsent_reset_timer="
362 <<m_nearest_unsent_reset_timer<<std::endl;*/
363 if(m_nearest_unsent_reset_timer > 5.0)
365 m_nearest_unsent_reset_timer = 0;
366 m_nearest_unsent_d = 0;
367 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
370 last_nearest_unsent_d = m_nearest_unsent_d;
372 d_start = m_nearest_unsent_d;
375 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
376 ("max_simultaneous_block_sends_per_client");
377 u16 maximum_simultaneous_block_sends =
378 maximum_simultaneous_block_sends_setting;
381 Check the time from last addNode/removeNode.
383 Decrease send rate if player is building stuff.
386 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
387 m_time_from_building.m_value += dtime;
388 /*if(m_time_from_building.m_value
389 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
390 if(m_time_from_building.m_value < g_settings.getFloat(
391 "full_block_send_enable_min_time_from_building"))
393 maximum_simultaneous_block_sends
394 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
398 u32 num_blocks_selected;
400 JMutexAutoLock lock(m_blocks_sending_mutex);
401 num_blocks_selected = m_blocks_sending.size();
405 next time d will be continued from the d from which the nearest
406 unsent block was found this time.
408 This is because not necessarily any of the blocks found this
409 time are actually sent.
411 s32 new_nearest_unsent_d = -1;
413 // Serialization version used
414 //u8 ser_version = serialization_version;
416 //bool has_incomplete_blocks = false;
418 s16 d_max = g_settings.getS16("max_block_send_distance");
419 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
421 //dstream<<"Starting from "<<d_start<<std::endl;
423 for(s16 d = d_start; d <= d_max; d++)
425 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
427 //if(has_incomplete_blocks == false)
429 JMutexAutoLock lock(m_blocks_sent_mutex);
431 If m_nearest_unsent_d was changed by the EmergeThread
432 (it can change it to 0 through SetBlockNotSent),
434 Else update m_nearest_unsent_d
436 if(m_nearest_unsent_d != last_nearest_unsent_d)
438 d = m_nearest_unsent_d;
439 last_nearest_unsent_d = m_nearest_unsent_d;
444 Get the border/face dot coordinates of a "d-radiused"
447 core::list<v3s16> list;
448 getFacePositions(list, d);
450 core::list<v3s16>::Iterator li;
451 for(li=list.begin(); li!=list.end(); li++)
453 v3s16 p = *li + center;
457 - Don't allow too many simultaneous transfers
458 - EXCEPT when the blocks are very close
460 Also, don't send blocks that are already flying.
463 u16 maximum_simultaneous_block_sends_now =
464 maximum_simultaneous_block_sends;
466 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
468 maximum_simultaneous_block_sends_now =
469 maximum_simultaneous_block_sends_setting;
473 JMutexAutoLock lock(m_blocks_sending_mutex);
475 // Limit is dynamically lowered when building
476 if(num_blocks_selected
477 >= maximum_simultaneous_block_sends_now)
479 /*dstream<<"Not sending more blocks. Queue full. "
480 <<m_blocks_sending.size()
485 if(m_blocks_sending.find(p) != NULL)
492 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
493 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
494 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
495 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
496 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
497 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
500 // If this is true, inexistent block will be made from scratch
501 bool generate = d <= d_max_gen;
504 /*// Limit the generating area vertically to 2/3
505 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
508 // Limit the send area vertically to 2/3
509 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
515 If block is far away, don't generate it unless it is
518 NOTE: We can't know the ground level this way with the
524 MapSector *sector = NULL;
527 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
529 catch(InvalidPositionException &e)
535 // Get center ground height in nodes
536 f32 gh = sector->getGroundHeight(
537 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
538 // Block center y in nodes
539 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
540 // If differs a lot, don't generate
541 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
548 Don't draw if not in sight
551 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
557 Don't send already sent blocks
560 JMutexAutoLock lock(m_blocks_sent_mutex);
562 if(m_blocks_sent.find(p) != NULL)
567 Check if map has this block
569 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
571 bool surely_not_found_on_disk = false;
572 bool block_is_invalid = false;
575 /*if(block->isIncomplete())
577 has_incomplete_blocks = true;
583 surely_not_found_on_disk = true;
586 if(block->isValid() == false)
588 block_is_invalid = true;
592 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
593 v2s16 chunkpos = map->sector_to_chunk(p2d);
594 if(map->chunkNonVolatile(chunkpos) == false)
595 block_is_invalid = true;
596 /*MapChunk *chunk = map->getChunk(chunkpos);
598 block_is_invalid = true;
599 else if(chunk->getIsVolatile() == true)
600 block_is_invalid = true;*/
604 If block has been marked to not exist on disk (dummy)
605 and generating new ones is not wanted, skip block.
607 if(generate == false && surely_not_found_on_disk == true)
614 Record the lowest d from which a a block has been
615 found being not sent and possibly to exist
617 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
619 new_nearest_unsent_d = d;
623 Add inexistent block to emerge queue.
625 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
627 //dstream<<"asd"<<std::endl;
629 /*SharedPtr<JMutexAutoLock> lock
630 (m_num_blocks_in_emerge_queue.getLock());*/
632 //TODO: Get value from somewhere
633 // Allow only one block in emerge queue
634 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
636 //dstream<<"Adding block to emerge queue"<<std::endl;
638 // Add it to the emerge queue and trigger the thread
641 if(generate == false)
642 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
644 server->m_emerge_queue.addBlock(peer_id, p, flags);
645 server->m_emergethread.trigger();
656 PrioritySortedBlockTransfer q((float)d, p, peer_id);
660 num_blocks_selected += 1;
665 if(new_nearest_unsent_d != -1)
667 JMutexAutoLock lock(m_blocks_sent_mutex);
668 m_nearest_unsent_d = new_nearest_unsent_d;
672 void RemoteClient::SendObjectData(
675 core::map<v3s16, bool> &stepped_blocks
678 DSTACK(__FUNCTION_NAME);
680 // Can't send anything without knowing version
681 if(serialization_version == SER_FMT_VER_INVALID)
683 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
689 Send a TOCLIENT_OBJECTDATA packet.
693 u16 number of player positions
704 std::ostringstream os(std::ios_base::binary);
708 writeU16(buf, TOCLIENT_OBJECTDATA);
709 os.write((char*)buf, 2);
712 Get and write player data
715 // Get connected players
716 core::list<Player*> players = server->m_env.getPlayers(true);
718 // Write player count
719 u16 playercount = players.size();
720 writeU16(buf, playercount);
721 os.write((char*)buf, 2);
723 core::list<Player*>::Iterator i;
724 for(i = players.begin();
725 i != players.end(); i++)
729 v3f pf = player->getPosition();
730 v3f sf = player->getSpeed();
732 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
733 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
734 s32 pitch_i (player->getPitch() * 100);
735 s32 yaw_i (player->getYaw() * 100);
737 writeU16(buf, player->peer_id);
738 os.write((char*)buf, 2);
739 writeV3S32(buf, position_i);
740 os.write((char*)buf, 12);
741 writeV3S32(buf, speed_i);
742 os.write((char*)buf, 12);
743 writeS32(buf, pitch_i);
744 os.write((char*)buf, 4);
745 writeS32(buf, yaw_i);
746 os.write((char*)buf, 4);
750 Get and write object data
756 For making players to be able to build to their nearby
757 environment (building is not possible on blocks that are not
760 - Add blocks to emerge queue if they are not found
762 SUGGESTION: These could be ignored from the backside of the player
765 Player *player = server->m_env.getPlayer(peer_id);
769 v3f playerpos = player->getPosition();
770 v3f playerspeed = player->getSpeed();
772 v3s16 center_nodepos = floatToInt(playerpos);
773 v3s16 center = getNodeBlockPos(center_nodepos);
775 s16 d_max = g_settings.getS16("active_object_range");
777 // Number of blocks whose objects were written to bos
780 std::ostringstream bos(std::ios_base::binary);
782 for(s16 d = 0; d <= d_max; d++)
784 core::list<v3s16> list;
785 getFacePositions(list, d);
787 core::list<v3s16>::Iterator li;
788 for(li=list.begin(); li!=list.end(); li++)
790 v3s16 p = *li + center;
793 Ignore blocks that haven't been sent to the client
796 JMutexAutoLock sentlock(m_blocks_sent_mutex);
797 if(m_blocks_sent.find(p) == NULL)
801 // Try stepping block and add it to a send queue
806 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
809 Step block if not in stepped_blocks and add to stepped_blocks.
811 if(stepped_blocks.find(p) == NULL)
813 block->stepObjects(dtime, true, server->getDayNightRatio());
814 stepped_blocks.insert(p, true);
815 block->setChangedFlag();
818 // Skip block if there are no objects
819 if(block->getObjectCount() == 0)
828 bos.write((char*)buf, 6);
831 block->serializeObjects(bos, serialization_version);
836 Stop collecting objects if data is already too big
838 // Sum of player and object data sizes
839 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
840 // break out if data too big
841 if(sum > MAX_OBJECTDATA_SIZE)
843 goto skip_subsequent;
847 catch(InvalidPositionException &e)
850 // Add it to the emerge queue and trigger the thread.
851 // Fetch the block only if it is on disk.
853 // Grab and increment counter
854 /*SharedPtr<JMutexAutoLock> lock
855 (m_num_blocks_in_emerge_queue.getLock());
856 m_num_blocks_in_emerge_queue.m_value++;*/
858 // Add to queue as an anonymous fetch from disk
859 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
860 server->m_emerge_queue.addBlock(0, p, flags);
861 server->m_emergethread.trigger();
869 writeU16(buf, blockcount);
870 os.write((char*)buf, 2);
872 // Write block objects
879 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
882 std::string s = os.str();
883 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
884 // Send as unreliable
885 server->m_con.Send(peer_id, 0, data, false);
888 void RemoteClient::GotBlock(v3s16 p)
890 JMutexAutoLock lock(m_blocks_sending_mutex);
891 JMutexAutoLock lock2(m_blocks_sent_mutex);
892 if(m_blocks_sending.find(p) != NULL)
893 m_blocks_sending.remove(p);
896 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
897 " m_blocks_sending"<<std::endl;*/
898 m_excess_gotblocks++;
900 m_blocks_sent.insert(p, true);
903 void RemoteClient::SentBlock(v3s16 p)
905 JMutexAutoLock lock(m_blocks_sending_mutex);
906 /*if(m_blocks_sending.size() > 15)
908 dstream<<"RemoteClient::SentBlock(): "
909 <<"m_blocks_sending.size()="
910 <<m_blocks_sending.size()<<std::endl;
912 if(m_blocks_sending.find(p) == NULL)
913 m_blocks_sending.insert(p, 0.0);
915 dstream<<"RemoteClient::SentBlock(): Sent block"
916 " already in m_blocks_sending"<<std::endl;
919 void RemoteClient::SetBlockNotSent(v3s16 p)
921 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
922 JMutexAutoLock sentlock(m_blocks_sent_mutex);
924 m_nearest_unsent_d = 0;
926 if(m_blocks_sending.find(p) != NULL)
927 m_blocks_sending.remove(p);
928 if(m_blocks_sent.find(p) != NULL)
929 m_blocks_sent.remove(p);
932 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
934 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
935 JMutexAutoLock sentlock(m_blocks_sent_mutex);
937 m_nearest_unsent_d = 0;
939 for(core::map<v3s16, MapBlock*>::Iterator
940 i = blocks.getIterator();
941 i.atEnd()==false; i++)
943 v3s16 p = i.getNode()->getKey();
945 if(m_blocks_sending.find(p) != NULL)
946 m_blocks_sending.remove(p);
947 if(m_blocks_sent.find(p) != NULL)
948 m_blocks_sent.remove(p);
956 PlayerInfo::PlayerInfo()
961 void PlayerInfo::PrintLine(std::ostream *s)
964 (*s)<<"\""<<name<<"\" ("
965 <<(position.X/10)<<","<<(position.Y/10)
966 <<","<<(position.Z/10)<<") ";
968 (*s)<<" avg_rtt="<<avg_rtt;
972 u32 PIChecksum(core::list<PlayerInfo> &l)
974 core::list<PlayerInfo>::Iterator i;
977 for(i=l.begin(); i!=l.end(); i++)
979 checksum += a * (i->id+1);
980 checksum ^= 0x435aafcd;
991 std::string mapsavedir
993 m_env(new ServerMap(mapsavedir), dout_server),
994 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
996 m_emergethread(this),
999 m_time_of_day_send_timer(0),
1001 m_mapsavedir(mapsavedir)
1003 //m_flowwater_timer = 0.0;
1004 m_liquid_transform_timer = 0.0;
1005 m_print_info_timer = 0.0;
1006 m_objectdata_timer = 0.0;
1007 m_emergethread_trigger_timer = 0.0;
1008 m_savemap_timer = 0.0;
1012 m_step_dtime_mutex.Init();
1016 m_env.deSerializePlayers(m_mapsavedir);
1022 m_env.serializePlayers(m_mapsavedir);
1027 JMutexAutoLock clientslock(m_con_mutex);
1029 for(core::map<u16, RemoteClient*>::Iterator
1030 i = m_clients.getIterator();
1031 i.atEnd() == false; i++)
1034 // NOTE: These are removed by env destructor
1036 u16 peer_id = i.getNode()->getKey();
1037 JMutexAutoLock envlock(m_env_mutex);
1038 m_env.removePlayer(peer_id);
1042 delete i.getNode()->getValue();
1046 void Server::start(unsigned short port)
1048 DSTACK(__FUNCTION_NAME);
1049 // Stop thread if already running
1052 // Initialize connection
1053 m_con.setTimeoutMs(30);
1057 m_thread.setRun(true);
1060 dout_server<<"Server started on port "<<port<<std::endl;
1065 DSTACK(__FUNCTION_NAME);
1066 // Stop threads (set run=false first so both start stopping)
1067 m_thread.setRun(false);
1068 m_emergethread.setRun(false);
1070 m_emergethread.stop();
1072 dout_server<<"Server threads stopped"<<std::endl;
1075 void Server::step(float dtime)
1077 DSTACK(__FUNCTION_NAME);
1082 JMutexAutoLock lock(m_step_dtime_mutex);
1083 m_step_dtime += dtime;
1087 void Server::AsyncRunStep()
1089 DSTACK(__FUNCTION_NAME);
1093 JMutexAutoLock lock1(m_step_dtime_mutex);
1094 dtime = m_step_dtime;
1097 // Send blocks to clients
1103 //dstream<<"Server steps "<<dtime<<std::endl;
1104 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1107 JMutexAutoLock lock1(m_step_dtime_mutex);
1108 m_step_dtime -= dtime;
1115 m_uptime.set(m_uptime.get() + dtime);
1119 Update m_time_of_day
1122 m_time_counter += dtime;
1123 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1124 u32 units = (u32)(m_time_counter*speed);
1125 m_time_counter -= (f32)units / speed;
1126 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1128 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1131 Send to clients at constant intervals
1134 m_time_of_day_send_timer -= dtime;
1135 if(m_time_of_day_send_timer < 0.0)
1137 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1139 //JMutexAutoLock envlock(m_env_mutex);
1140 JMutexAutoLock conlock(m_con_mutex);
1142 for(core::map<u16, RemoteClient*>::Iterator
1143 i = m_clients.getIterator();
1144 i.atEnd() == false; i++)
1146 RemoteClient *client = i.getNode()->getValue();
1147 //Player *player = m_env.getPlayer(client->peer_id);
1149 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1150 m_time_of_day.get());
1152 m_con.Send(client->peer_id, 0, data, true);
1158 // Process connection's timeouts
1159 JMutexAutoLock lock2(m_con_mutex);
1160 m_con.RunTimeouts(dtime);
1164 // This has to be called so that the client list gets synced
1165 // with the peer list of the connection
1166 handlePeerChanges();
1171 // This also runs Map's timers
1172 JMutexAutoLock lock(m_env_mutex);
1183 m_liquid_transform_timer += dtime;
1184 if(m_liquid_transform_timer >= 1.00)
1186 m_liquid_transform_timer -= 1.00;
1188 JMutexAutoLock lock(m_env_mutex);
1190 core::map<v3s16, MapBlock*> modified_blocks;
1191 m_env.getMap().transformLiquids(modified_blocks);
1196 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1197 ServerMap &map = ((ServerMap&)m_env.getMap());
1198 map.updateLighting(modified_blocks, lighting_modified_blocks);
1200 // Add blocks modified by lighting to modified_blocks
1201 for(core::map<v3s16, MapBlock*>::Iterator
1202 i = lighting_modified_blocks.getIterator();
1203 i.atEnd() == false; i++)
1205 MapBlock *block = i.getNode()->getValue();
1206 modified_blocks.insert(block->getPos(), block);
1210 Set the modified blocks unsent for all the clients
1213 JMutexAutoLock lock2(m_con_mutex);
1215 for(core::map<u16, RemoteClient*>::Iterator
1216 i = m_clients.getIterator();
1217 i.atEnd() == false; i++)
1219 RemoteClient *client = i.getNode()->getValue();
1221 if(modified_blocks.size() > 0)
1223 // Remove block from sent history
1224 client->SetBlocksNotSent(modified_blocks);
1229 // Periodically print some info
1231 float &counter = m_print_info_timer;
1237 JMutexAutoLock lock2(m_con_mutex);
1239 for(core::map<u16, RemoteClient*>::Iterator
1240 i = m_clients.getIterator();
1241 i.atEnd() == false; i++)
1243 //u16 peer_id = i.getNode()->getKey();
1244 RemoteClient *client = i.getNode()->getValue();
1245 client->PrintInfo(std::cout);
1253 NOTE: Some of this could be moved to RemoteClient
1257 JMutexAutoLock envlock(m_env_mutex);
1258 JMutexAutoLock conlock(m_con_mutex);
1260 for(core::map<u16, RemoteClient*>::Iterator
1261 i = m_clients.getIterator();
1262 i.atEnd() == false; i++)
1264 RemoteClient *client = i.getNode()->getValue();
1265 Player *player = m_env.getPlayer(client->peer_id);
1267 JMutexAutoLock digmutex(client->m_dig_mutex);
1269 if(client->m_dig_tool_item == -1)
1272 client->m_dig_time_remaining -= dtime;
1274 if(client->m_dig_time_remaining > 0)
1276 client->m_time_from_building.set(0.0);
1280 v3s16 p_under = client->m_dig_position;
1282 // Mandatory parameter; actually used for nothing
1283 core::map<v3s16, MapBlock*> modified_blocks;
1289 // Get material at position
1290 material = m_env.getMap().getNode(p_under).d;
1291 // If it's not diggable, do nothing
1292 if(content_diggable(material) == false)
1294 derr_server<<"Server: Not finishing digging: Node not diggable"
1296 client->m_dig_tool_item = -1;
1300 catch(InvalidPositionException &e)
1302 derr_server<<"Server: Not finishing digging: Node not found"
1304 client->m_dig_tool_item = -1;
1310 SharedBuffer<u8> reply(replysize);
1311 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1312 writeS16(&reply[2], p_under.X);
1313 writeS16(&reply[4], p_under.Y);
1314 writeS16(&reply[6], p_under.Z);
1316 m_con.SendToAll(0, reply, true);
1318 if(g_settings.getBool("creative_mode") == false)
1320 // Add to inventory and send inventory
1321 InventoryItem *item = new MaterialItem(material, 1);
1322 player->inventory.addItem("main", item);
1323 SendInventory(player->peer_id);
1328 (this takes some time so it is done after the quick stuff)
1330 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1336 // Update water pressure around modification
1337 // This also adds it to m_flow_active_nodes if appropriate
1339 MapVoxelManipulator v(&m_env.getMap());
1340 v.m_disable_water_climb =
1341 g_settings.getBool("disable_water_climb");
1343 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1347 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1349 catch(ProcessingLimitException &e)
1351 dstream<<"Processing limit reached (1)"<<std::endl;
1354 v.blitBack(modified_blocks);
1359 // Send object positions
1361 float &counter = m_objectdata_timer;
1363 if(counter >= g_settings.getFloat("objectdata_interval"))
1365 JMutexAutoLock lock1(m_env_mutex);
1366 JMutexAutoLock lock2(m_con_mutex);
1367 SendObjectData(counter);
1374 Trigger emergethread (it somehow gets to a non-triggered but
1375 bysy state sometimes)
1378 float &counter = m_emergethread_trigger_timer;
1384 m_emergethread.trigger();
1390 float &counter = m_savemap_timer;
1392 if(counter >= g_settings.getFloat("server_map_save_interval"))
1396 JMutexAutoLock lock(m_env_mutex);
1398 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1400 // Save only changed parts
1401 m_env.getMap().save(true);
1403 // Delete unused sectors
1404 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1405 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1406 if(deleted_count > 0)
1408 dout_server<<"Server: Unloaded "<<deleted_count
1409 <<" sectors from memory"<<std::endl;
1413 m_env.serializePlayers(m_mapsavedir);
1419 void Server::Receive()
1421 DSTACK(__FUNCTION_NAME);
1422 u32 data_maxsize = 10000;
1423 Buffer<u8> data(data_maxsize);
1428 JMutexAutoLock conlock(m_con_mutex);
1429 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1432 // This has to be called so that the client list gets synced
1433 // with the peer list of the connection
1434 handlePeerChanges();
1436 ProcessData(*data, datasize, peer_id);
1438 catch(con::InvalidIncomingDataException &e)
1440 derr_server<<"Server::Receive(): "
1441 "InvalidIncomingDataException: what()="
1442 <<e.what()<<std::endl;
1444 catch(con::PeerNotFoundException &e)
1446 //NOTE: This is not needed anymore
1448 // The peer has been disconnected.
1449 // Find the associated player and remove it.
1451 /*JMutexAutoLock envlock(m_env_mutex);
1453 dout_server<<"ServerThread: peer_id="<<peer_id
1454 <<" has apparently closed connection. "
1455 <<"Removing player."<<std::endl;
1457 m_env.removePlayer(peer_id);*/
1461 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1463 DSTACK(__FUNCTION_NAME);
1464 // Environment is locked first.
1465 JMutexAutoLock envlock(m_env_mutex);
1466 JMutexAutoLock conlock(m_con_mutex);
1470 peer = m_con.GetPeer(peer_id);
1472 catch(con::PeerNotFoundException &e)
1474 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1475 <<peer_id<<" not found"<<std::endl;
1479 //u8 peer_ser_ver = peer->serialization_version;
1480 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1488 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1490 if(command == TOSERVER_INIT)
1492 // [0] u16 TOSERVER_INIT
1493 // [2] u8 SER_FMT_VER_HIGHEST
1494 // [3] u8[20] player_name
1499 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1500 <<peer->id<<std::endl;
1502 // First byte after command is maximum supported
1503 // serialization version
1504 u8 client_max = data[2];
1505 u8 our_max = SER_FMT_VER_HIGHEST;
1506 // Use the highest version supported by both
1507 u8 deployed = core::min_(client_max, our_max);
1508 // If it's lower than the lowest supported, give up.
1509 if(deployed < SER_FMT_VER_LOWEST)
1510 deployed = SER_FMT_VER_INVALID;
1512 //peer->serialization_version = deployed;
1513 getClient(peer->id)->pending_serialization_version = deployed;
1515 if(deployed == SER_FMT_VER_INVALID)
1517 derr_server<<DTIME<<"Server: Cannot negotiate "
1518 "serialization version with peer "
1519 <<peer_id<<std::endl;
1528 const u32 playername_size = 20;
1529 char playername[playername_size];
1530 for(u32 i=0; i<playername_size-1; i++)
1532 playername[i] = data[3+i];
1534 playername[playername_size-1] = 0;
1537 Player *player = emergePlayer(playername, "", peer_id);
1538 //Player *player = m_env.getPlayer(peer_id);
1541 // DEBUG: Test serialization
1542 std::ostringstream test_os;
1543 player->serialize(test_os);
1544 dstream<<"Player serialization test: \""<<test_os.str()
1546 std::istringstream test_is(test_os.str());
1547 player->deSerialize(test_is);
1550 // If failed, cancel
1553 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1554 <<": failed to emerge player"<<std::endl;
1559 // If a client is already connected to the player, cancel
1560 if(player->peer_id != 0)
1562 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1563 <<" tried to connect to "
1564 "an already connected player (peer_id="
1565 <<player->peer_id<<")"<<std::endl;
1568 // Set client of player
1569 player->peer_id = peer_id;
1572 // Check if player doesn't exist
1574 throw con::InvalidIncomingDataException
1575 ("Server::ProcessData(): INIT: Player doesn't exist");
1577 /*// update name if it was supplied
1578 if(datasize >= 20+3)
1581 player->updateName((const char*)&data[3]);
1584 // Now answer with a TOCLIENT_INIT
1586 SharedBuffer<u8> reply(2+1+6);
1587 writeU16(&reply[0], TOCLIENT_INIT);
1588 writeU8(&reply[2], deployed);
1589 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1591 m_con.Send(peer_id, 0, reply, true);
1595 if(command == TOSERVER_INIT2)
1597 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1598 <<peer->id<<std::endl;
1601 getClient(peer->id)->serialization_version
1602 = getClient(peer->id)->pending_serialization_version;
1605 Send some initialization data
1608 // Send player info to all players
1611 // Send inventory to player
1612 SendInventory(peer->id);
1616 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1617 m_time_of_day.get());
1618 m_con.Send(peer->id, 0, data, true);
1621 // Send information about server to player in chat
1623 std::wostringstream os(std::ios_base::binary);
1626 os<<L"uptime="<<m_uptime.get();
1627 // Information about clients
1629 for(core::map<u16, RemoteClient*>::Iterator
1630 i = m_clients.getIterator();
1631 i.atEnd() == false; i++)
1633 // Get client and check that it is valid
1634 RemoteClient *client = i.getNode()->getValue();
1635 assert(client->peer_id == i.getNode()->getKey());
1636 if(client->serialization_version == SER_FMT_VER_INVALID)
1639 Player *player = m_env.getPlayer(client->peer_id);
1640 // Get name of player
1641 std::wstring name = L"unknown";
1643 name = narrow_to_wide(player->getName());
1644 // Add name to information string
1648 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
1649 os<<" WARNING: Map saving is disabled."<<std::endl;
1651 SendChatMessage(peer_id, os.str());
1654 // Send information about joining in chat
1656 std::wstring name = L"unknown";
1657 Player *player = m_env.getPlayer(peer_id);
1659 name = narrow_to_wide(player->getName());
1661 std::wstring message;
1664 message += L" joined game";
1665 BroadcastChatMessage(message);
1671 if(peer_ser_ver == SER_FMT_VER_INVALID)
1673 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1674 " serialization format invalid or not initialized."
1675 " Skipping incoming command="<<command<<std::endl;
1679 Player *player = m_env.getPlayer(peer_id);
1682 derr_server<<"Server::ProcessData(): Cancelling: "
1683 "No player for peer_id="<<peer_id
1687 if(command == TOSERVER_PLAYERPOS)
1689 if(datasize < 2+12+12+4+4)
1693 v3s32 ps = readV3S32(&data[start+2]);
1694 v3s32 ss = readV3S32(&data[start+2+12]);
1695 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1696 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1697 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1698 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1699 pitch = wrapDegrees(pitch);
1700 yaw = wrapDegrees(yaw);
1701 player->setPosition(position);
1702 player->setSpeed(speed);
1703 player->setPitch(pitch);
1704 player->setYaw(yaw);
1706 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1707 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1708 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1710 else if(command == TOSERVER_GOTBLOCKS)
1723 u16 count = data[2];
1724 for(u16 i=0; i<count; i++)
1726 if((s16)datasize < 2+1+(i+1)*6)
1727 throw con::InvalidIncomingDataException
1728 ("GOTBLOCKS length is too short");
1729 v3s16 p = readV3S16(&data[2+1+i*6]);
1730 /*dstream<<"Server: GOTBLOCKS ("
1731 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1732 RemoteClient *client = getClient(peer_id);
1733 client->GotBlock(p);
1736 else if(command == TOSERVER_DELETEDBLOCKS)
1749 u16 count = data[2];
1750 for(u16 i=0; i<count; i++)
1752 if((s16)datasize < 2+1+(i+1)*6)
1753 throw con::InvalidIncomingDataException
1754 ("DELETEDBLOCKS length is too short");
1755 v3s16 p = readV3S16(&data[2+1+i*6]);
1756 /*dstream<<"Server: DELETEDBLOCKS ("
1757 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1758 RemoteClient *client = getClient(peer_id);
1759 client->SetBlockNotSent(p);
1762 else if(command == TOSERVER_CLICK_OBJECT)
1769 [2] u8 button (0=left, 1=right)
1774 u8 button = readU8(&data[2]);
1776 p.X = readS16(&data[3]);
1777 p.Y = readS16(&data[5]);
1778 p.Z = readS16(&data[7]);
1779 s16 id = readS16(&data[9]);
1780 //u16 item_i = readU16(&data[11]);
1782 MapBlock *block = NULL;
1785 block = m_env.getMap().getBlockNoCreate(p);
1787 catch(InvalidPositionException &e)
1789 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1793 MapBlockObject *obj = block->getObject(id);
1797 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1801 //TODO: Check that object is reasonably close
1806 InventoryList *ilist = player->inventory.getList("main");
1807 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1810 // Skip if inventory has no free space
1811 if(ilist->getUsedSlots() == ilist->getSize())
1813 dout_server<<"Player inventory has no free space"<<std::endl;
1818 Create the inventory item
1820 InventoryItem *item = NULL;
1821 // If it is an item-object, take the item from it
1822 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1824 item = ((ItemObject*)obj)->createInventoryItem();
1826 // Else create an item of the object
1829 item = new MapBlockObjectItem
1830 (obj->getInventoryString());
1833 // Add to inventory and send inventory
1834 ilist->addItem(item);
1835 SendInventory(player->peer_id);
1838 // Remove from block
1839 block->removeObject(id);
1842 else if(command == TOSERVER_GROUND_ACTION)
1850 [3] v3s16 nodepos_undersurface
1851 [9] v3s16 nodepos_abovesurface
1856 2: stop digging (all parameters ignored)
1857 3: digging completed
1859 u8 action = readU8(&data[2]);
1861 p_under.X = readS16(&data[3]);
1862 p_under.Y = readS16(&data[5]);
1863 p_under.Z = readS16(&data[7]);
1865 p_over.X = readS16(&data[9]);
1866 p_over.Y = readS16(&data[11]);
1867 p_over.Z = readS16(&data[13]);
1868 u16 item_i = readU16(&data[15]);
1870 //TODO: Check that target is reasonably close
1878 NOTE: This can be used in the future to check if
1879 somebody is cheating, by checking the timing.
1886 else if(action == 2)
1889 RemoteClient *client = getClient(peer->id);
1890 JMutexAutoLock digmutex(client->m_dig_mutex);
1891 client->m_dig_tool_item = -1;
1896 3: Digging completed
1898 else if(action == 3)
1900 // Mandatory parameter; actually used for nothing
1901 core::map<v3s16, MapBlock*> modified_blocks;
1904 u8 mineral = MINERAL_NONE;
1908 MapNode n = m_env.getMap().getNode(p_under);
1909 // Get material at position
1911 // If it's not diggable, do nothing
1912 if(content_diggable(material) == false)
1914 derr_server<<"Server: Not finishing digging: Node not diggable"
1919 mineral = n.getMineral();
1921 catch(InvalidPositionException &e)
1923 derr_server<<"Server: Not finishing digging: Node not found."
1924 <<" Adding block to emerge queue."
1926 m_emerge_queue.addBlock(peer_id,
1927 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1932 Send the removal to all other clients
1937 SharedBuffer<u8> reply(replysize);
1938 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1939 writeS16(&reply[2], p_under.X);
1940 writeS16(&reply[4], p_under.Y);
1941 writeS16(&reply[6], p_under.Z);
1943 for(core::map<u16, RemoteClient*>::Iterator
1944 i = m_clients.getIterator();
1945 i.atEnd() == false; i++)
1947 // Get client and check that it is valid
1948 RemoteClient *client = i.getNode()->getValue();
1949 assert(client->peer_id == i.getNode()->getKey());
1950 if(client->serialization_version == SER_FMT_VER_INVALID)
1953 // Don't send if it's the same one
1954 if(peer_id == client->peer_id)
1958 m_con.Send(client->peer_id, 0, reply, true);
1962 Update and send inventory
1965 if(g_settings.getBool("creative_mode") == false)
1970 InventoryList *mlist = player->inventory.getList("main");
1973 InventoryItem *item = mlist->getItem(item_i);
1974 if(item && (std::string)item->getName() == "ToolItem")
1976 ToolItem *titem = (ToolItem*)item;
1977 std::string toolname = titem->getToolName();
1979 // Get digging properties for material and tool
1980 DiggingProperties prop =
1981 getDiggingProperties(material, toolname);
1983 if(prop.diggable == false)
1985 derr_server<<"Server: WARNING: Player digged"
1986 <<" with impossible material + tool"
1987 <<" combination"<<std::endl;
1990 bool weared_out = titem->addWear(prop.wear);
1994 mlist->deleteItem(item_i);
2000 Add dug item to inventory
2003 InventoryItem *item = NULL;
2005 if(mineral != MINERAL_NONE)
2006 item = getDiggedMineralItem(mineral);
2011 std::string &dug_s = content_features(material).dug_item;
2014 std::istringstream is(dug_s, std::ios::binary);
2015 item = InventoryItem::deSerialize(is);
2021 // Add a item to inventory
2022 player->inventory.addItem("main", item);
2025 SendInventory(player->peer_id);
2031 (this takes some time so it is done after the quick stuff)
2033 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2040 // Update water pressure around modification
2041 // This also adds it to m_flow_active_nodes if appropriate
2043 MapVoxelManipulator v(&m_env.getMap());
2044 v.m_disable_water_climb =
2045 g_settings.getBool("disable_water_climb");
2047 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2051 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2053 catch(ProcessingLimitException &e)
2055 dstream<<"Processing limit reached (1)"<<std::endl;
2058 v.blitBack(modified_blocks);
2065 else if(action == 1)
2068 InventoryList *ilist = player->inventory.getList("main");
2073 InventoryItem *item = ilist->getItem(item_i);
2075 // If there is no item, it is not possible to add it anywhere
2080 Handle material items
2082 if(std::string("MaterialItem") == item->getName())
2085 // Don't add a node if this is not a free space
2086 MapNode n2 = m_env.getMap().getNode(p_over);
2087 if(content_buildable_to(n2.d) == false)
2089 // Client probably has wrong data.
2090 // Set block not sent, so that client will get
2092 dstream<<"Client "<<peer_id<<" tried to place"
2093 <<" node in invalid position; setting"
2094 <<" MapBlock not sent."<<std::endl;
2095 RemoteClient *client = getClient(peer_id);
2096 v3s16 blockpos = getNodeBlockPos(p_over);
2097 client->SetBlockNotSent(blockpos);
2101 catch(InvalidPositionException &e)
2103 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2104 <<" Adding block to emerge queue."
2106 m_emerge_queue.addBlock(peer_id,
2107 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2111 // Reset build time counter
2112 getClient(peer->id)->m_time_from_building.set(0.0);
2115 MaterialItem *mitem = (MaterialItem*)item;
2117 n.d = mitem->getMaterial();
2118 if(content_features(n.d).wall_mounted)
2119 n.dir = packDir(p_under - p_over);
2123 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2124 SharedBuffer<u8> reply(replysize);
2125 writeU16(&reply[0], TOCLIENT_ADDNODE);
2126 writeS16(&reply[2], p_over.X);
2127 writeS16(&reply[4], p_over.Y);
2128 writeS16(&reply[6], p_over.Z);
2129 n.serialize(&reply[8], peer_ser_ver);
2131 m_con.SendToAll(0, reply, true);
2136 InventoryList *ilist = player->inventory.getList("main");
2137 if(g_settings.getBool("creative_mode") == false && ilist)
2139 // Remove from inventory and send inventory
2140 if(mitem->getCount() == 1)
2141 ilist->deleteItem(item_i);
2145 SendInventory(peer_id);
2151 This takes some time so it is done after the quick stuff
2153 core::map<v3s16, MapBlock*> modified_blocks;
2154 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2160 InventoryList *ilist = player->inventory.getList("main");
2161 if(g_settings.getBool("creative_mode") == false && ilist)
2163 // Remove from inventory and send inventory
2164 if(mitem->getCount() == 1)
2165 ilist->deleteItem(item_i);
2169 SendInventory(peer_id);
2175 This takes some time so it is done after the quick stuff
2177 core::map<v3s16, MapBlock*> modified_blocks;
2178 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2181 Set the modified blocks unsent for all the clients
2184 //JMutexAutoLock lock2(m_con_mutex);
2186 for(core::map<u16, RemoteClient*>::Iterator
2187 i = m_clients.getIterator();
2188 i.atEnd() == false; i++)
2190 RemoteClient *client = i.getNode()->getValue();
2192 if(modified_blocks.size() > 0)
2194 // Remove block from sent history
2195 client->SetBlocksNotSent(modified_blocks);
2205 // Update water pressure around modification
2206 // This also adds it to m_flow_active_nodes if appropriate
2208 MapVoxelManipulator v(&m_env.getMap());
2209 v.m_disable_water_climb =
2210 g_settings.getBool("disable_water_climb");
2212 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2216 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2218 catch(ProcessingLimitException &e)
2220 dstream<<"Processing limit reached (1)"<<std::endl;
2223 v.blitBack(modified_blocks);
2231 v3s16 blockpos = getNodeBlockPos(p_over);
2233 MapBlock *block = NULL;
2236 block = m_env.getMap().getBlockNoCreate(blockpos);
2238 catch(InvalidPositionException &e)
2240 derr_server<<"Error while placing object: "
2241 "block not found"<<std::endl;
2245 v3s16 block_pos_i_on_map = block->getPosRelative();
2246 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2248 v3f pos = intToFloat(p_over);
2249 pos -= block_pos_f_on_map;
2251 /*dout_server<<"pos="
2252 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2255 MapBlockObject *obj = NULL;
2258 Handle block object items
2260 if(std::string("MBOItem") == item->getName())
2262 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2264 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2265 "inventorystring=\""
2266 <<oitem->getInventoryString()
2267 <<"\""<<std::endl;*/
2269 obj = oitem->createObject
2270 (pos, player->getYaw(), player->getPitch());
2277 dout_server<<"Placing a miscellaneous item on map"
2280 Create an ItemObject that contains the item.
2282 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2283 std::ostringstream os(std::ios_base::binary);
2284 item->serialize(os);
2285 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2286 iobj->setItemString(os.str());
2292 derr_server<<"WARNING: item resulted in NULL object, "
2293 <<"not placing onto map"
2298 block->addObject(obj);
2300 dout_server<<"Placed object"<<std::endl;
2302 InventoryList *ilist = player->inventory.getList("main");
2303 if(g_settings.getBool("creative_mode") == false && ilist)
2305 // Remove from inventory and send inventory
2306 ilist->deleteItem(item_i);
2308 SendInventory(peer_id);
2316 Catch invalid actions
2320 derr_server<<"WARNING: Server: Invalid action "
2321 <<action<<std::endl;
2325 else if(command == TOSERVER_RELEASE)
2334 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2337 else if(command == TOSERVER_SIGNTEXT)
2346 std::string datastring((char*)&data[2], datasize-2);
2347 std::istringstream is(datastring, std::ios_base::binary);
2350 is.read((char*)buf, 6);
2351 v3s16 blockpos = readV3S16(buf);
2352 is.read((char*)buf, 2);
2353 s16 id = readS16(buf);
2354 is.read((char*)buf, 2);
2355 u16 textlen = readU16(buf);
2357 for(u16 i=0; i<textlen; i++)
2359 is.read((char*)buf, 1);
2360 text += (char)buf[0];
2363 MapBlock *block = NULL;
2366 block = m_env.getMap().getBlockNoCreate(blockpos);
2368 catch(InvalidPositionException &e)
2370 derr_server<<"Error while setting sign text: "
2371 "block not found"<<std::endl;
2375 MapBlockObject *obj = block->getObject(id);
2378 derr_server<<"Error while setting sign text: "
2379 "object not found"<<std::endl;
2383 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2385 derr_server<<"Error while setting sign text: "
2386 "object is not a sign"<<std::endl;
2390 ((SignObject*)obj)->setText(text);
2392 obj->getBlock()->setChangedFlag();
2394 else if(command == TOSERVER_INVENTORY_ACTION)
2396 /*// Ignore inventory changes if in creative mode
2397 if(g_settings.getBool("creative_mode") == true)
2399 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2403 // Strip command and create a stream
2404 std::string datastring((char*)&data[2], datasize-2);
2405 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2406 std::istringstream is(datastring, std::ios_base::binary);
2408 InventoryAction *a = InventoryAction::deSerialize(is);
2412 Handle craftresult specially if not in creative mode
2414 bool disable_action = false;
2415 if(a->getType() == IACTION_MOVE
2416 && g_settings.getBool("creative_mode") == false)
2418 IMoveAction *ma = (IMoveAction*)a;
2419 // Don't allow moving anything to craftresult
2420 if(ma->to_name == "craftresult")
2423 disable_action = true;
2425 // When something is removed from craftresult
2426 if(ma->from_name == "craftresult")
2428 disable_action = true;
2429 // Remove stuff from craft
2430 InventoryList *clist = player->inventory.getList("craft");
2433 u16 count = ma->count;
2436 clist->decrementMaterials(count);
2439 // Feed action to player inventory
2440 a->apply(&player->inventory);
2443 // If something appeared in craftresult, throw it
2445 InventoryList *rlist = player->inventory.getList("craftresult");
2446 InventoryList *mlist = player->inventory.getList("main");
2447 if(rlist && mlist && rlist->getUsedSlots() == 1)
2449 InventoryItem *item1 = rlist->changeItem(0, NULL);
2450 mlist->addItem(item1);
2454 if(disable_action == false)
2456 // Feed action to player inventory
2457 a->apply(&player->inventory);
2462 SendInventory(player->peer_id);
2466 dstream<<"TOSERVER_INVENTORY_ACTION: "
2467 <<"InventoryAction::deSerialize() returned NULL"
2471 else if(command == TOSERVER_CHAT_MESSAGE)
2479 std::string datastring((char*)&data[2], datasize-2);
2480 std::istringstream is(datastring, std::ios_base::binary);
2483 is.read((char*)buf, 2);
2484 u16 len = readU16(buf);
2486 std::wstring message;
2487 for(u16 i=0; i<len; i++)
2489 is.read((char*)buf, 2);
2490 message += (wchar_t)readU16(buf);
2493 // Get player name of this client
2494 std::wstring name = narrow_to_wide(player->getName());
2496 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2498 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2501 Send the message to all other clients
2503 for(core::map<u16, RemoteClient*>::Iterator
2504 i = m_clients.getIterator();
2505 i.atEnd() == false; i++)
2507 // Get client and check that it is valid
2508 RemoteClient *client = i.getNode()->getValue();
2509 assert(client->peer_id == i.getNode()->getKey());
2510 if(client->serialization_version == SER_FMT_VER_INVALID)
2513 // Don't send if it's the same one
2514 if(peer_id == client->peer_id)
2517 SendChatMessage(client->peer_id, line);
2522 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2523 "unknown command "<<command<<std::endl;
2527 catch(SendFailedException &e)
2529 derr_server<<"Server::ProcessData(): SendFailedException: "
2535 /*void Server::Send(u16 peer_id, u16 channelnum,
2536 SharedBuffer<u8> data, bool reliable)
2538 JMutexAutoLock lock(m_con_mutex);
2539 m_con.Send(peer_id, channelnum, data, reliable);
2542 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2544 DSTACK(__FUNCTION_NAME);
2546 Create a packet with the block in the right format
2549 std::ostringstream os(std::ios_base::binary);
2550 block->serialize(os, ver);
2551 std::string s = os.str();
2552 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2554 u32 replysize = 8 + blockdata.getSize();
2555 SharedBuffer<u8> reply(replysize);
2556 v3s16 p = block->getPos();
2557 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2558 writeS16(&reply[2], p.X);
2559 writeS16(&reply[4], p.Y);
2560 writeS16(&reply[6], p.Z);
2561 memcpy(&reply[8], *blockdata, blockdata.getSize());
2563 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2564 <<": \tpacket size: "<<replysize<<std::endl;*/
2569 m_con.Send(peer_id, 1, reply, true);
2572 core::list<PlayerInfo> Server::getPlayerInfo()
2574 DSTACK(__FUNCTION_NAME);
2575 JMutexAutoLock envlock(m_env_mutex);
2576 JMutexAutoLock conlock(m_con_mutex);
2578 core::list<PlayerInfo> list;
2580 core::list<Player*> players = m_env.getPlayers();
2582 core::list<Player*>::Iterator i;
2583 for(i = players.begin();
2584 i != players.end(); i++)
2588 Player *player = *i;
2591 con::Peer *peer = m_con.GetPeer(player->peer_id);
2592 // Copy info from peer to info struct
2594 info.address = peer->address;
2595 info.avg_rtt = peer->avg_rtt;
2597 catch(con::PeerNotFoundException &e)
2599 // Set dummy peer info
2601 info.address = Address(0,0,0,0,0);
2605 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2606 info.position = player->getPosition();
2608 list.push_back(info);
2614 void Server::peerAdded(con::Peer *peer)
2616 DSTACK(__FUNCTION_NAME);
2617 dout_server<<"Server::peerAdded(): peer->id="
2618 <<peer->id<<std::endl;
2621 c.type = PEER_ADDED;
2622 c.peer_id = peer->id;
2624 m_peer_change_queue.push_back(c);
2627 void Server::deletingPeer(con::Peer *peer, bool timeout)
2629 DSTACK(__FUNCTION_NAME);
2630 dout_server<<"Server::deletingPeer(): peer->id="
2631 <<peer->id<<", timeout="<<timeout<<std::endl;
2634 c.type = PEER_REMOVED;
2635 c.peer_id = peer->id;
2636 c.timeout = timeout;
2637 m_peer_change_queue.push_back(c);
2640 void Server::SendObjectData(float dtime)
2642 DSTACK(__FUNCTION_NAME);
2644 core::map<v3s16, bool> stepped_blocks;
2646 for(core::map<u16, RemoteClient*>::Iterator
2647 i = m_clients.getIterator();
2648 i.atEnd() == false; i++)
2650 u16 peer_id = i.getNode()->getKey();
2651 RemoteClient *client = i.getNode()->getValue();
2652 assert(client->peer_id == peer_id);
2654 if(client->serialization_version == SER_FMT_VER_INVALID)
2657 client->SendObjectData(this, dtime, stepped_blocks);
2661 void Server::SendPlayerInfos()
2663 DSTACK(__FUNCTION_NAME);
2665 //JMutexAutoLock envlock(m_env_mutex);
2667 // Get connected players
2668 core::list<Player*> players = m_env.getPlayers(true);
2670 u32 player_count = players.getSize();
2671 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2673 SharedBuffer<u8> data(datasize);
2674 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2677 core::list<Player*>::Iterator i;
2678 for(i = players.begin();
2679 i != players.end(); i++)
2681 Player *player = *i;
2683 /*dstream<<"Server sending player info for player with "
2684 "peer_id="<<player->peer_id<<std::endl;*/
2686 writeU16(&data[start], player->peer_id);
2687 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2688 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2689 start += 2+PLAYERNAME_SIZE;
2692 //JMutexAutoLock conlock(m_con_mutex);
2695 m_con.SendToAll(0, data, true);
2713 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2719 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2725 enum ItemSpecType type;
2726 // Only other one of these is used
2732 items: a pointer to an array of 9 pointers to items
2733 specs: a pointer to an array of 9 ItemSpecs
2735 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2737 u16 items_min_x = 100;
2738 u16 items_max_x = 100;
2739 u16 items_min_y = 100;
2740 u16 items_max_y = 100;
2741 for(u16 y=0; y<3; y++)
2742 for(u16 x=0; x<3; x++)
2744 if(items[y*3 + x] == NULL)
2746 if(items_min_x == 100 || x < items_min_x)
2748 if(items_min_y == 100 || y < items_min_y)
2750 if(items_max_x == 100 || x > items_max_x)
2752 if(items_max_y == 100 || y > items_max_y)
2755 // No items at all, just return false
2756 if(items_min_x == 100)
2759 u16 items_w = items_max_x - items_min_x + 1;
2760 u16 items_h = items_max_y - items_min_y + 1;
2762 u16 specs_min_x = 100;
2763 u16 specs_max_x = 100;
2764 u16 specs_min_y = 100;
2765 u16 specs_max_y = 100;
2766 for(u16 y=0; y<3; y++)
2767 for(u16 x=0; x<3; x++)
2769 if(specs[y*3 + x].type == ITEM_NONE)
2771 if(specs_min_x == 100 || x < specs_min_x)
2773 if(specs_min_y == 100 || y < specs_min_y)
2775 if(specs_max_x == 100 || x > specs_max_x)
2777 if(specs_max_y == 100 || y > specs_max_y)
2780 // No specs at all, just return false
2781 if(specs_min_x == 100)
2784 u16 specs_w = specs_max_x - specs_min_x + 1;
2785 u16 specs_h = specs_max_y - specs_min_y + 1;
2788 if(items_w != specs_w || items_h != specs_h)
2791 for(u16 y=0; y<specs_h; y++)
2792 for(u16 x=0; x<specs_w; x++)
2794 u16 items_x = items_min_x + x;
2795 u16 items_y = items_min_y + y;
2796 u16 specs_x = specs_min_x + x;
2797 u16 specs_y = specs_min_y + y;
2798 InventoryItem *item = items[items_y * 3 + items_x];
2799 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2801 if(spec.type == ITEM_NONE)
2803 // Has to be no item
2809 // There should be an item
2813 std::string itemname = item->getName();
2815 if(spec.type == ITEM_MATERIAL)
2817 if(itemname != "MaterialItem")
2819 MaterialItem *mitem = (MaterialItem*)item;
2820 if(mitem->getMaterial() != spec.num)
2823 else if(spec.type == ITEM_CRAFT)
2825 if(itemname != "CraftItem")
2827 CraftItem *mitem = (CraftItem*)item;
2828 if(mitem->getSubName() != spec.name)
2831 else if(spec.type == ITEM_TOOL)
2833 // Not supported yet
2836 else if(spec.type == ITEM_MBO)
2838 // Not supported yet
2843 // Not supported yet
2851 void Server::SendInventory(u16 peer_id)
2853 DSTACK(__FUNCTION_NAME);
2855 Player* player = m_env.getPlayer(peer_id);
2858 Calculate crafting stuff
2860 if(g_settings.getBool("creative_mode") == false)
2862 InventoryList *clist = player->inventory.getList("craft");
2863 InventoryList *rlist = player->inventory.getList("craftresult");
2866 rlist->clearItems();
2870 InventoryItem *items[9];
2871 for(u16 i=0; i<9; i++)
2873 items[i] = clist->getItem(i);
2882 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2883 if(checkItemCombination(items, specs))
2885 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2894 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2895 if(checkItemCombination(items, specs))
2897 rlist->addItem(new CraftItem("Stick", 4));
2906 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2907 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2908 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2909 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2910 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2911 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2912 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2913 if(checkItemCombination(items, specs))
2915 rlist->addItem(new MapBlockObjectItem("Sign"));
2924 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2925 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2926 if(checkItemCombination(items, specs))
2928 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2937 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2938 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2939 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2940 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2941 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2942 if(checkItemCombination(items, specs))
2944 rlist->addItem(new ToolItem("WPick", 0));
2953 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2954 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2955 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2956 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2957 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2958 if(checkItemCombination(items, specs))
2960 rlist->addItem(new ToolItem("STPick", 0));
2969 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2970 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2971 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2972 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2973 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2974 if(checkItemCombination(items, specs))
2976 rlist->addItem(new ToolItem("MesePick", 0));
2981 } // if creative_mode == false
2987 std::ostringstream os;
2988 //os.imbue(std::locale("C"));
2990 player->inventory.serialize(os);
2992 std::string s = os.str();
2994 SharedBuffer<u8> data(s.size()+2);
2995 writeU16(&data[0], TOCLIENT_INVENTORY);
2996 memcpy(&data[2], s.c_str(), s.size());
2999 m_con.Send(peer_id, 0, data, true);
3002 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3004 DSTACK(__FUNCTION_NAME);
3006 std::ostringstream os(std::ios_base::binary);
3010 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3011 os.write((char*)buf, 2);
3014 writeU16(buf, message.size());
3015 os.write((char*)buf, 2);
3018 for(u32 i=0; i<message.size(); i++)
3022 os.write((char*)buf, 2);
3026 std::string s = os.str();
3027 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3029 m_con.Send(peer_id, 0, data, true);
3032 void Server::BroadcastChatMessage(const std::wstring &message)
3034 for(core::map<u16, RemoteClient*>::Iterator
3035 i = m_clients.getIterator();
3036 i.atEnd() == false; i++)
3038 // Get client and check that it is valid
3039 RemoteClient *client = i.getNode()->getValue();
3040 assert(client->peer_id == i.getNode()->getKey());
3041 if(client->serialization_version == SER_FMT_VER_INVALID)
3044 SendChatMessage(client->peer_id, message);
3048 void Server::SendBlocks(float dtime)
3050 DSTACK(__FUNCTION_NAME);
3052 JMutexAutoLock envlock(m_env_mutex);
3054 core::array<PrioritySortedBlockTransfer> queue;
3056 s32 total_sending = 0;
3058 for(core::map<u16, RemoteClient*>::Iterator
3059 i = m_clients.getIterator();
3060 i.atEnd() == false; i++)
3062 RemoteClient *client = i.getNode()->getValue();
3063 assert(client->peer_id == i.getNode()->getKey());
3065 total_sending += client->SendingCount();
3067 if(client->serialization_version == SER_FMT_VER_INVALID)
3070 client->GetNextBlocks(this, dtime, queue);
3074 // Lowest priority number comes first.
3075 // Lowest is most important.
3078 JMutexAutoLock conlock(m_con_mutex);
3080 for(u32 i=0; i<queue.size(); i++)
3082 //TODO: Calculate limit dynamically
3083 if(total_sending >= g_settings.getS32
3084 ("max_simultaneous_block_sends_server_total"))
3087 PrioritySortedBlockTransfer q = queue[i];
3089 MapBlock *block = NULL;
3092 block = m_env.getMap().getBlockNoCreate(q.pos);
3094 catch(InvalidPositionException &e)
3099 RemoteClient *client = getClient(q.peer_id);
3101 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3103 client->SentBlock(q.pos);
3110 RemoteClient* Server::getClient(u16 peer_id)
3112 DSTACK(__FUNCTION_NAME);
3113 //JMutexAutoLock lock(m_con_mutex);
3114 core::map<u16, RemoteClient*>::Node *n;
3115 n = m_clients.find(peer_id);
3116 // A client should exist for all peers
3118 return n->getValue();
3121 void setCreativeInventory(Player *player)
3123 player->resetInventory();
3125 // Give some good picks
3127 InventoryItem *item = new ToolItem("STPick", 0);
3128 void* r = player->inventory.addItem("main", item);
3132 InventoryItem *item = new ToolItem("MesePick", 0);
3133 void* r = player->inventory.addItem("main", item);
3140 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3143 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3144 player->inventory.addItem("main", item);
3147 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3149 // Skip some materials
3150 if(i == CONTENT_WATER || i == CONTENT_TORCH
3151 || i == CONTENT_COALSTONE)
3154 InventoryItem *item = new MaterialItem(i, 1);
3155 player->inventory.addItem("main", item);
3159 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3160 void* r = player->inventory.addItem("main", item);
3165 Player *Server::emergePlayer(const char *name, const char *password,
3169 Try to get an existing player
3171 Player *player = m_env.getPlayer(name);
3174 // If player is already connected, cancel
3175 if(player->peer_id != 0)
3177 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3182 player->peer_id = peer_id;
3184 // Reset inventory to creative if in creative mode
3185 if(g_settings.getBool("creative_mode"))
3187 setCreativeInventory(player);
3194 If player with the wanted peer_id already exists, cancel.
3196 if(m_env.getPlayer(peer_id) != NULL)
3198 dstream<<"emergePlayer(): Player with wrong name but same"
3199 " peer_id already exists"<<std::endl;
3207 player = new ServerRemotePlayer();
3208 //player->peer_id = c.peer_id;
3209 //player->peer_id = PEER_ID_INEXISTENT;
3210 player->peer_id = peer_id;
3211 player->updateName(name);
3217 dstream<<"Server: Finding spawn place for player \""
3218 <<player->getName()<<"\""<<std::endl;
3222 player->setPosition(intToFloat(v3s16(
3229 f32 groundheight = 0;
3231 // Try to find a good place a few times
3232 for(s32 i=0; i<500; i++)
3235 // We're going to try to throw the player to this position
3236 nodepos = v2s16(-range + (myrand()%(range*2)),
3237 -range + (myrand()%(range*2)));
3238 v2s16 sectorpos = getNodeSectorPos(nodepos);
3240 m_env.getMap().emergeSector(sectorpos);
3241 // Get ground height at point
3242 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3243 // The sector should have been generated -> groundheight exists
3244 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3245 // Don't go underwater
3246 if(groundheight < WATER_LEVEL)
3248 //dstream<<"-> Underwater"<<std::endl;
3251 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3252 // Get block at point
3254 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3255 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3256 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3257 // Don't go inside ground
3259 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3260 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3261 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3262 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3263 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3264 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3266 dstream<<"-> Inside ground"<<std::endl;
3270 }catch(InvalidPositionException &e)
3272 dstream<<"-> Invalid position"<<std::endl;
3273 // Ignore invalid position
3277 // Found a good place
3278 dstream<<"Searched through "<<i<<" places."<<std::endl;
3283 // If no suitable place was not found, go above water at least.
3284 if(groundheight < WATER_LEVEL)
3285 groundheight = WATER_LEVEL;
3287 player->setPosition(intToFloat(v3s16(
3296 Add player to environment
3299 m_env.addPlayer(player);
3302 Add stuff to inventory
3305 if(g_settings.getBool("creative_mode"))
3307 setCreativeInventory(player);
3312 InventoryItem *item = new ToolItem("WPick", 32000);
3313 void* r = player->inventory.addItem("main", item);
3317 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3318 void* r = player->inventory.addItem("main", item);
3322 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3323 void* r = player->inventory.addItem("main", item);
3327 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3328 void* r = player->inventory.addItem("main", item);
3332 InventoryItem *item = new CraftItem("Stick", 4);
3333 void* r = player->inventory.addItem("main", item);
3337 InventoryItem *item = new ToolItem("WPick", 32000);
3338 void* r = player->inventory.addItem("main", item);
3342 InventoryItem *item = new ToolItem("STPick", 32000);
3343 void* r = player->inventory.addItem("main", item);
3346 /*// Give some lights
3348 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3349 bool r = player->inventory.addItem("main", item);
3353 for(u16 i=0; i<4; i++)
3355 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3356 bool r = player->inventory.addItem("main", item);
3359 /*// Give some other stuff
3361 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3362 bool r = player->inventory.addItem("main", item);
3369 } // create new player
3373 void Server::UpdateBlockWaterPressure(MapBlock *block,
3374 core::map<v3s16, MapBlock*> &modified_blocks)
3376 MapVoxelManipulator v(&m_env.getMap());
3377 v.m_disable_water_climb =
3378 g_settings.getBool("disable_water_climb");
3380 VoxelArea area(block->getPosRelative(),
3381 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3385 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3387 catch(ProcessingLimitException &e)
3389 dstream<<"Processing limit reached (1)"<<std::endl;
3392 v.blitBack(modified_blocks);
3396 void Server::handlePeerChange(PeerChange &c)
3398 JMutexAutoLock envlock(m_env_mutex);
3399 JMutexAutoLock conlock(m_con_mutex);
3401 if(c.type == PEER_ADDED)
3408 core::map<u16, RemoteClient*>::Node *n;
3409 n = m_clients.find(c.peer_id);
3410 // The client shouldn't already exist
3414 RemoteClient *client = new RemoteClient();
3415 client->peer_id = c.peer_id;
3416 m_clients.insert(client->peer_id, client);
3419 else if(c.type == PEER_REMOVED)
3426 core::map<u16, RemoteClient*>::Node *n;
3427 n = m_clients.find(c.peer_id);
3428 // The client should exist
3431 // Collect information about leaving in chat
3432 std::wstring message;
3434 std::wstring name = L"unknown";
3435 Player *player = m_env.getPlayer(c.peer_id);
3437 name = narrow_to_wide(player->getName());
3441 message += L" left game";
3443 message += L" (timed out)";
3448 m_env.removePlayer(c.peer_id);
3451 // Set player client disconnected
3453 Player *player = m_env.getPlayer(c.peer_id);
3455 player->peer_id = 0;
3459 delete m_clients[c.peer_id];
3460 m_clients.remove(c.peer_id);
3462 // Send player info to all remaining clients
3465 // Send leave chat message to all remaining clients
3466 BroadcastChatMessage(message);
3475 void Server::handlePeerChanges()
3477 while(m_peer_change_queue.size() > 0)
3479 PeerChange c = m_peer_change_queue.pop_front();
3481 dout_server<<"Server: Handling peer change: "
3482 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3485 handlePeerChange(c);
3489 void dedicated_server_loop(Server &server)
3491 DSTACK(__FUNCTION_NAME);
3493 std::cout<<std::endl;
3494 std::cout<<"========================"<<std::endl;
3495 std::cout<<"Running dedicated server"<<std::endl;
3496 std::cout<<"========================"<<std::endl;
3497 std::cout<<std::endl;
3501 // This is kind of a hack but can be done like this
3502 // because server.step() is very light
3506 static int counter = 0;
3512 core::list<PlayerInfo> list = server.getPlayerInfo();
3513 core::list<PlayerInfo>::Iterator i;
3514 static u32 sum_old = 0;
3515 u32 sum = PIChecksum(list);
3518 std::cout<<DTIME<<"Player info:"<<std::endl;
3519 for(i=list.begin(); i!=list.end(); i++)
3521 i->PrintLine(&std::cout);