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 // Save only changed parts
1399 m_env.getMap().save(true);
1401 // Delete unused sectors
1402 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1403 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1404 if(deleted_count > 0)
1406 dout_server<<"Server: Unloaded "<<deleted_count
1407 <<" sectors from memory"<<std::endl;
1411 m_env.serializePlayers(m_mapsavedir);
1416 void Server::Receive()
1418 DSTACK(__FUNCTION_NAME);
1419 u32 data_maxsize = 10000;
1420 Buffer<u8> data(data_maxsize);
1425 JMutexAutoLock conlock(m_con_mutex);
1426 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1429 // This has to be called so that the client list gets synced
1430 // with the peer list of the connection
1431 handlePeerChanges();
1433 ProcessData(*data, datasize, peer_id);
1435 catch(con::InvalidIncomingDataException &e)
1437 derr_server<<"Server::Receive(): "
1438 "InvalidIncomingDataException: what()="
1439 <<e.what()<<std::endl;
1441 catch(con::PeerNotFoundException &e)
1443 //NOTE: This is not needed anymore
1445 // The peer has been disconnected.
1446 // Find the associated player and remove it.
1448 /*JMutexAutoLock envlock(m_env_mutex);
1450 dout_server<<"ServerThread: peer_id="<<peer_id
1451 <<" has apparently closed connection. "
1452 <<"Removing player."<<std::endl;
1454 m_env.removePlayer(peer_id);*/
1458 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1460 DSTACK(__FUNCTION_NAME);
1461 // Environment is locked first.
1462 JMutexAutoLock envlock(m_env_mutex);
1463 JMutexAutoLock conlock(m_con_mutex);
1467 peer = m_con.GetPeer(peer_id);
1469 catch(con::PeerNotFoundException &e)
1471 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1472 <<peer_id<<" not found"<<std::endl;
1476 //u8 peer_ser_ver = peer->serialization_version;
1477 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1485 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1487 if(command == TOSERVER_INIT)
1489 // [0] u16 TOSERVER_INIT
1490 // [2] u8 SER_FMT_VER_HIGHEST
1491 // [3] u8[20] player_name
1496 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1497 <<peer->id<<std::endl;
1499 // First byte after command is maximum supported
1500 // serialization version
1501 u8 client_max = data[2];
1502 u8 our_max = SER_FMT_VER_HIGHEST;
1503 // Use the highest version supported by both
1504 u8 deployed = core::min_(client_max, our_max);
1505 // If it's lower than the lowest supported, give up.
1506 if(deployed < SER_FMT_VER_LOWEST)
1507 deployed = SER_FMT_VER_INVALID;
1509 //peer->serialization_version = deployed;
1510 getClient(peer->id)->pending_serialization_version = deployed;
1512 if(deployed == SER_FMT_VER_INVALID)
1514 derr_server<<DTIME<<"Server: Cannot negotiate "
1515 "serialization version with peer "
1516 <<peer_id<<std::endl;
1525 const u32 playername_size = 20;
1526 char playername[playername_size];
1527 for(u32 i=0; i<playername_size-1; i++)
1529 playername[i] = data[3+i];
1531 playername[playername_size-1] = 0;
1534 Player *player = emergePlayer(playername, "", peer_id);
1535 //Player *player = m_env.getPlayer(peer_id);
1538 // DEBUG: Test serialization
1539 std::ostringstream test_os;
1540 player->serialize(test_os);
1541 dstream<<"Player serialization test: \""<<test_os.str()
1543 std::istringstream test_is(test_os.str());
1544 player->deSerialize(test_is);
1547 // If failed, cancel
1550 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1551 <<": failed to emerge player"<<std::endl;
1556 // If a client is already connected to the player, cancel
1557 if(player->peer_id != 0)
1559 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1560 <<" tried to connect to "
1561 "an already connected player (peer_id="
1562 <<player->peer_id<<")"<<std::endl;
1565 // Set client of player
1566 player->peer_id = peer_id;
1569 // Check if player doesn't exist
1571 throw con::InvalidIncomingDataException
1572 ("Server::ProcessData(): INIT: Player doesn't exist");
1574 /*// update name if it was supplied
1575 if(datasize >= 20+3)
1578 player->updateName((const char*)&data[3]);
1581 // Now answer with a TOCLIENT_INIT
1583 SharedBuffer<u8> reply(2+1+6);
1584 writeU16(&reply[0], TOCLIENT_INIT);
1585 writeU8(&reply[2], deployed);
1586 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1588 m_con.Send(peer_id, 0, reply, true);
1592 if(command == TOSERVER_INIT2)
1594 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1595 <<peer->id<<std::endl;
1598 getClient(peer->id)->serialization_version
1599 = getClient(peer->id)->pending_serialization_version;
1602 Send some initialization data
1605 // Send player info to all players
1608 // Send inventory to player
1609 SendInventory(peer->id);
1613 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1614 m_time_of_day.get());
1615 m_con.Send(peer->id, 0, data, true);
1618 // Send information about server to player in chat
1620 std::wostringstream os(std::ios_base::binary);
1623 os<<L"uptime="<<m_uptime.get();
1624 // Information about clients
1626 for(core::map<u16, RemoteClient*>::Iterator
1627 i = m_clients.getIterator();
1628 i.atEnd() == false; i++)
1630 // Get client and check that it is valid
1631 RemoteClient *client = i.getNode()->getValue();
1632 assert(client->peer_id == i.getNode()->getKey());
1633 if(client->serialization_version == SER_FMT_VER_INVALID)
1636 Player *player = m_env.getPlayer(client->peer_id);
1637 // Get name of player
1638 std::wstring name = L"unknown";
1640 name = narrow_to_wide(player->getName());
1641 // Add name to information string
1645 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
1646 os<<" WARNING: Map saving is disabled."<<std::endl;
1648 SendChatMessage(peer_id, os.str());
1651 // Send information about joining in chat
1653 std::wstring name = L"unknown";
1654 Player *player = m_env.getPlayer(peer_id);
1656 name = narrow_to_wide(player->getName());
1658 std::wstring message;
1661 message += L" joined game";
1662 BroadcastChatMessage(message);
1668 if(peer_ser_ver == SER_FMT_VER_INVALID)
1670 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1671 " serialization format invalid or not initialized."
1672 " Skipping incoming command="<<command<<std::endl;
1676 Player *player = m_env.getPlayer(peer_id);
1679 derr_server<<"Server::ProcessData(): Cancelling: "
1680 "No player for peer_id="<<peer_id
1684 if(command == TOSERVER_PLAYERPOS)
1686 if(datasize < 2+12+12+4+4)
1690 v3s32 ps = readV3S32(&data[start+2]);
1691 v3s32 ss = readV3S32(&data[start+2+12]);
1692 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1693 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1694 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1695 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1696 pitch = wrapDegrees(pitch);
1697 yaw = wrapDegrees(yaw);
1698 player->setPosition(position);
1699 player->setSpeed(speed);
1700 player->setPitch(pitch);
1701 player->setYaw(yaw);
1703 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1704 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1705 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1707 else if(command == TOSERVER_GOTBLOCKS)
1720 u16 count = data[2];
1721 for(u16 i=0; i<count; i++)
1723 if((s16)datasize < 2+1+(i+1)*6)
1724 throw con::InvalidIncomingDataException
1725 ("GOTBLOCKS length is too short");
1726 v3s16 p = readV3S16(&data[2+1+i*6]);
1727 /*dstream<<"Server: GOTBLOCKS ("
1728 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1729 RemoteClient *client = getClient(peer_id);
1730 client->GotBlock(p);
1733 else if(command == TOSERVER_DELETEDBLOCKS)
1746 u16 count = data[2];
1747 for(u16 i=0; i<count; i++)
1749 if((s16)datasize < 2+1+(i+1)*6)
1750 throw con::InvalidIncomingDataException
1751 ("DELETEDBLOCKS length is too short");
1752 v3s16 p = readV3S16(&data[2+1+i*6]);
1753 /*dstream<<"Server: DELETEDBLOCKS ("
1754 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1755 RemoteClient *client = getClient(peer_id);
1756 client->SetBlockNotSent(p);
1759 else if(command == TOSERVER_CLICK_OBJECT)
1766 [2] u8 button (0=left, 1=right)
1771 u8 button = readU8(&data[2]);
1773 p.X = readS16(&data[3]);
1774 p.Y = readS16(&data[5]);
1775 p.Z = readS16(&data[7]);
1776 s16 id = readS16(&data[9]);
1777 //u16 item_i = readU16(&data[11]);
1779 MapBlock *block = NULL;
1782 block = m_env.getMap().getBlockNoCreate(p);
1784 catch(InvalidPositionException &e)
1786 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1790 MapBlockObject *obj = block->getObject(id);
1794 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1798 //TODO: Check that object is reasonably close
1803 InventoryList *ilist = player->inventory.getList("main");
1804 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1807 // Skip if inventory has no free space
1808 if(ilist->getUsedSlots() == ilist->getSize())
1810 dout_server<<"Player inventory has no free space"<<std::endl;
1815 Create the inventory item
1817 InventoryItem *item = NULL;
1818 // If it is an item-object, take the item from it
1819 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1821 item = ((ItemObject*)obj)->createInventoryItem();
1823 // Else create an item of the object
1826 item = new MapBlockObjectItem
1827 (obj->getInventoryString());
1830 // Add to inventory and send inventory
1831 ilist->addItem(item);
1832 SendInventory(player->peer_id);
1835 // Remove from block
1836 block->removeObject(id);
1839 else if(command == TOSERVER_GROUND_ACTION)
1847 [3] v3s16 nodepos_undersurface
1848 [9] v3s16 nodepos_abovesurface
1853 2: stop digging (all parameters ignored)
1855 u8 action = readU8(&data[2]);
1857 p_under.X = readS16(&data[3]);
1858 p_under.Y = readS16(&data[5]);
1859 p_under.Z = readS16(&data[7]);
1861 p_over.X = readS16(&data[9]);
1862 p_over.Y = readS16(&data[11]);
1863 p_over.Z = readS16(&data[13]);
1864 u16 item_i = readU16(&data[15]);
1866 //TODO: Check that target is reasonably close
1874 NOTE: This can be used in the future to check if
1875 somebody is cheating, by checking the timing.
1882 else if(action == 2)
1885 RemoteClient *client = getClient(peer->id);
1886 JMutexAutoLock digmutex(client->m_dig_mutex);
1887 client->m_dig_tool_item = -1;
1892 3: Digging completed
1894 else if(action == 3)
1896 // Mandatory parameter; actually used for nothing
1897 core::map<v3s16, MapBlock*> modified_blocks;
1900 u8 mineral = MINERAL_NONE;
1904 MapNode n = m_env.getMap().getNode(p_under);
1905 // Get material at position
1907 // If it's not diggable, do nothing
1908 if(content_diggable(material) == false)
1910 derr_server<<"Server: Not finishing digging: Node not diggable"
1915 mineral = n.getMineral();
1917 catch(InvalidPositionException &e)
1919 derr_server<<"Server: Not finishing digging: Node not found."
1920 <<" Adding block to emerge queue."
1922 m_emerge_queue.addBlock(peer_id,
1923 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1928 Send the removal to all other clients
1933 SharedBuffer<u8> reply(replysize);
1934 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1935 writeS16(&reply[2], p_under.X);
1936 writeS16(&reply[4], p_under.Y);
1937 writeS16(&reply[6], p_under.Z);
1939 for(core::map<u16, RemoteClient*>::Iterator
1940 i = m_clients.getIterator();
1941 i.atEnd() == false; i++)
1943 // Get client and check that it is valid
1944 RemoteClient *client = i.getNode()->getValue();
1945 assert(client->peer_id == i.getNode()->getKey());
1946 if(client->serialization_version == SER_FMT_VER_INVALID)
1949 // Don't send if it's the same one
1950 if(peer_id == client->peer_id)
1954 m_con.Send(client->peer_id, 0, reply, true);
1958 Update and send inventory
1961 if(g_settings.getBool("creative_mode") == false)
1966 InventoryList *mlist = player->inventory.getList("main");
1969 InventoryItem *item = mlist->getItem(item_i);
1970 if(item && (std::string)item->getName() == "ToolItem")
1972 ToolItem *titem = (ToolItem*)item;
1973 std::string toolname = titem->getToolName();
1975 // Get digging properties for material and tool
1976 DiggingProperties prop =
1977 getDiggingProperties(material, toolname);
1979 if(prop.diggable == false)
1981 derr_server<<"Server: WARNING: Player digged"
1982 <<" with impossible material + tool"
1983 <<" combination"<<std::endl;
1986 bool weared_out = titem->addWear(prop.wear);
1990 mlist->deleteItem(item_i);
1996 Add digged item to inventory
1999 InventoryItem *item = NULL;
2001 if(mineral != MINERAL_NONE)
2002 item = getDiggedMineralItem(mineral);
2005 item = new MaterialItem(material, 1);
2007 player->inventory.addItem("main", item);
2012 SendInventory(player->peer_id);
2017 (this takes some time so it is done after the quick stuff)
2019 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2026 // Update water pressure around modification
2027 // This also adds it to m_flow_active_nodes if appropriate
2029 MapVoxelManipulator v(&m_env.getMap());
2030 v.m_disable_water_climb =
2031 g_settings.getBool("disable_water_climb");
2033 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2037 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2039 catch(ProcessingLimitException &e)
2041 dstream<<"Processing limit reached (1)"<<std::endl;
2044 v.blitBack(modified_blocks);
2051 else if(action == 1)
2054 InventoryList *ilist = player->inventory.getList("main");
2059 InventoryItem *item = ilist->getItem(item_i);
2061 // If there is no item, it is not possible to add it anywhere
2066 Handle material items
2068 if(std::string("MaterialItem") == item->getName())
2071 // Don't add a node if this is not a free space
2072 MapNode n2 = m_env.getMap().getNode(p_over);
2073 if(content_buildable_to(n2.d) == false)
2075 // Client probably has wrong data.
2076 // Set block not sent, so that client will get
2078 dstream<<"Client "<<peer_id<<" tried to place"
2079 <<" node in invalid position; setting"
2080 <<" MapBlock not sent."<<std::endl;
2081 RemoteClient *client = getClient(peer_id);
2082 v3s16 blockpos = getNodeBlockPos(p_over);
2083 client->SetBlockNotSent(blockpos);
2087 catch(InvalidPositionException &e)
2089 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2090 <<" Adding block to emerge queue."
2092 m_emerge_queue.addBlock(peer_id,
2093 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2097 // Reset build time counter
2098 getClient(peer->id)->m_time_from_building.set(0.0);
2101 MaterialItem *mitem = (MaterialItem*)item;
2103 n.d = mitem->getMaterial();
2104 if(content_features(n.d).wall_mounted)
2105 n.dir = packDir(p_under - p_over);
2109 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2110 SharedBuffer<u8> reply(replysize);
2111 writeU16(&reply[0], TOCLIENT_ADDNODE);
2112 writeS16(&reply[2], p_over.X);
2113 writeS16(&reply[4], p_over.Y);
2114 writeS16(&reply[6], p_over.Z);
2115 n.serialize(&reply[8], peer_ser_ver);
2117 m_con.SendToAll(0, reply, true);
2122 InventoryList *ilist = player->inventory.getList("main");
2123 if(g_settings.getBool("creative_mode") == false && ilist)
2125 // Remove from inventory and send inventory
2126 if(mitem->getCount() == 1)
2127 ilist->deleteItem(item_i);
2131 SendInventory(peer_id);
2137 This takes some time so it is done after the quick stuff
2139 core::map<v3s16, MapBlock*> modified_blocks;
2140 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2146 InventoryList *ilist = player->inventory.getList("main");
2147 if(g_settings.getBool("creative_mode") == false && ilist)
2149 // Remove from inventory and send inventory
2150 if(mitem->getCount() == 1)
2151 ilist->deleteItem(item_i);
2155 SendInventory(peer_id);
2161 This takes some time so it is done after the quick stuff
2163 core::map<v3s16, MapBlock*> modified_blocks;
2164 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2167 Set the modified blocks unsent for all the clients
2170 //JMutexAutoLock lock2(m_con_mutex);
2172 for(core::map<u16, RemoteClient*>::Iterator
2173 i = m_clients.getIterator();
2174 i.atEnd() == false; i++)
2176 RemoteClient *client = i.getNode()->getValue();
2178 if(modified_blocks.size() > 0)
2180 // Remove block from sent history
2181 client->SetBlocksNotSent(modified_blocks);
2191 // Update water pressure around modification
2192 // This also adds it to m_flow_active_nodes if appropriate
2194 MapVoxelManipulator v(&m_env.getMap());
2195 v.m_disable_water_climb =
2196 g_settings.getBool("disable_water_climb");
2198 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2202 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2204 catch(ProcessingLimitException &e)
2206 dstream<<"Processing limit reached (1)"<<std::endl;
2209 v.blitBack(modified_blocks);
2217 v3s16 blockpos = getNodeBlockPos(p_over);
2219 MapBlock *block = NULL;
2222 block = m_env.getMap().getBlockNoCreate(blockpos);
2224 catch(InvalidPositionException &e)
2226 derr_server<<"Error while placing object: "
2227 "block not found"<<std::endl;
2231 v3s16 block_pos_i_on_map = block->getPosRelative();
2232 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2234 v3f pos = intToFloat(p_over);
2235 pos -= block_pos_f_on_map;
2237 /*dout_server<<"pos="
2238 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2241 MapBlockObject *obj = NULL;
2244 Handle block object items
2246 if(std::string("MBOItem") == item->getName())
2248 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2250 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2251 "inventorystring=\""
2252 <<oitem->getInventoryString()
2253 <<"\""<<std::endl;*/
2255 obj = oitem->createObject
2256 (pos, player->getYaw(), player->getPitch());
2263 dout_server<<"Placing a miscellaneous item on map"
2266 Create an ItemObject that contains the item.
2268 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2269 std::ostringstream os(std::ios_base::binary);
2270 item->serialize(os);
2271 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2272 iobj->setItemString(os.str());
2278 derr_server<<"WARNING: item resulted in NULL object, "
2279 <<"not placing onto map"
2284 block->addObject(obj);
2286 dout_server<<"Placed object"<<std::endl;
2288 InventoryList *ilist = player->inventory.getList("main");
2289 if(g_settings.getBool("creative_mode") == false && ilist)
2291 // Remove from inventory and send inventory
2292 ilist->deleteItem(item_i);
2294 SendInventory(peer_id);
2302 Catch invalid actions
2306 derr_server<<"WARNING: Server: Invalid action "
2307 <<action<<std::endl;
2311 else if(command == TOSERVER_RELEASE)
2320 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2323 else if(command == TOSERVER_SIGNTEXT)
2332 std::string datastring((char*)&data[2], datasize-2);
2333 std::istringstream is(datastring, std::ios_base::binary);
2336 is.read((char*)buf, 6);
2337 v3s16 blockpos = readV3S16(buf);
2338 is.read((char*)buf, 2);
2339 s16 id = readS16(buf);
2340 is.read((char*)buf, 2);
2341 u16 textlen = readU16(buf);
2343 for(u16 i=0; i<textlen; i++)
2345 is.read((char*)buf, 1);
2346 text += (char)buf[0];
2349 MapBlock *block = NULL;
2352 block = m_env.getMap().getBlockNoCreate(blockpos);
2354 catch(InvalidPositionException &e)
2356 derr_server<<"Error while setting sign text: "
2357 "block not found"<<std::endl;
2361 MapBlockObject *obj = block->getObject(id);
2364 derr_server<<"Error while setting sign text: "
2365 "object not found"<<std::endl;
2369 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2371 derr_server<<"Error while setting sign text: "
2372 "object is not a sign"<<std::endl;
2376 ((SignObject*)obj)->setText(text);
2378 obj->getBlock()->setChangedFlag();
2380 else if(command == TOSERVER_INVENTORY_ACTION)
2382 /*// Ignore inventory changes if in creative mode
2383 if(g_settings.getBool("creative_mode") == true)
2385 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2389 // Strip command and create a stream
2390 std::string datastring((char*)&data[2], datasize-2);
2391 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2392 std::istringstream is(datastring, std::ios_base::binary);
2394 InventoryAction *a = InventoryAction::deSerialize(is);
2398 Handle craftresult specially if not in creative mode
2400 bool disable_action = false;
2401 if(a->getType() == IACTION_MOVE
2402 && g_settings.getBool("creative_mode") == false)
2404 IMoveAction *ma = (IMoveAction*)a;
2405 // Don't allow moving anything to craftresult
2406 if(ma->to_name == "craftresult")
2409 disable_action = true;
2411 // When something is removed from craftresult
2412 if(ma->from_name == "craftresult")
2414 disable_action = true;
2415 // Remove stuff from craft
2416 InventoryList *clist = player->inventory.getList("craft");
2419 u16 count = ma->count;
2422 clist->decrementMaterials(count);
2425 // Feed action to player inventory
2426 a->apply(&player->inventory);
2429 // If something appeared in craftresult, throw it
2431 InventoryList *rlist = player->inventory.getList("craftresult");
2432 InventoryList *mlist = player->inventory.getList("main");
2433 if(rlist && mlist && rlist->getUsedSlots() == 1)
2435 InventoryItem *item1 = rlist->changeItem(0, NULL);
2436 mlist->addItem(item1);
2440 if(disable_action == false)
2442 // Feed action to player inventory
2443 a->apply(&player->inventory);
2448 SendInventory(player->peer_id);
2452 dstream<<"TOSERVER_INVENTORY_ACTION: "
2453 <<"InventoryAction::deSerialize() returned NULL"
2457 else if(command == TOSERVER_CHAT_MESSAGE)
2465 std::string datastring((char*)&data[2], datasize-2);
2466 std::istringstream is(datastring, std::ios_base::binary);
2469 is.read((char*)buf, 2);
2470 u16 len = readU16(buf);
2472 std::wstring message;
2473 for(u16 i=0; i<len; i++)
2475 is.read((char*)buf, 2);
2476 message += (wchar_t)readU16(buf);
2479 // Get player name of this client
2480 std::wstring name = narrow_to_wide(player->getName());
2482 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2484 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2487 Send the message to all other clients
2489 for(core::map<u16, RemoteClient*>::Iterator
2490 i = m_clients.getIterator();
2491 i.atEnd() == false; i++)
2493 // Get client and check that it is valid
2494 RemoteClient *client = i.getNode()->getValue();
2495 assert(client->peer_id == i.getNode()->getKey());
2496 if(client->serialization_version == SER_FMT_VER_INVALID)
2499 // Don't send if it's the same one
2500 if(peer_id == client->peer_id)
2503 SendChatMessage(client->peer_id, line);
2508 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2509 "unknown command "<<command<<std::endl;
2513 catch(SendFailedException &e)
2515 derr_server<<"Server::ProcessData(): SendFailedException: "
2521 /*void Server::Send(u16 peer_id, u16 channelnum,
2522 SharedBuffer<u8> data, bool reliable)
2524 JMutexAutoLock lock(m_con_mutex);
2525 m_con.Send(peer_id, channelnum, data, reliable);
2528 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2530 DSTACK(__FUNCTION_NAME);
2532 Create a packet with the block in the right format
2535 std::ostringstream os(std::ios_base::binary);
2536 block->serialize(os, ver);
2537 std::string s = os.str();
2538 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2540 u32 replysize = 8 + blockdata.getSize();
2541 SharedBuffer<u8> reply(replysize);
2542 v3s16 p = block->getPos();
2543 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2544 writeS16(&reply[2], p.X);
2545 writeS16(&reply[4], p.Y);
2546 writeS16(&reply[6], p.Z);
2547 memcpy(&reply[8], *blockdata, blockdata.getSize());
2549 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2550 <<": \tpacket size: "<<replysize<<std::endl;*/
2555 m_con.Send(peer_id, 1, reply, true);
2558 core::list<PlayerInfo> Server::getPlayerInfo()
2560 DSTACK(__FUNCTION_NAME);
2561 JMutexAutoLock envlock(m_env_mutex);
2562 JMutexAutoLock conlock(m_con_mutex);
2564 core::list<PlayerInfo> list;
2566 core::list<Player*> players = m_env.getPlayers();
2568 core::list<Player*>::Iterator i;
2569 for(i = players.begin();
2570 i != players.end(); i++)
2574 Player *player = *i;
2577 con::Peer *peer = m_con.GetPeer(player->peer_id);
2578 // Copy info from peer to info struct
2580 info.address = peer->address;
2581 info.avg_rtt = peer->avg_rtt;
2583 catch(con::PeerNotFoundException &e)
2585 // Set dummy peer info
2587 info.address = Address(0,0,0,0,0);
2591 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2592 info.position = player->getPosition();
2594 list.push_back(info);
2600 void Server::peerAdded(con::Peer *peer)
2602 DSTACK(__FUNCTION_NAME);
2603 dout_server<<"Server::peerAdded(): peer->id="
2604 <<peer->id<<std::endl;
2607 c.type = PEER_ADDED;
2608 c.peer_id = peer->id;
2610 m_peer_change_queue.push_back(c);
2613 void Server::deletingPeer(con::Peer *peer, bool timeout)
2615 DSTACK(__FUNCTION_NAME);
2616 dout_server<<"Server::deletingPeer(): peer->id="
2617 <<peer->id<<", timeout="<<timeout<<std::endl;
2620 c.type = PEER_REMOVED;
2621 c.peer_id = peer->id;
2622 c.timeout = timeout;
2623 m_peer_change_queue.push_back(c);
2626 void Server::SendObjectData(float dtime)
2628 DSTACK(__FUNCTION_NAME);
2630 core::map<v3s16, bool> stepped_blocks;
2632 for(core::map<u16, RemoteClient*>::Iterator
2633 i = m_clients.getIterator();
2634 i.atEnd() == false; i++)
2636 u16 peer_id = i.getNode()->getKey();
2637 RemoteClient *client = i.getNode()->getValue();
2638 assert(client->peer_id == peer_id);
2640 if(client->serialization_version == SER_FMT_VER_INVALID)
2643 client->SendObjectData(this, dtime, stepped_blocks);
2647 void Server::SendPlayerInfos()
2649 DSTACK(__FUNCTION_NAME);
2651 //JMutexAutoLock envlock(m_env_mutex);
2653 // Get connected players
2654 core::list<Player*> players = m_env.getPlayers(true);
2656 u32 player_count = players.getSize();
2657 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2659 SharedBuffer<u8> data(datasize);
2660 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2663 core::list<Player*>::Iterator i;
2664 for(i = players.begin();
2665 i != players.end(); i++)
2667 Player *player = *i;
2669 /*dstream<<"Server sending player info for player with "
2670 "peer_id="<<player->peer_id<<std::endl;*/
2672 writeU16(&data[start], player->peer_id);
2673 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2674 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2675 start += 2+PLAYERNAME_SIZE;
2678 //JMutexAutoLock conlock(m_con_mutex);
2681 m_con.SendToAll(0, data, true);
2699 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2705 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2711 enum ItemSpecType type;
2712 // Only other one of these is used
2718 items: a pointer to an array of 9 pointers to items
2719 specs: a pointer to an array of 9 ItemSpecs
2721 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2723 u16 items_min_x = 100;
2724 u16 items_max_x = 100;
2725 u16 items_min_y = 100;
2726 u16 items_max_y = 100;
2727 for(u16 y=0; y<3; y++)
2728 for(u16 x=0; x<3; x++)
2730 if(items[y*3 + x] == NULL)
2732 if(items_min_x == 100 || x < items_min_x)
2734 if(items_min_y == 100 || y < items_min_y)
2736 if(items_max_x == 100 || x > items_max_x)
2738 if(items_max_y == 100 || y > items_max_y)
2741 // No items at all, just return false
2742 if(items_min_x == 100)
2745 u16 items_w = items_max_x - items_min_x + 1;
2746 u16 items_h = items_max_y - items_min_y + 1;
2748 u16 specs_min_x = 100;
2749 u16 specs_max_x = 100;
2750 u16 specs_min_y = 100;
2751 u16 specs_max_y = 100;
2752 for(u16 y=0; y<3; y++)
2753 for(u16 x=0; x<3; x++)
2755 if(specs[y*3 + x].type == ITEM_NONE)
2757 if(specs_min_x == 100 || x < specs_min_x)
2759 if(specs_min_y == 100 || y < specs_min_y)
2761 if(specs_max_x == 100 || x > specs_max_x)
2763 if(specs_max_y == 100 || y > specs_max_y)
2766 // No specs at all, just return false
2767 if(specs_min_x == 100)
2770 u16 specs_w = specs_max_x - specs_min_x + 1;
2771 u16 specs_h = specs_max_y - specs_min_y + 1;
2774 if(items_w != specs_w || items_h != specs_h)
2777 for(u16 y=0; y<specs_h; y++)
2778 for(u16 x=0; x<specs_w; x++)
2780 u16 items_x = items_min_x + x;
2781 u16 items_y = items_min_y + y;
2782 u16 specs_x = specs_min_x + x;
2783 u16 specs_y = specs_min_y + y;
2784 InventoryItem *item = items[items_y * 3 + items_x];
2785 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2787 if(spec.type == ITEM_NONE)
2789 // Has to be no item
2795 // There should be an item
2799 std::string itemname = item->getName();
2801 if(spec.type == ITEM_MATERIAL)
2803 if(itemname != "MaterialItem")
2805 MaterialItem *mitem = (MaterialItem*)item;
2806 if(mitem->getMaterial() != spec.num)
2809 else if(spec.type == ITEM_CRAFT)
2811 if(itemname != "CraftItem")
2813 CraftItem *mitem = (CraftItem*)item;
2814 if(mitem->getSubName() != spec.name)
2817 else if(spec.type == ITEM_TOOL)
2819 // Not supported yet
2822 else if(spec.type == ITEM_MBO)
2824 // Not supported yet
2829 // Not supported yet
2837 void Server::SendInventory(u16 peer_id)
2839 DSTACK(__FUNCTION_NAME);
2841 Player* player = m_env.getPlayer(peer_id);
2844 Calculate crafting stuff
2846 if(g_settings.getBool("creative_mode") == false)
2848 InventoryList *clist = player->inventory.getList("craft");
2849 InventoryList *rlist = player->inventory.getList("craftresult");
2852 rlist->clearItems();
2856 InventoryItem *items[9];
2857 for(u16 i=0; i<9; i++)
2859 items[i] = clist->getItem(i);
2868 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2869 if(checkItemCombination(items, specs))
2871 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2880 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2881 if(checkItemCombination(items, specs))
2883 rlist->addItem(new CraftItem("Stick", 4));
2892 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2893 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2894 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2895 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2896 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2897 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2898 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2899 if(checkItemCombination(items, specs))
2901 rlist->addItem(new MapBlockObjectItem("Sign"));
2910 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2911 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2912 if(checkItemCombination(items, specs))
2914 rlist->addItem(new MaterialItem(CONTENT_TORCH, 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[4] = ItemSpec(ITEM_CRAFT, "Stick");
2927 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2928 if(checkItemCombination(items, specs))
2930 rlist->addItem(new ToolItem("WPick", 0));
2939 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2940 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2941 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2942 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2943 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2944 if(checkItemCombination(items, specs))
2946 rlist->addItem(new ToolItem("STPick", 0));
2955 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2956 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2957 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2958 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2959 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2960 if(checkItemCombination(items, specs))
2962 rlist->addItem(new ToolItem("MesePick", 0));
2967 } // if creative_mode == false
2973 std::ostringstream os;
2974 //os.imbue(std::locale("C"));
2976 player->inventory.serialize(os);
2978 std::string s = os.str();
2980 SharedBuffer<u8> data(s.size()+2);
2981 writeU16(&data[0], TOCLIENT_INVENTORY);
2982 memcpy(&data[2], s.c_str(), s.size());
2985 m_con.Send(peer_id, 0, data, true);
2988 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2990 DSTACK(__FUNCTION_NAME);
2992 std::ostringstream os(std::ios_base::binary);
2996 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2997 os.write((char*)buf, 2);
3000 writeU16(buf, message.size());
3001 os.write((char*)buf, 2);
3004 for(u32 i=0; i<message.size(); i++)
3008 os.write((char*)buf, 2);
3012 std::string s = os.str();
3013 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3015 m_con.Send(peer_id, 0, data, true);
3018 void Server::BroadcastChatMessage(const std::wstring &message)
3020 for(core::map<u16, RemoteClient*>::Iterator
3021 i = m_clients.getIterator();
3022 i.atEnd() == false; i++)
3024 // Get client and check that it is valid
3025 RemoteClient *client = i.getNode()->getValue();
3026 assert(client->peer_id == i.getNode()->getKey());
3027 if(client->serialization_version == SER_FMT_VER_INVALID)
3030 SendChatMessage(client->peer_id, message);
3034 void Server::SendBlocks(float dtime)
3036 DSTACK(__FUNCTION_NAME);
3038 JMutexAutoLock envlock(m_env_mutex);
3040 core::array<PrioritySortedBlockTransfer> queue;
3042 s32 total_sending = 0;
3044 for(core::map<u16, RemoteClient*>::Iterator
3045 i = m_clients.getIterator();
3046 i.atEnd() == false; i++)
3048 RemoteClient *client = i.getNode()->getValue();
3049 assert(client->peer_id == i.getNode()->getKey());
3051 total_sending += client->SendingCount();
3053 if(client->serialization_version == SER_FMT_VER_INVALID)
3056 client->GetNextBlocks(this, dtime, queue);
3060 // Lowest priority number comes first.
3061 // Lowest is most important.
3064 JMutexAutoLock conlock(m_con_mutex);
3066 for(u32 i=0; i<queue.size(); i++)
3068 //TODO: Calculate limit dynamically
3069 if(total_sending >= g_settings.getS32
3070 ("max_simultaneous_block_sends_server_total"))
3073 PrioritySortedBlockTransfer q = queue[i];
3075 MapBlock *block = NULL;
3078 block = m_env.getMap().getBlockNoCreate(q.pos);
3080 catch(InvalidPositionException &e)
3085 RemoteClient *client = getClient(q.peer_id);
3087 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3089 client->SentBlock(q.pos);
3096 RemoteClient* Server::getClient(u16 peer_id)
3098 DSTACK(__FUNCTION_NAME);
3099 //JMutexAutoLock lock(m_con_mutex);
3100 core::map<u16, RemoteClient*>::Node *n;
3101 n = m_clients.find(peer_id);
3102 // A client should exist for all peers
3104 return n->getValue();
3107 void setCreativeInventory(Player *player)
3109 player->resetInventory();
3111 // Give some good picks
3113 InventoryItem *item = new ToolItem("STPick", 0);
3114 void* r = player->inventory.addItem("main", item);
3118 InventoryItem *item = new ToolItem("MesePick", 0);
3119 void* r = player->inventory.addItem("main", item);
3126 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3129 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3130 player->inventory.addItem("main", item);
3133 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3135 // Skip some materials
3136 if(i == CONTENT_WATER || i == CONTENT_TORCH
3137 || i == CONTENT_COALSTONE)
3140 InventoryItem *item = new MaterialItem(i, 1);
3141 player->inventory.addItem("main", item);
3145 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3146 void* r = player->inventory.addItem("main", item);
3151 Player *Server::emergePlayer(const char *name, const char *password,
3155 Try to get an existing player
3157 Player *player = m_env.getPlayer(name);
3160 // If player is already connected, cancel
3161 if(player->peer_id != 0)
3163 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3168 player->peer_id = peer_id;
3170 // Reset inventory to creative if in creative mode
3171 if(g_settings.getBool("creative_mode"))
3173 setCreativeInventory(player);
3180 If player with the wanted peer_id already exists, cancel.
3182 if(m_env.getPlayer(peer_id) != NULL)
3184 dstream<<"emergePlayer(): Player with wrong name but same"
3185 " peer_id already exists"<<std::endl;
3193 player = new ServerRemotePlayer();
3194 //player->peer_id = c.peer_id;
3195 //player->peer_id = PEER_ID_INEXISTENT;
3196 player->peer_id = peer_id;
3197 player->updateName(name);
3203 dstream<<"Server: Finding spawn place for player \""
3204 <<player->getName()<<"\""<<std::endl;
3208 player->setPosition(intToFloat(v3s16(
3215 f32 groundheight = 0;
3217 // Try to find a good place a few times
3218 for(s32 i=0; i<500; i++)
3221 // We're going to try to throw the player to this position
3222 nodepos = v2s16(-range + (myrand()%(range*2)),
3223 -range + (myrand()%(range*2)));
3224 v2s16 sectorpos = getNodeSectorPos(nodepos);
3226 m_env.getMap().emergeSector(sectorpos);
3227 // Get ground height at point
3228 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3229 // The sector should have been generated -> groundheight exists
3230 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3231 // Don't go underwater
3232 if(groundheight < WATER_LEVEL)
3234 //dstream<<"-> Underwater"<<std::endl;
3237 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3238 // Get block at point
3240 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3241 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3242 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3243 // Don't go inside ground
3245 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3246 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3247 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3248 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3249 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3250 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3252 dstream<<"-> Inside ground"<<std::endl;
3256 }catch(InvalidPositionException &e)
3258 dstream<<"-> Invalid position"<<std::endl;
3259 // Ignore invalid position
3263 // Found a good place
3264 dstream<<"Searched through "<<i<<" places."<<std::endl;
3269 // If no suitable place was not found, go above water at least.
3270 if(groundheight < WATER_LEVEL)
3271 groundheight = WATER_LEVEL;
3273 player->setPosition(intToFloat(v3s16(
3282 Add player to environment
3285 m_env.addPlayer(player);
3288 Add stuff to inventory
3291 if(g_settings.getBool("creative_mode"))
3293 setCreativeInventory(player);
3298 InventoryItem *item = new ToolItem("WPick", 32000);
3299 void* r = player->inventory.addItem("main", item);
3303 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3304 void* r = player->inventory.addItem("main", item);
3308 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3309 void* r = player->inventory.addItem("main", item);
3313 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3314 void* r = player->inventory.addItem("main", item);
3318 InventoryItem *item = new CraftItem("Stick", 4);
3319 void* r = player->inventory.addItem("main", item);
3323 InventoryItem *item = new ToolItem("WPick", 32000);
3324 void* r = player->inventory.addItem("main", item);
3328 InventoryItem *item = new ToolItem("STPick", 32000);
3329 void* r = player->inventory.addItem("main", item);
3332 /*// Give some lights
3334 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3335 bool r = player->inventory.addItem("main", item);
3339 for(u16 i=0; i<4; i++)
3341 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3342 bool r = player->inventory.addItem("main", item);
3345 /*// Give some other stuff
3347 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3348 bool r = player->inventory.addItem("main", item);
3355 } // create new player
3359 void Server::UpdateBlockWaterPressure(MapBlock *block,
3360 core::map<v3s16, MapBlock*> &modified_blocks)
3362 MapVoxelManipulator v(&m_env.getMap());
3363 v.m_disable_water_climb =
3364 g_settings.getBool("disable_water_climb");
3366 VoxelArea area(block->getPosRelative(),
3367 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3371 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3373 catch(ProcessingLimitException &e)
3375 dstream<<"Processing limit reached (1)"<<std::endl;
3378 v.blitBack(modified_blocks);
3382 void Server::handlePeerChange(PeerChange &c)
3384 JMutexAutoLock envlock(m_env_mutex);
3385 JMutexAutoLock conlock(m_con_mutex);
3387 if(c.type == PEER_ADDED)
3394 core::map<u16, RemoteClient*>::Node *n;
3395 n = m_clients.find(c.peer_id);
3396 // The client shouldn't already exist
3400 RemoteClient *client = new RemoteClient();
3401 client->peer_id = c.peer_id;
3402 m_clients.insert(client->peer_id, client);
3405 else if(c.type == PEER_REMOVED)
3412 core::map<u16, RemoteClient*>::Node *n;
3413 n = m_clients.find(c.peer_id);
3414 // The client should exist
3417 // Collect information about leaving in chat
3418 std::wstring message;
3420 std::wstring name = L"unknown";
3421 Player *player = m_env.getPlayer(c.peer_id);
3423 name = narrow_to_wide(player->getName());
3427 message += L" left game";
3429 message += L" (timed out)";
3434 m_env.removePlayer(c.peer_id);
3437 // Set player client disconnected
3439 Player *player = m_env.getPlayer(c.peer_id);
3441 player->peer_id = 0;
3445 delete m_clients[c.peer_id];
3446 m_clients.remove(c.peer_id);
3448 // Send player info to all remaining clients
3451 // Send leave chat message to all remaining clients
3452 BroadcastChatMessage(message);
3461 void Server::handlePeerChanges()
3463 while(m_peer_change_queue.size() > 0)
3465 PeerChange c = m_peer_change_queue.pop_front();
3467 dout_server<<"Server: Handling peer change: "
3468 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3471 handlePeerChange(c);
3475 void dedicated_server_loop(Server &server)
3477 DSTACK(__FUNCTION_NAME);
3479 std::cout<<std::endl;
3480 std::cout<<"========================"<<std::endl;
3481 std::cout<<"Running dedicated server"<<std::endl;
3482 std::cout<<"========================"<<std::endl;
3483 std::cout<<std::endl;
3487 // This is kind of a hack but can be done like this
3488 // because server.step() is very light
3492 static int counter = 0;
3498 core::list<PlayerInfo> list = server.getPlayerInfo();
3499 core::list<PlayerInfo>::Iterator i;
3500 static u32 sum_old = 0;
3501 u32 sum = PIChecksum(list);
3504 std::cout<<DTIME<<"Player info:"<<std::endl;
3505 for(i=list.begin(); i!=list.end(); i++)
3507 i->PrintLine(&std::cout);