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
1646 SendChatMessage(peer_id, os.str());
1649 // Send information about joining in chat
1651 std::wstring name = L"unknown";
1652 Player *player = m_env.getPlayer(peer_id);
1654 name = narrow_to_wide(player->getName());
1656 std::wstring message;
1659 message += L" joined game";
1660 BroadcastChatMessage(message);
1666 if(peer_ser_ver == SER_FMT_VER_INVALID)
1668 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1669 " serialization format invalid or not initialized."
1670 " Skipping incoming command="<<command<<std::endl;
1674 Player *player = m_env.getPlayer(peer_id);
1677 derr_server<<"Server::ProcessData(): Cancelling: "
1678 "No player for peer_id="<<peer_id
1682 if(command == TOSERVER_PLAYERPOS)
1684 if(datasize < 2+12+12+4+4)
1688 v3s32 ps = readV3S32(&data[start+2]);
1689 v3s32 ss = readV3S32(&data[start+2+12]);
1690 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1691 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1692 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1693 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1694 pitch = wrapDegrees(pitch);
1695 yaw = wrapDegrees(yaw);
1696 player->setPosition(position);
1697 player->setSpeed(speed);
1698 player->setPitch(pitch);
1699 player->setYaw(yaw);
1701 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1702 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1703 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1705 else if(command == TOSERVER_GOTBLOCKS)
1718 u16 count = data[2];
1719 for(u16 i=0; i<count; i++)
1721 if((s16)datasize < 2+1+(i+1)*6)
1722 throw con::InvalidIncomingDataException
1723 ("GOTBLOCKS length is too short");
1724 v3s16 p = readV3S16(&data[2+1+i*6]);
1725 /*dstream<<"Server: GOTBLOCKS ("
1726 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1727 RemoteClient *client = getClient(peer_id);
1728 client->GotBlock(p);
1731 else if(command == TOSERVER_DELETEDBLOCKS)
1744 u16 count = data[2];
1745 for(u16 i=0; i<count; i++)
1747 if((s16)datasize < 2+1+(i+1)*6)
1748 throw con::InvalidIncomingDataException
1749 ("DELETEDBLOCKS length is too short");
1750 v3s16 p = readV3S16(&data[2+1+i*6]);
1751 /*dstream<<"Server: DELETEDBLOCKS ("
1752 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1753 RemoteClient *client = getClient(peer_id);
1754 client->SetBlockNotSent(p);
1757 else if(command == TOSERVER_CLICK_OBJECT)
1764 [2] u8 button (0=left, 1=right)
1769 u8 button = readU8(&data[2]);
1771 p.X = readS16(&data[3]);
1772 p.Y = readS16(&data[5]);
1773 p.Z = readS16(&data[7]);
1774 s16 id = readS16(&data[9]);
1775 //u16 item_i = readU16(&data[11]);
1777 MapBlock *block = NULL;
1780 block = m_env.getMap().getBlockNoCreate(p);
1782 catch(InvalidPositionException &e)
1784 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1788 MapBlockObject *obj = block->getObject(id);
1792 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1796 //TODO: Check that object is reasonably close
1801 InventoryList *ilist = player->inventory.getList("main");
1802 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1805 // Skip if inventory has no free space
1806 if(ilist->getUsedSlots() == ilist->getSize())
1808 dout_server<<"Player inventory has no free space"<<std::endl;
1813 Create the inventory item
1815 InventoryItem *item = NULL;
1816 // If it is an item-object, take the item from it
1817 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1819 item = ((ItemObject*)obj)->createInventoryItem();
1821 // Else create an item of the object
1824 item = new MapBlockObjectItem
1825 (obj->getInventoryString());
1828 // Add to inventory and send inventory
1829 ilist->addItem(item);
1830 SendInventory(player->peer_id);
1833 // Remove from block
1834 block->removeObject(id);
1837 else if(command == TOSERVER_GROUND_ACTION)
1845 [3] v3s16 nodepos_undersurface
1846 [9] v3s16 nodepos_abovesurface
1851 2: stop digging (all parameters ignored)
1853 u8 action = readU8(&data[2]);
1855 p_under.X = readS16(&data[3]);
1856 p_under.Y = readS16(&data[5]);
1857 p_under.Z = readS16(&data[7]);
1859 p_over.X = readS16(&data[9]);
1860 p_over.Y = readS16(&data[11]);
1861 p_over.Z = readS16(&data[13]);
1862 u16 item_i = readU16(&data[15]);
1864 //TODO: Check that target is reasonably close
1872 NOTE: This can be used in the future to check if
1873 somebody is cheating, by checking the timing.
1880 else if(action == 2)
1883 RemoteClient *client = getClient(peer->id);
1884 JMutexAutoLock digmutex(client->m_dig_mutex);
1885 client->m_dig_tool_item = -1;
1890 3: Digging completed
1892 else if(action == 3)
1894 // Mandatory parameter; actually used for nothing
1895 core::map<v3s16, MapBlock*> modified_blocks;
1898 u8 mineral = MINERAL_NONE;
1902 MapNode n = m_env.getMap().getNode(p_under);
1903 // Get material at position
1905 // If it's not diggable, do nothing
1906 if(content_diggable(material) == false)
1908 derr_server<<"Server: Not finishing digging: Node not diggable"
1913 mineral = n.getMineral();
1915 catch(InvalidPositionException &e)
1917 derr_server<<"Server: Not finishing digging: Node not found."
1918 <<" Adding block to emerge queue."
1920 m_emerge_queue.addBlock(peer_id,
1921 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1926 Send the removal to all other clients
1931 SharedBuffer<u8> reply(replysize);
1932 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1933 writeS16(&reply[2], p_under.X);
1934 writeS16(&reply[4], p_under.Y);
1935 writeS16(&reply[6], p_under.Z);
1937 for(core::map<u16, RemoteClient*>::Iterator
1938 i = m_clients.getIterator();
1939 i.atEnd() == false; i++)
1941 // Get client and check that it is valid
1942 RemoteClient *client = i.getNode()->getValue();
1943 assert(client->peer_id == i.getNode()->getKey());
1944 if(client->serialization_version == SER_FMT_VER_INVALID)
1947 // Don't send if it's the same one
1948 if(peer_id == client->peer_id)
1952 m_con.Send(client->peer_id, 0, reply, true);
1956 Update and send inventory
1959 if(g_settings.getBool("creative_mode") == false)
1964 InventoryList *mlist = player->inventory.getList("main");
1967 InventoryItem *item = mlist->getItem(item_i);
1968 if(item && (std::string)item->getName() == "ToolItem")
1970 ToolItem *titem = (ToolItem*)item;
1971 std::string toolname = titem->getToolName();
1973 // Get digging properties for material and tool
1974 DiggingProperties prop =
1975 getDiggingProperties(material, toolname);
1977 if(prop.diggable == false)
1979 derr_server<<"Server: WARNING: Player digged"
1980 <<" with impossible material + tool"
1981 <<" combination"<<std::endl;
1984 bool weared_out = titem->addWear(prop.wear);
1988 mlist->deleteItem(item_i);
1994 Add digged item to inventory
1997 InventoryItem *item = NULL;
1999 if(mineral != MINERAL_NONE)
2000 item = getDiggedMineralItem(mineral);
2003 item = new MaterialItem(material, 1);
2005 player->inventory.addItem("main", item);
2010 SendInventory(player->peer_id);
2015 (this takes some time so it is done after the quick stuff)
2017 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2024 // Update water pressure around modification
2025 // This also adds it to m_flow_active_nodes if appropriate
2027 MapVoxelManipulator v(&m_env.getMap());
2028 v.m_disable_water_climb =
2029 g_settings.getBool("disable_water_climb");
2031 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2035 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2037 catch(ProcessingLimitException &e)
2039 dstream<<"Processing limit reached (1)"<<std::endl;
2042 v.blitBack(modified_blocks);
2049 else if(action == 1)
2052 InventoryList *ilist = player->inventory.getList("main");
2057 InventoryItem *item = ilist->getItem(item_i);
2059 // If there is no item, it is not possible to add it anywhere
2064 Handle material items
2066 if(std::string("MaterialItem") == item->getName())
2069 // Don't add a node if this is not a free space
2070 MapNode n2 = m_env.getMap().getNode(p_over);
2071 if(content_buildable_to(n2.d) == false)
2073 // Client probably has wrong data.
2074 // Set block not sent, so that client will get
2076 dstream<<"Client "<<peer_id<<" tried to place"
2077 <<" node in invalid position; setting"
2078 <<" MapBlock not sent."<<std::endl;
2079 RemoteClient *client = getClient(peer_id);
2080 v3s16 blockpos = getNodeBlockPos(p_over);
2081 client->SetBlockNotSent(blockpos);
2085 catch(InvalidPositionException &e)
2087 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2088 <<" Adding block to emerge queue."
2090 m_emerge_queue.addBlock(peer_id,
2091 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2095 // Reset build time counter
2096 getClient(peer->id)->m_time_from_building.set(0.0);
2099 MaterialItem *mitem = (MaterialItem*)item;
2101 n.d = mitem->getMaterial();
2102 if(content_features(n.d).wall_mounted)
2103 n.dir = packDir(p_under - p_over);
2107 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2108 SharedBuffer<u8> reply(replysize);
2109 writeU16(&reply[0], TOCLIENT_ADDNODE);
2110 writeS16(&reply[2], p_over.X);
2111 writeS16(&reply[4], p_over.Y);
2112 writeS16(&reply[6], p_over.Z);
2113 n.serialize(&reply[8], peer_ser_ver);
2115 m_con.SendToAll(0, reply, true);
2120 InventoryList *ilist = player->inventory.getList("main");
2121 if(g_settings.getBool("creative_mode") == false && ilist)
2123 // Remove from inventory and send inventory
2124 if(mitem->getCount() == 1)
2125 ilist->deleteItem(item_i);
2129 SendInventory(peer_id);
2135 This takes some time so it is done after the quick stuff
2137 core::map<v3s16, MapBlock*> modified_blocks;
2138 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2144 InventoryList *ilist = player->inventory.getList("main");
2145 if(g_settings.getBool("creative_mode") == false && ilist)
2147 // Remove from inventory and send inventory
2148 if(mitem->getCount() == 1)
2149 ilist->deleteItem(item_i);
2153 SendInventory(peer_id);
2159 This takes some time so it is done after the quick stuff
2161 core::map<v3s16, MapBlock*> modified_blocks;
2162 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2165 Set the modified blocks unsent for all the clients
2168 //JMutexAutoLock lock2(m_con_mutex);
2170 for(core::map<u16, RemoteClient*>::Iterator
2171 i = m_clients.getIterator();
2172 i.atEnd() == false; i++)
2174 RemoteClient *client = i.getNode()->getValue();
2176 if(modified_blocks.size() > 0)
2178 // Remove block from sent history
2179 client->SetBlocksNotSent(modified_blocks);
2189 // Update water pressure around modification
2190 // This also adds it to m_flow_active_nodes if appropriate
2192 MapVoxelManipulator v(&m_env.getMap());
2193 v.m_disable_water_climb =
2194 g_settings.getBool("disable_water_climb");
2196 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2200 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2202 catch(ProcessingLimitException &e)
2204 dstream<<"Processing limit reached (1)"<<std::endl;
2207 v.blitBack(modified_blocks);
2215 v3s16 blockpos = getNodeBlockPos(p_over);
2217 MapBlock *block = NULL;
2220 block = m_env.getMap().getBlockNoCreate(blockpos);
2222 catch(InvalidPositionException &e)
2224 derr_server<<"Error while placing object: "
2225 "block not found"<<std::endl;
2229 v3s16 block_pos_i_on_map = block->getPosRelative();
2230 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2232 v3f pos = intToFloat(p_over);
2233 pos -= block_pos_f_on_map;
2235 /*dout_server<<"pos="
2236 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2239 MapBlockObject *obj = NULL;
2242 Handle block object items
2244 if(std::string("MBOItem") == item->getName())
2246 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2248 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2249 "inventorystring=\""
2250 <<oitem->getInventoryString()
2251 <<"\""<<std::endl;*/
2253 obj = oitem->createObject
2254 (pos, player->getYaw(), player->getPitch());
2261 dout_server<<"Placing a miscellaneous item on map"
2264 Create an ItemObject that contains the item.
2266 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2267 std::ostringstream os(std::ios_base::binary);
2268 item->serialize(os);
2269 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2270 iobj->setItemString(os.str());
2276 derr_server<<"WARNING: item resulted in NULL object, "
2277 <<"not placing onto map"
2282 block->addObject(obj);
2284 dout_server<<"Placed object"<<std::endl;
2286 InventoryList *ilist = player->inventory.getList("main");
2287 if(g_settings.getBool("creative_mode") == false && ilist)
2289 // Remove from inventory and send inventory
2290 ilist->deleteItem(item_i);
2292 SendInventory(peer_id);
2300 Catch invalid actions
2304 derr_server<<"WARNING: Server: Invalid action "
2305 <<action<<std::endl;
2309 else if(command == TOSERVER_RELEASE)
2318 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2321 else if(command == TOSERVER_SIGNTEXT)
2330 std::string datastring((char*)&data[2], datasize-2);
2331 std::istringstream is(datastring, std::ios_base::binary);
2334 is.read((char*)buf, 6);
2335 v3s16 blockpos = readV3S16(buf);
2336 is.read((char*)buf, 2);
2337 s16 id = readS16(buf);
2338 is.read((char*)buf, 2);
2339 u16 textlen = readU16(buf);
2341 for(u16 i=0; i<textlen; i++)
2343 is.read((char*)buf, 1);
2344 text += (char)buf[0];
2347 MapBlock *block = NULL;
2350 block = m_env.getMap().getBlockNoCreate(blockpos);
2352 catch(InvalidPositionException &e)
2354 derr_server<<"Error while setting sign text: "
2355 "block not found"<<std::endl;
2359 MapBlockObject *obj = block->getObject(id);
2362 derr_server<<"Error while setting sign text: "
2363 "object not found"<<std::endl;
2367 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2369 derr_server<<"Error while setting sign text: "
2370 "object is not a sign"<<std::endl;
2374 ((SignObject*)obj)->setText(text);
2376 obj->getBlock()->setChangedFlag();
2378 else if(command == TOSERVER_INVENTORY_ACTION)
2380 /*// Ignore inventory changes if in creative mode
2381 if(g_settings.getBool("creative_mode") == true)
2383 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2387 // Strip command and create a stream
2388 std::string datastring((char*)&data[2], datasize-2);
2389 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2390 std::istringstream is(datastring, std::ios_base::binary);
2392 InventoryAction *a = InventoryAction::deSerialize(is);
2396 Handle craftresult specially if not in creative mode
2398 bool disable_action = false;
2399 if(a->getType() == IACTION_MOVE
2400 && g_settings.getBool("creative_mode") == false)
2402 IMoveAction *ma = (IMoveAction*)a;
2403 // Don't allow moving anything to craftresult
2404 if(ma->to_name == "craftresult")
2407 disable_action = true;
2409 // When something is removed from craftresult
2410 if(ma->from_name == "craftresult")
2412 disable_action = true;
2413 // Remove stuff from craft
2414 InventoryList *clist = player->inventory.getList("craft");
2417 u16 count = ma->count;
2420 clist->decrementMaterials(count);
2423 // Feed action to player inventory
2424 a->apply(&player->inventory);
2427 // If something appeared in craftresult, throw it
2429 InventoryList *rlist = player->inventory.getList("craftresult");
2430 InventoryList *mlist = player->inventory.getList("main");
2431 if(rlist && mlist && rlist->getUsedSlots() == 1)
2433 InventoryItem *item1 = rlist->changeItem(0, NULL);
2434 mlist->addItem(item1);
2438 if(disable_action == false)
2440 // Feed action to player inventory
2441 a->apply(&player->inventory);
2446 SendInventory(player->peer_id);
2450 dstream<<"TOSERVER_INVENTORY_ACTION: "
2451 <<"InventoryAction::deSerialize() returned NULL"
2455 else if(command == TOSERVER_CHAT_MESSAGE)
2463 std::string datastring((char*)&data[2], datasize-2);
2464 std::istringstream is(datastring, std::ios_base::binary);
2467 is.read((char*)buf, 2);
2468 u16 len = readU16(buf);
2470 std::wstring message;
2471 for(u16 i=0; i<len; i++)
2473 is.read((char*)buf, 2);
2474 message += (wchar_t)readU16(buf);
2477 // Get player name of this client
2478 std::wstring name = narrow_to_wide(player->getName());
2480 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2482 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2485 Send the message to all other clients
2487 for(core::map<u16, RemoteClient*>::Iterator
2488 i = m_clients.getIterator();
2489 i.atEnd() == false; i++)
2491 // Get client and check that it is valid
2492 RemoteClient *client = i.getNode()->getValue();
2493 assert(client->peer_id == i.getNode()->getKey());
2494 if(client->serialization_version == SER_FMT_VER_INVALID)
2497 // Don't send if it's the same one
2498 if(peer_id == client->peer_id)
2501 SendChatMessage(client->peer_id, line);
2506 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2507 "unknown command "<<command<<std::endl;
2511 catch(SendFailedException &e)
2513 derr_server<<"Server::ProcessData(): SendFailedException: "
2519 /*void Server::Send(u16 peer_id, u16 channelnum,
2520 SharedBuffer<u8> data, bool reliable)
2522 JMutexAutoLock lock(m_con_mutex);
2523 m_con.Send(peer_id, channelnum, data, reliable);
2526 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2528 DSTACK(__FUNCTION_NAME);
2530 Create a packet with the block in the right format
2533 std::ostringstream os(std::ios_base::binary);
2534 block->serialize(os, ver);
2535 std::string s = os.str();
2536 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2538 u32 replysize = 8 + blockdata.getSize();
2539 SharedBuffer<u8> reply(replysize);
2540 v3s16 p = block->getPos();
2541 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2542 writeS16(&reply[2], p.X);
2543 writeS16(&reply[4], p.Y);
2544 writeS16(&reply[6], p.Z);
2545 memcpy(&reply[8], *blockdata, blockdata.getSize());
2547 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2548 <<": \tpacket size: "<<replysize<<std::endl;*/
2553 m_con.Send(peer_id, 1, reply, true);
2556 core::list<PlayerInfo> Server::getPlayerInfo()
2558 DSTACK(__FUNCTION_NAME);
2559 JMutexAutoLock envlock(m_env_mutex);
2560 JMutexAutoLock conlock(m_con_mutex);
2562 core::list<PlayerInfo> list;
2564 core::list<Player*> players = m_env.getPlayers();
2566 core::list<Player*>::Iterator i;
2567 for(i = players.begin();
2568 i != players.end(); i++)
2572 Player *player = *i;
2575 con::Peer *peer = m_con.GetPeer(player->peer_id);
2576 // Copy info from peer to info struct
2578 info.address = peer->address;
2579 info.avg_rtt = peer->avg_rtt;
2581 catch(con::PeerNotFoundException &e)
2583 // Set dummy peer info
2585 info.address = Address(0,0,0,0,0);
2589 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2590 info.position = player->getPosition();
2592 list.push_back(info);
2598 void Server::peerAdded(con::Peer *peer)
2600 DSTACK(__FUNCTION_NAME);
2601 dout_server<<"Server::peerAdded(): peer->id="
2602 <<peer->id<<std::endl;
2605 c.type = PEER_ADDED;
2606 c.peer_id = peer->id;
2608 m_peer_change_queue.push_back(c);
2611 void Server::deletingPeer(con::Peer *peer, bool timeout)
2613 DSTACK(__FUNCTION_NAME);
2614 dout_server<<"Server::deletingPeer(): peer->id="
2615 <<peer->id<<", timeout="<<timeout<<std::endl;
2618 c.type = PEER_REMOVED;
2619 c.peer_id = peer->id;
2620 c.timeout = timeout;
2621 m_peer_change_queue.push_back(c);
2624 void Server::SendObjectData(float dtime)
2626 DSTACK(__FUNCTION_NAME);
2628 core::map<v3s16, bool> stepped_blocks;
2630 for(core::map<u16, RemoteClient*>::Iterator
2631 i = m_clients.getIterator();
2632 i.atEnd() == false; i++)
2634 u16 peer_id = i.getNode()->getKey();
2635 RemoteClient *client = i.getNode()->getValue();
2636 assert(client->peer_id == peer_id);
2638 if(client->serialization_version == SER_FMT_VER_INVALID)
2641 client->SendObjectData(this, dtime, stepped_blocks);
2645 void Server::SendPlayerInfos()
2647 DSTACK(__FUNCTION_NAME);
2649 //JMutexAutoLock envlock(m_env_mutex);
2651 // Get connected players
2652 core::list<Player*> players = m_env.getPlayers(true);
2654 u32 player_count = players.getSize();
2655 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2657 SharedBuffer<u8> data(datasize);
2658 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2661 core::list<Player*>::Iterator i;
2662 for(i = players.begin();
2663 i != players.end(); i++)
2665 Player *player = *i;
2667 /*dstream<<"Server sending player info for player with "
2668 "peer_id="<<player->peer_id<<std::endl;*/
2670 writeU16(&data[start], player->peer_id);
2671 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2672 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2673 start += 2+PLAYERNAME_SIZE;
2676 //JMutexAutoLock conlock(m_con_mutex);
2679 m_con.SendToAll(0, data, true);
2697 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2703 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2709 enum ItemSpecType type;
2710 // Only other one of these is used
2716 items: a pointer to an array of 9 pointers to items
2717 specs: a pointer to an array of 9 ItemSpecs
2719 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2721 u16 items_min_x = 100;
2722 u16 items_max_x = 100;
2723 u16 items_min_y = 100;
2724 u16 items_max_y = 100;
2725 for(u16 y=0; y<3; y++)
2726 for(u16 x=0; x<3; x++)
2728 if(items[y*3 + x] == NULL)
2730 if(items_min_x == 100 || x < items_min_x)
2732 if(items_min_y == 100 || y < items_min_y)
2734 if(items_max_x == 100 || x > items_max_x)
2736 if(items_max_y == 100 || y > items_max_y)
2739 // No items at all, just return false
2740 if(items_min_x == 100)
2743 u16 items_w = items_max_x - items_min_x + 1;
2744 u16 items_h = items_max_y - items_min_y + 1;
2746 u16 specs_min_x = 100;
2747 u16 specs_max_x = 100;
2748 u16 specs_min_y = 100;
2749 u16 specs_max_y = 100;
2750 for(u16 y=0; y<3; y++)
2751 for(u16 x=0; x<3; x++)
2753 if(specs[y*3 + x].type == ITEM_NONE)
2755 if(specs_min_x == 100 || x < specs_min_x)
2757 if(specs_min_y == 100 || y < specs_min_y)
2759 if(specs_max_x == 100 || x > specs_max_x)
2761 if(specs_max_y == 100 || y > specs_max_y)
2764 // No specs at all, just return false
2765 if(specs_min_x == 100)
2768 u16 specs_w = specs_max_x - specs_min_x + 1;
2769 u16 specs_h = specs_max_y - specs_min_y + 1;
2772 if(items_w != specs_w || items_h != specs_h)
2775 for(u16 y=0; y<specs_h; y++)
2776 for(u16 x=0; x<specs_w; x++)
2778 u16 items_x = items_min_x + x;
2779 u16 items_y = items_min_y + y;
2780 u16 specs_x = specs_min_x + x;
2781 u16 specs_y = specs_min_y + y;
2782 InventoryItem *item = items[items_y * 3 + items_x];
2783 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2785 if(spec.type == ITEM_NONE)
2787 // Has to be no item
2793 // There should be an item
2797 std::string itemname = item->getName();
2799 if(spec.type == ITEM_MATERIAL)
2801 if(itemname != "MaterialItem")
2803 MaterialItem *mitem = (MaterialItem*)item;
2804 if(mitem->getMaterial() != spec.num)
2807 else if(spec.type == ITEM_CRAFT)
2809 if(itemname != "CraftItem")
2811 CraftItem *mitem = (CraftItem*)item;
2812 if(mitem->getSubName() != spec.name)
2815 else if(spec.type == ITEM_TOOL)
2817 // Not supported yet
2820 else if(spec.type == ITEM_MBO)
2822 // Not supported yet
2827 // Not supported yet
2835 void Server::SendInventory(u16 peer_id)
2837 DSTACK(__FUNCTION_NAME);
2839 Player* player = m_env.getPlayer(peer_id);
2842 Calculate crafting stuff
2844 if(g_settings.getBool("creative_mode") == false)
2846 InventoryList *clist = player->inventory.getList("craft");
2847 InventoryList *rlist = player->inventory.getList("craftresult");
2850 rlist->clearItems();
2854 InventoryItem *items[9];
2855 for(u16 i=0; i<9; i++)
2857 items[i] = clist->getItem(i);
2866 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2867 if(checkItemCombination(items, specs))
2869 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2878 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2879 if(checkItemCombination(items, specs))
2881 rlist->addItem(new CraftItem("Stick", 4));
2890 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2891 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2892 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2893 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2894 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2895 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2896 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2897 if(checkItemCombination(items, specs))
2899 rlist->addItem(new MapBlockObjectItem("Sign"));
2908 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2909 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2910 if(checkItemCombination(items, specs))
2912 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2921 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2922 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2923 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2924 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2925 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2926 if(checkItemCombination(items, specs))
2928 rlist->addItem(new ToolItem("WPick", 0));
2937 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2938 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2939 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2940 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2941 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2942 if(checkItemCombination(items, specs))
2944 rlist->addItem(new ToolItem("STPick", 0));
2953 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2954 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2955 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2956 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2957 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2958 if(checkItemCombination(items, specs))
2960 rlist->addItem(new ToolItem("MesePick", 0));
2965 } // if creative_mode == false
2971 std::ostringstream os;
2972 //os.imbue(std::locale("C"));
2974 player->inventory.serialize(os);
2976 std::string s = os.str();
2978 SharedBuffer<u8> data(s.size()+2);
2979 writeU16(&data[0], TOCLIENT_INVENTORY);
2980 memcpy(&data[2], s.c_str(), s.size());
2983 m_con.Send(peer_id, 0, data, true);
2986 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2988 DSTACK(__FUNCTION_NAME);
2990 std::ostringstream os(std::ios_base::binary);
2994 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2995 os.write((char*)buf, 2);
2998 writeU16(buf, message.size());
2999 os.write((char*)buf, 2);
3002 for(u32 i=0; i<message.size(); i++)
3006 os.write((char*)buf, 2);
3010 std::string s = os.str();
3011 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3013 m_con.Send(peer_id, 0, data, true);
3016 void Server::BroadcastChatMessage(const std::wstring &message)
3018 for(core::map<u16, RemoteClient*>::Iterator
3019 i = m_clients.getIterator();
3020 i.atEnd() == false; i++)
3022 // Get client and check that it is valid
3023 RemoteClient *client = i.getNode()->getValue();
3024 assert(client->peer_id == i.getNode()->getKey());
3025 if(client->serialization_version == SER_FMT_VER_INVALID)
3028 SendChatMessage(client->peer_id, message);
3032 void Server::SendBlocks(float dtime)
3034 DSTACK(__FUNCTION_NAME);
3036 JMutexAutoLock envlock(m_env_mutex);
3038 core::array<PrioritySortedBlockTransfer> queue;
3040 s32 total_sending = 0;
3042 for(core::map<u16, RemoteClient*>::Iterator
3043 i = m_clients.getIterator();
3044 i.atEnd() == false; i++)
3046 RemoteClient *client = i.getNode()->getValue();
3047 assert(client->peer_id == i.getNode()->getKey());
3049 total_sending += client->SendingCount();
3051 if(client->serialization_version == SER_FMT_VER_INVALID)
3054 client->GetNextBlocks(this, dtime, queue);
3058 // Lowest priority number comes first.
3059 // Lowest is most important.
3062 JMutexAutoLock conlock(m_con_mutex);
3064 for(u32 i=0; i<queue.size(); i++)
3066 //TODO: Calculate limit dynamically
3067 if(total_sending >= g_settings.getS32
3068 ("max_simultaneous_block_sends_server_total"))
3071 PrioritySortedBlockTransfer q = queue[i];
3073 MapBlock *block = NULL;
3076 block = m_env.getMap().getBlockNoCreate(q.pos);
3078 catch(InvalidPositionException &e)
3083 RemoteClient *client = getClient(q.peer_id);
3085 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3087 client->SentBlock(q.pos);
3094 RemoteClient* Server::getClient(u16 peer_id)
3096 DSTACK(__FUNCTION_NAME);
3097 //JMutexAutoLock lock(m_con_mutex);
3098 core::map<u16, RemoteClient*>::Node *n;
3099 n = m_clients.find(peer_id);
3100 // A client should exist for all peers
3102 return n->getValue();
3105 void setCreativeInventory(Player *player)
3107 player->resetInventory();
3109 // Give some good picks
3111 InventoryItem *item = new ToolItem("STPick", 0);
3112 void* r = player->inventory.addItem("main", item);
3116 InventoryItem *item = new ToolItem("MesePick", 0);
3117 void* r = player->inventory.addItem("main", item);
3124 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3127 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3128 player->inventory.addItem("main", item);
3131 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3133 // Skip some materials
3134 if(i == CONTENT_WATER || i == CONTENT_TORCH
3135 || i == CONTENT_COALSTONE)
3138 InventoryItem *item = new MaterialItem(i, 1);
3139 player->inventory.addItem("main", item);
3143 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3144 void* r = player->inventory.addItem("main", item);
3149 Player *Server::emergePlayer(const char *name, const char *password,
3153 Try to get an existing player
3155 Player *player = m_env.getPlayer(name);
3158 // If player is already connected, cancel
3159 if(player->peer_id != 0)
3161 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3166 player->peer_id = peer_id;
3168 // Reset inventory to creative if in creative mode
3169 if(g_settings.getBool("creative_mode"))
3171 setCreativeInventory(player);
3178 If player with the wanted peer_id already exists, cancel.
3180 if(m_env.getPlayer(peer_id) != NULL)
3182 dstream<<"emergePlayer(): Player with wrong name but same"
3183 " peer_id already exists"<<std::endl;
3191 player = new ServerRemotePlayer();
3192 //player->peer_id = c.peer_id;
3193 //player->peer_id = PEER_ID_INEXISTENT;
3194 player->peer_id = peer_id;
3195 player->updateName(name);
3201 dstream<<"Server: Finding spawn place for player \""
3202 <<player->getName()<<"\""<<std::endl;
3206 player->setPosition(intToFloat(v3s16(
3213 f32 groundheight = 0;
3215 // Try to find a good place a few times
3216 for(s32 i=0; i<500; i++)
3219 // We're going to try to throw the player to this position
3220 nodepos = v2s16(-range + (myrand()%(range*2)),
3221 -range + (myrand()%(range*2)));
3222 v2s16 sectorpos = getNodeSectorPos(nodepos);
3224 m_env.getMap().emergeSector(sectorpos);
3225 // Get ground height at point
3226 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3227 // The sector should have been generated -> groundheight exists
3228 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3229 // Don't go underwater
3230 if(groundheight < WATER_LEVEL)
3232 //dstream<<"-> Underwater"<<std::endl;
3235 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3236 // Get block at point
3238 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3239 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3240 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3241 // Don't go inside ground
3243 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3244 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3245 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3246 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3247 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3248 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3250 dstream<<"-> Inside ground"<<std::endl;
3254 }catch(InvalidPositionException &e)
3256 dstream<<"-> Invalid position"<<std::endl;
3257 // Ignore invalid position
3261 // Found a good place
3262 dstream<<"Searched through "<<i<<" places."<<std::endl;
3267 // If no suitable place was not found, go above water at least.
3268 if(groundheight < WATER_LEVEL)
3269 groundheight = WATER_LEVEL;
3271 player->setPosition(intToFloat(v3s16(
3280 Add player to environment
3283 m_env.addPlayer(player);
3286 Add stuff to inventory
3289 if(g_settings.getBool("creative_mode"))
3291 setCreativeInventory(player);
3296 InventoryItem *item = new ToolItem("WPick", 32000);
3297 void* r = player->inventory.addItem("main", item);
3301 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3302 void* r = player->inventory.addItem("main", item);
3306 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3307 void* r = player->inventory.addItem("main", item);
3311 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3312 void* r = player->inventory.addItem("main", item);
3316 InventoryItem *item = new CraftItem("Stick", 4);
3317 void* r = player->inventory.addItem("main", item);
3321 InventoryItem *item = new ToolItem("WPick", 32000);
3322 void* r = player->inventory.addItem("main", item);
3326 InventoryItem *item = new ToolItem("STPick", 32000);
3327 void* r = player->inventory.addItem("main", item);
3330 /*// Give some lights
3332 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3333 bool r = player->inventory.addItem("main", item);
3337 for(u16 i=0; i<4; i++)
3339 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3340 bool r = player->inventory.addItem("main", item);
3343 /*// Give some other stuff
3345 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3346 bool r = player->inventory.addItem("main", item);
3353 } // create new player
3357 void Server::UpdateBlockWaterPressure(MapBlock *block,
3358 core::map<v3s16, MapBlock*> &modified_blocks)
3360 MapVoxelManipulator v(&m_env.getMap());
3361 v.m_disable_water_climb =
3362 g_settings.getBool("disable_water_climb");
3364 VoxelArea area(block->getPosRelative(),
3365 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3369 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3371 catch(ProcessingLimitException &e)
3373 dstream<<"Processing limit reached (1)"<<std::endl;
3376 v.blitBack(modified_blocks);
3380 void Server::handlePeerChange(PeerChange &c)
3382 JMutexAutoLock envlock(m_env_mutex);
3383 JMutexAutoLock conlock(m_con_mutex);
3385 if(c.type == PEER_ADDED)
3392 core::map<u16, RemoteClient*>::Node *n;
3393 n = m_clients.find(c.peer_id);
3394 // The client shouldn't already exist
3398 RemoteClient *client = new RemoteClient();
3399 client->peer_id = c.peer_id;
3400 m_clients.insert(client->peer_id, client);
3403 else if(c.type == PEER_REMOVED)
3410 core::map<u16, RemoteClient*>::Node *n;
3411 n = m_clients.find(c.peer_id);
3412 // The client should exist
3415 // Collect information about leaving in chat
3416 std::wstring message;
3418 std::wstring name = L"unknown";
3419 Player *player = m_env.getPlayer(c.peer_id);
3421 name = narrow_to_wide(player->getName());
3425 message += L" left game";
3427 message += L" (timed out)";
3432 m_env.removePlayer(c.peer_id);
3435 // Set player client disconnected
3437 Player *player = m_env.getPlayer(c.peer_id);
3439 player->peer_id = 0;
3443 delete m_clients[c.peer_id];
3444 m_clients.remove(c.peer_id);
3446 // Send player info to all remaining clients
3449 // Send leave chat message to all remaining clients
3450 BroadcastChatMessage(message);
3459 void Server::handlePeerChanges()
3461 while(m_peer_change_queue.size() > 0)
3463 PeerChange c = m_peer_change_queue.pop_front();
3465 dout_server<<"Server: Handling peer change: "
3466 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3469 handlePeerChange(c);
3473 void dedicated_server_loop(Server &server)
3475 DSTACK(__FUNCTION_NAME);
3477 std::cout<<std::endl;
3478 std::cout<<"========================"<<std::endl;
3479 std::cout<<"Running dedicated server"<<std::endl;
3480 std::cout<<"========================"<<std::endl;
3481 std::cout<<std::endl;
3485 // This is kind of a hack but can be done like this
3486 // because server.step() is very light
3490 static int counter = 0;
3496 core::list<PlayerInfo> list = server.getPlayerInfo();
3497 core::list<PlayerInfo>::Iterator i;
3498 static u32 sum_old = 0;
3499 u32 sum = PIChecksum(list);
3502 std::cout<<DTIME<<"Player info:"<<std::endl;
3503 for(i=list.begin(); i!=list.end(); i++)
3505 i->PrintLine(&std::cout);