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);
171 // If it is a dummy, block was not found on disk
174 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
177 if(only_from_disk == false)
179 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
184 catch(InvalidPositionException &e)
187 // This happens when position is over limit.
193 if(debug && changed_blocks.size() > 0)
195 dout_server<<DTIME<<"Got changed_blocks: ";
196 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
197 i.atEnd() == false; i++)
199 MapBlock *block = i.getNode()->getValue();
200 v3s16 p = block->getPos();
201 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
203 dout_server<<std::endl;
207 Collect a list of blocks that have been modified in
208 addition to the fetched one.
211 // Add all the "changed blocks" to modified_blocks
212 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
213 i.atEnd() == false; i++)
215 MapBlock *block = i.getNode()->getValue();
216 modified_blocks.insert(block->getPos(), block);
219 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
220 <<" blocks"<<std::endl;*/
222 //TimeTaker timer("** updateLighting");
224 // Update lighting without locking the environment mutex,
225 // add modified blocks to changed blocks
226 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
228 // If we got no block, there should be no invalidated blocks
231 assert(lighting_invalidated_blocks.size() == 0);
237 Set sent status of modified blocks on clients
240 // NOTE: Server's clients are also behind the connection mutex
241 JMutexAutoLock lock(m_server->m_con_mutex);
244 Add the originally fetched block to the modified list
248 modified_blocks.insert(p, block);
252 Set the modified blocks unsent for all the clients
255 for(core::map<u16, RemoteClient*>::Iterator
256 i = m_server->m_clients.getIterator();
257 i.atEnd() == false; i++)
259 RemoteClient *client = i.getNode()->getValue();
261 if(modified_blocks.size() > 0)
263 // Remove block from sent history
264 client->SetBlocksNotSent(modified_blocks);
270 END_DEBUG_EXCEPTION_HANDLER
275 void RemoteClient::GetNextBlocks(Server *server, float dtime,
276 core::array<PrioritySortedBlockTransfer> &dest)
278 DSTACK(__FUNCTION_NAME);
282 JMutexAutoLock lock(m_blocks_sent_mutex);
283 m_nearest_unsent_reset_timer += dtime;
286 // Won't send anything if already sending
288 JMutexAutoLock lock(m_blocks_sending_mutex);
290 if(m_blocks_sending.size() >= g_settings.getU16
291 ("max_simultaneous_block_sends_per_client"))
293 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
298 Player *player = server->m_env.getPlayer(peer_id);
300 assert(player != NULL);
302 v3f playerpos = player->getPosition();
303 v3f playerspeed = player->getSpeed();
305 v3s16 center_nodepos = floatToInt(playerpos);
307 v3s16 center = getNodeBlockPos(center_nodepos);
309 // Camera position and direction
311 playerpos + v3f(0, BS+BS/2, 0);
312 v3f camera_dir = v3f(0,0,1);
313 camera_dir.rotateYZBy(player->getPitch());
314 camera_dir.rotateXZBy(player->getYaw());
317 Get the starting value of the block finder radius.
319 s16 last_nearest_unsent_d;
322 JMutexAutoLock lock(m_blocks_sent_mutex);
324 if(m_last_center != center)
326 m_nearest_unsent_d = 0;
327 m_last_center = center;
330 /*dstream<<"m_nearest_unsent_reset_timer="
331 <<m_nearest_unsent_reset_timer<<std::endl;*/
332 if(m_nearest_unsent_reset_timer > 5.0)
334 m_nearest_unsent_reset_timer = 0;
335 m_nearest_unsent_d = 0;
336 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
339 last_nearest_unsent_d = m_nearest_unsent_d;
341 d_start = m_nearest_unsent_d;
344 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
345 ("max_simultaneous_block_sends_per_client");
346 u16 maximum_simultaneous_block_sends =
347 maximum_simultaneous_block_sends_setting;
350 Check the time from last addNode/removeNode.
352 Decrease send rate if player is building stuff.
355 SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
356 m_time_from_building.m_value += dtime;
357 /*if(m_time_from_building.m_value
358 < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
359 if(m_time_from_building.m_value < g_settings.getFloat(
360 "full_block_send_enable_min_time_from_building"))
362 maximum_simultaneous_block_sends
363 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
367 u32 num_blocks_selected;
369 JMutexAutoLock lock(m_blocks_sending_mutex);
370 num_blocks_selected = m_blocks_sending.size();
374 next time d will be continued from the d from which the nearest
375 unsent block was found this time.
377 This is because not necessarily any of the blocks found this
378 time are actually sent.
380 s32 new_nearest_unsent_d = -1;
382 // Serialization version used
383 //u8 ser_version = serialization_version;
385 //bool has_incomplete_blocks = false;
387 s16 d_max = g_settings.getS16("max_block_send_distance");
388 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
390 //dstream<<"Starting from "<<d_start<<std::endl;
392 for(s16 d = d_start; d <= d_max; d++)
394 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
396 //if(has_incomplete_blocks == false)
398 JMutexAutoLock lock(m_blocks_sent_mutex);
400 If m_nearest_unsent_d was changed by the EmergeThread
401 (it can change it to 0 through SetBlockNotSent),
403 Else update m_nearest_unsent_d
405 if(m_nearest_unsent_d != last_nearest_unsent_d)
407 d = m_nearest_unsent_d;
408 last_nearest_unsent_d = m_nearest_unsent_d;
413 Get the border/face dot coordinates of a "d-radiused"
416 core::list<v3s16> list;
417 getFacePositions(list, d);
419 core::list<v3s16>::Iterator li;
420 for(li=list.begin(); li!=list.end(); li++)
422 v3s16 p = *li + center;
426 - Don't allow too many simultaneous transfers
427 - EXCEPT when the blocks are very close
429 Also, don't send blocks that are already flying.
432 u16 maximum_simultaneous_block_sends_now =
433 maximum_simultaneous_block_sends;
435 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
437 maximum_simultaneous_block_sends_now =
438 maximum_simultaneous_block_sends_setting;
442 JMutexAutoLock lock(m_blocks_sending_mutex);
444 // Limit is dynamically lowered when building
445 if(num_blocks_selected
446 >= maximum_simultaneous_block_sends_now)
448 /*dstream<<"Not sending more blocks. Queue full. "
449 <<m_blocks_sending.size()
454 if(m_blocks_sending.find(p) != NULL)
461 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
462 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
463 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
469 // If this is true, inexistent block will be made from scratch
470 bool generate = d <= d_max_gen;
473 /*// Limit the generating area vertically to 2/3
474 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
477 // Limit the send area vertically to 2/3
478 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
484 If block is far away, don't generate it unless it is
487 NOTE: We can't know the ground level this way with the
493 MapSector *sector = NULL;
496 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
498 catch(InvalidPositionException &e)
504 // Get center ground height in nodes
505 f32 gh = sector->getGroundHeight(
506 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
507 // Block center y in nodes
508 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
509 // If differs a lot, don't generate
510 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
517 Don't draw if not in sight
520 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
526 Don't send already sent blocks
529 JMutexAutoLock lock(m_blocks_sent_mutex);
531 if(m_blocks_sent.find(p) != NULL)
536 Check if map has this block
538 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
540 bool surely_not_found_on_disk = false;
541 bool block_is_invalid = false;
544 /*if(block->isIncomplete())
546 has_incomplete_blocks = true;
552 surely_not_found_on_disk = true;
555 if(block->isValid() == false)
557 block_is_invalid = true;
561 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
562 v2s16 chunkpos = map->sector_to_chunk(p2d);
563 if(map->chunkNonVolatile(chunkpos) == false)
564 block_is_invalid = true;
565 /*MapChunk *chunk = map->getChunk(chunkpos);
567 block_is_invalid = true;
568 else if(chunk->getIsVolatile() == true)
569 block_is_invalid = true;*/
573 If block has been marked to not exist on disk (dummy)
574 and generating new ones is not wanted, skip block.
576 if(generate == false && surely_not_found_on_disk == true)
583 Record the lowest d from which a a block has been
584 found being not sent and possibly to exist
586 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
588 new_nearest_unsent_d = d;
592 Add inexistent block to emerge queue.
594 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
596 //dstream<<"asd"<<std::endl;
598 /*SharedPtr<JMutexAutoLock> lock
599 (m_num_blocks_in_emerge_queue.getLock());*/
601 //TODO: Get value from somewhere
602 // Allow only one block in emerge queue
603 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
605 //dstream<<"Adding block to emerge queue"<<std::endl;
607 // Add it to the emerge queue and trigger the thread
610 if(generate == false)
611 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
613 server->m_emerge_queue.addBlock(peer_id, p, flags);
614 server->m_emergethread.trigger();
625 PrioritySortedBlockTransfer q((float)d, p, peer_id);
629 num_blocks_selected += 1;
634 if(new_nearest_unsent_d != -1)
636 JMutexAutoLock lock(m_blocks_sent_mutex);
637 m_nearest_unsent_d = new_nearest_unsent_d;
641 void RemoteClient::SendObjectData(
644 core::map<v3s16, bool> &stepped_blocks
647 DSTACK(__FUNCTION_NAME);
649 // Can't send anything without knowing version
650 if(serialization_version == SER_FMT_VER_INVALID)
652 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
658 Send a TOCLIENT_OBJECTDATA packet.
662 u16 number of player positions
673 std::ostringstream os(std::ios_base::binary);
677 writeU16(buf, TOCLIENT_OBJECTDATA);
678 os.write((char*)buf, 2);
681 Get and write player data
684 // Get connected players
685 core::list<Player*> players = server->m_env.getPlayers(true);
687 // Write player count
688 u16 playercount = players.size();
689 writeU16(buf, playercount);
690 os.write((char*)buf, 2);
692 core::list<Player*>::Iterator i;
693 for(i = players.begin();
694 i != players.end(); i++)
698 v3f pf = player->getPosition();
699 v3f sf = player->getSpeed();
701 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
702 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
703 s32 pitch_i (player->getPitch() * 100);
704 s32 yaw_i (player->getYaw() * 100);
706 writeU16(buf, player->peer_id);
707 os.write((char*)buf, 2);
708 writeV3S32(buf, position_i);
709 os.write((char*)buf, 12);
710 writeV3S32(buf, speed_i);
711 os.write((char*)buf, 12);
712 writeS32(buf, pitch_i);
713 os.write((char*)buf, 4);
714 writeS32(buf, yaw_i);
715 os.write((char*)buf, 4);
719 Get and write object data
725 For making players to be able to build to their nearby
726 environment (building is not possible on blocks that are not
729 - Add blocks to emerge queue if they are not found
731 SUGGESTION: These could be ignored from the backside of the player
734 Player *player = server->m_env.getPlayer(peer_id);
738 v3f playerpos = player->getPosition();
739 v3f playerspeed = player->getSpeed();
741 v3s16 center_nodepos = floatToInt(playerpos);
742 v3s16 center = getNodeBlockPos(center_nodepos);
744 s16 d_max = g_settings.getS16("active_object_range");
746 // Number of blocks whose objects were written to bos
749 std::ostringstream bos(std::ios_base::binary);
751 for(s16 d = 0; d <= d_max; d++)
753 core::list<v3s16> list;
754 getFacePositions(list, d);
756 core::list<v3s16>::Iterator li;
757 for(li=list.begin(); li!=list.end(); li++)
759 v3s16 p = *li + center;
762 Ignore blocks that haven't been sent to the client
765 JMutexAutoLock sentlock(m_blocks_sent_mutex);
766 if(m_blocks_sent.find(p) == NULL)
770 // Try stepping block and add it to a send queue
775 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
778 Step block if not in stepped_blocks and add to stepped_blocks.
780 if(stepped_blocks.find(p) == NULL)
782 block->stepObjects(dtime, true, server->getDayNightRatio());
783 stepped_blocks.insert(p, true);
784 block->setChangedFlag();
787 // Skip block if there are no objects
788 if(block->getObjectCount() == 0)
797 bos.write((char*)buf, 6);
800 block->serializeObjects(bos, serialization_version);
805 Stop collecting objects if data is already too big
807 // Sum of player and object data sizes
808 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
809 // break out if data too big
810 if(sum > MAX_OBJECTDATA_SIZE)
812 goto skip_subsequent;
816 catch(InvalidPositionException &e)
819 // Add it to the emerge queue and trigger the thread.
820 // Fetch the block only if it is on disk.
822 // Grab and increment counter
823 /*SharedPtr<JMutexAutoLock> lock
824 (m_num_blocks_in_emerge_queue.getLock());
825 m_num_blocks_in_emerge_queue.m_value++;*/
827 // Add to queue as an anonymous fetch from disk
828 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
829 server->m_emerge_queue.addBlock(0, p, flags);
830 server->m_emergethread.trigger();
838 writeU16(buf, blockcount);
839 os.write((char*)buf, 2);
841 // Write block objects
848 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
851 std::string s = os.str();
852 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
853 // Send as unreliable
854 server->m_con.Send(peer_id, 0, data, false);
857 void RemoteClient::GotBlock(v3s16 p)
859 JMutexAutoLock lock(m_blocks_sending_mutex);
860 JMutexAutoLock lock2(m_blocks_sent_mutex);
861 if(m_blocks_sending.find(p) != NULL)
862 m_blocks_sending.remove(p);
865 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
866 " m_blocks_sending"<<std::endl;*/
867 m_excess_gotblocks++;
869 m_blocks_sent.insert(p, true);
872 void RemoteClient::SentBlock(v3s16 p)
874 JMutexAutoLock lock(m_blocks_sending_mutex);
875 /*if(m_blocks_sending.size() > 15)
877 dstream<<"RemoteClient::SentBlock(): "
878 <<"m_blocks_sending.size()="
879 <<m_blocks_sending.size()<<std::endl;
881 if(m_blocks_sending.find(p) == NULL)
882 m_blocks_sending.insert(p, 0.0);
884 dstream<<"RemoteClient::SentBlock(): Sent block"
885 " already in m_blocks_sending"<<std::endl;
888 void RemoteClient::SetBlockNotSent(v3s16 p)
890 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
891 JMutexAutoLock sentlock(m_blocks_sent_mutex);
893 m_nearest_unsent_d = 0;
895 if(m_blocks_sending.find(p) != NULL)
896 m_blocks_sending.remove(p);
897 if(m_blocks_sent.find(p) != NULL)
898 m_blocks_sent.remove(p);
901 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
903 JMutexAutoLock sendinglock(m_blocks_sending_mutex);
904 JMutexAutoLock sentlock(m_blocks_sent_mutex);
906 m_nearest_unsent_d = 0;
908 for(core::map<v3s16, MapBlock*>::Iterator
909 i = blocks.getIterator();
910 i.atEnd()==false; i++)
912 v3s16 p = i.getNode()->getKey();
914 if(m_blocks_sending.find(p) != NULL)
915 m_blocks_sending.remove(p);
916 if(m_blocks_sent.find(p) != NULL)
917 m_blocks_sent.remove(p);
925 PlayerInfo::PlayerInfo()
930 void PlayerInfo::PrintLine(std::ostream *s)
933 (*s)<<"\""<<name<<"\" ("
934 <<(position.X/10)<<","<<(position.Y/10)
935 <<","<<(position.Z/10)<<") ";
937 (*s)<<" avg_rtt="<<avg_rtt;
941 u32 PIChecksum(core::list<PlayerInfo> &l)
943 core::list<PlayerInfo>::Iterator i;
946 for(i=l.begin(); i!=l.end(); i++)
948 checksum += a * (i->id+1);
949 checksum ^= 0x435aafcd;
960 std::string mapsavedir
962 m_env(new ServerMap(mapsavedir), dout_server),
963 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
965 m_emergethread(this),
968 m_time_of_day_send_timer(0),
970 m_mapsavedir(mapsavedir)
972 //m_flowwater_timer = 0.0;
973 m_liquid_transform_timer = 0.0;
974 m_print_info_timer = 0.0;
975 m_objectdata_timer = 0.0;
976 m_emergethread_trigger_timer = 0.0;
977 m_savemap_timer = 0.0;
981 m_step_dtime_mutex.Init();
985 m_env.deSerializePlayers(m_mapsavedir);
991 m_env.serializePlayers(m_mapsavedir);
996 JMutexAutoLock clientslock(m_con_mutex);
998 for(core::map<u16, RemoteClient*>::Iterator
999 i = m_clients.getIterator();
1000 i.atEnd() == false; i++)
1003 // NOTE: These are removed by env destructor
1005 u16 peer_id = i.getNode()->getKey();
1006 JMutexAutoLock envlock(m_env_mutex);
1007 m_env.removePlayer(peer_id);
1011 delete i.getNode()->getValue();
1015 void Server::start(unsigned short port)
1017 DSTACK(__FUNCTION_NAME);
1018 // Stop thread if already running
1021 // Initialize connection
1022 m_con.setTimeoutMs(30);
1026 m_thread.setRun(true);
1029 dout_server<<"Server started on port "<<port<<std::endl;
1034 DSTACK(__FUNCTION_NAME);
1035 // Stop threads (set run=false first so both start stopping)
1036 m_thread.setRun(false);
1037 m_emergethread.setRun(false);
1039 m_emergethread.stop();
1041 dout_server<<"Server threads stopped"<<std::endl;
1044 void Server::step(float dtime)
1046 DSTACK(__FUNCTION_NAME);
1051 JMutexAutoLock lock(m_step_dtime_mutex);
1052 m_step_dtime += dtime;
1056 void Server::AsyncRunStep()
1058 DSTACK(__FUNCTION_NAME);
1062 JMutexAutoLock lock1(m_step_dtime_mutex);
1063 dtime = m_step_dtime;
1066 // Send blocks to clients
1072 //dstream<<"Server steps "<<dtime<<std::endl;
1073 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1076 JMutexAutoLock lock1(m_step_dtime_mutex);
1077 m_step_dtime -= dtime;
1084 m_uptime.set(m_uptime.get() + dtime);
1088 Update m_time_of_day
1091 m_time_counter += dtime;
1092 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1093 u32 units = (u32)(m_time_counter*speed);
1094 m_time_counter -= (f32)units / speed;
1095 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1097 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1100 Send to clients at constant intervals
1103 m_time_of_day_send_timer -= dtime;
1104 if(m_time_of_day_send_timer < 0.0)
1106 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1108 //JMutexAutoLock envlock(m_env_mutex);
1109 JMutexAutoLock conlock(m_con_mutex);
1111 for(core::map<u16, RemoteClient*>::Iterator
1112 i = m_clients.getIterator();
1113 i.atEnd() == false; i++)
1115 RemoteClient *client = i.getNode()->getValue();
1116 //Player *player = m_env.getPlayer(client->peer_id);
1118 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1119 m_time_of_day.get());
1121 m_con.Send(client->peer_id, 0, data, true);
1127 // Process connection's timeouts
1128 JMutexAutoLock lock2(m_con_mutex);
1129 m_con.RunTimeouts(dtime);
1133 // This has to be called so that the client list gets synced
1134 // with the peer list of the connection
1135 handlePeerChanges();
1140 // This also runs Map's timers
1141 JMutexAutoLock lock(m_env_mutex);
1152 m_liquid_transform_timer += dtime;
1153 if(m_liquid_transform_timer >= 1.00)
1155 m_liquid_transform_timer -= 1.00;
1157 JMutexAutoLock lock(m_env_mutex);
1159 core::map<v3s16, MapBlock*> modified_blocks;
1160 m_env.getMap().transformLiquids(modified_blocks);
1165 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1166 ServerMap &map = ((ServerMap&)m_env.getMap());
1167 map.updateLighting(modified_blocks, lighting_modified_blocks);
1169 // Add blocks modified by lighting to modified_blocks
1170 for(core::map<v3s16, MapBlock*>::Iterator
1171 i = lighting_modified_blocks.getIterator();
1172 i.atEnd() == false; i++)
1174 MapBlock *block = i.getNode()->getValue();
1175 modified_blocks.insert(block->getPos(), block);
1179 Set the modified blocks unsent for all the clients
1182 JMutexAutoLock lock2(m_con_mutex);
1184 for(core::map<u16, RemoteClient*>::Iterator
1185 i = m_clients.getIterator();
1186 i.atEnd() == false; i++)
1188 RemoteClient *client = i.getNode()->getValue();
1190 if(modified_blocks.size() > 0)
1192 // Remove block from sent history
1193 client->SetBlocksNotSent(modified_blocks);
1198 // Periodically print some info
1200 float &counter = m_print_info_timer;
1206 JMutexAutoLock lock2(m_con_mutex);
1208 for(core::map<u16, RemoteClient*>::Iterator
1209 i = m_clients.getIterator();
1210 i.atEnd() == false; i++)
1212 //u16 peer_id = i.getNode()->getKey();
1213 RemoteClient *client = i.getNode()->getValue();
1214 client->PrintInfo(std::cout);
1222 NOTE: Some of this could be moved to RemoteClient
1226 JMutexAutoLock envlock(m_env_mutex);
1227 JMutexAutoLock conlock(m_con_mutex);
1229 for(core::map<u16, RemoteClient*>::Iterator
1230 i = m_clients.getIterator();
1231 i.atEnd() == false; i++)
1233 RemoteClient *client = i.getNode()->getValue();
1234 Player *player = m_env.getPlayer(client->peer_id);
1236 JMutexAutoLock digmutex(client->m_dig_mutex);
1238 if(client->m_dig_tool_item == -1)
1241 client->m_dig_time_remaining -= dtime;
1243 if(client->m_dig_time_remaining > 0)
1245 client->m_time_from_building.set(0.0);
1249 v3s16 p_under = client->m_dig_position;
1251 // Mandatory parameter; actually used for nothing
1252 core::map<v3s16, MapBlock*> modified_blocks;
1258 // Get material at position
1259 material = m_env.getMap().getNode(p_under).d;
1260 // If it's not diggable, do nothing
1261 if(content_diggable(material) == false)
1263 derr_server<<"Server: Not finishing digging: Node not diggable"
1265 client->m_dig_tool_item = -1;
1269 catch(InvalidPositionException &e)
1271 derr_server<<"Server: Not finishing digging: Node not found"
1273 client->m_dig_tool_item = -1;
1279 SharedBuffer<u8> reply(replysize);
1280 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1281 writeS16(&reply[2], p_under.X);
1282 writeS16(&reply[4], p_under.Y);
1283 writeS16(&reply[6], p_under.Z);
1285 m_con.SendToAll(0, reply, true);
1287 if(g_settings.getBool("creative_mode") == false)
1289 // Add to inventory and send inventory
1290 InventoryItem *item = new MaterialItem(material, 1);
1291 player->inventory.addItem("main", item);
1292 SendInventory(player->peer_id);
1297 (this takes some time so it is done after the quick stuff)
1299 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
1305 // Update water pressure around modification
1306 // This also adds it to m_flow_active_nodes if appropriate
1308 MapVoxelManipulator v(&m_env.getMap());
1309 v.m_disable_water_climb =
1310 g_settings.getBool("disable_water_climb");
1312 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
1316 v.updateAreaWaterPressure(area, m_flow_active_nodes);
1318 catch(ProcessingLimitException &e)
1320 dstream<<"Processing limit reached (1)"<<std::endl;
1323 v.blitBack(modified_blocks);
1328 // Send object positions
1330 float &counter = m_objectdata_timer;
1332 if(counter >= g_settings.getFloat("objectdata_interval"))
1334 JMutexAutoLock lock1(m_env_mutex);
1335 JMutexAutoLock lock2(m_con_mutex);
1336 SendObjectData(counter);
1343 Trigger emergethread (it somehow gets to a non-triggered but
1344 bysy state sometimes)
1347 float &counter = m_emergethread_trigger_timer;
1353 m_emergethread.trigger();
1359 float &counter = m_savemap_timer;
1361 if(counter >= g_settings.getFloat("server_map_save_interval"))
1365 JMutexAutoLock lock(m_env_mutex);
1367 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1369 // Save only changed parts
1370 m_env.getMap().save(true);
1372 // Delete unused sectors
1373 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1374 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1375 if(deleted_count > 0)
1377 dout_server<<"Server: Unloaded "<<deleted_count
1378 <<" sectors from memory"<<std::endl;
1382 m_env.serializePlayers(m_mapsavedir);
1388 void Server::Receive()
1390 DSTACK(__FUNCTION_NAME);
1391 u32 data_maxsize = 10000;
1392 Buffer<u8> data(data_maxsize);
1397 JMutexAutoLock conlock(m_con_mutex);
1398 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1401 // This has to be called so that the client list gets synced
1402 // with the peer list of the connection
1403 handlePeerChanges();
1405 ProcessData(*data, datasize, peer_id);
1407 catch(con::InvalidIncomingDataException &e)
1409 derr_server<<"Server::Receive(): "
1410 "InvalidIncomingDataException: what()="
1411 <<e.what()<<std::endl;
1413 catch(con::PeerNotFoundException &e)
1415 //NOTE: This is not needed anymore
1417 // The peer has been disconnected.
1418 // Find the associated player and remove it.
1420 /*JMutexAutoLock envlock(m_env_mutex);
1422 dout_server<<"ServerThread: peer_id="<<peer_id
1423 <<" has apparently closed connection. "
1424 <<"Removing player."<<std::endl;
1426 m_env.removePlayer(peer_id);*/
1430 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1432 DSTACK(__FUNCTION_NAME);
1433 // Environment is locked first.
1434 JMutexAutoLock envlock(m_env_mutex);
1435 JMutexAutoLock conlock(m_con_mutex);
1439 peer = m_con.GetPeer(peer_id);
1441 catch(con::PeerNotFoundException &e)
1443 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1444 <<peer_id<<" not found"<<std::endl;
1448 //u8 peer_ser_ver = peer->serialization_version;
1449 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1457 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1459 if(command == TOSERVER_INIT)
1461 // [0] u16 TOSERVER_INIT
1462 // [2] u8 SER_FMT_VER_HIGHEST
1463 // [3] u8[20] player_name
1468 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1469 <<peer->id<<std::endl;
1471 // First byte after command is maximum supported
1472 // serialization version
1473 u8 client_max = data[2];
1474 u8 our_max = SER_FMT_VER_HIGHEST;
1475 // Use the highest version supported by both
1476 u8 deployed = core::min_(client_max, our_max);
1477 // If it's lower than the lowest supported, give up.
1478 if(deployed < SER_FMT_VER_LOWEST)
1479 deployed = SER_FMT_VER_INVALID;
1481 //peer->serialization_version = deployed;
1482 getClient(peer->id)->pending_serialization_version = deployed;
1484 if(deployed == SER_FMT_VER_INVALID)
1486 derr_server<<DTIME<<"Server: Cannot negotiate "
1487 "serialization version with peer "
1488 <<peer_id<<std::endl;
1497 const u32 playername_size = 20;
1498 char playername[playername_size];
1499 for(u32 i=0; i<playername_size-1; i++)
1501 playername[i] = data[3+i];
1503 playername[playername_size-1] = 0;
1506 Player *player = emergePlayer(playername, "", peer_id);
1507 //Player *player = m_env.getPlayer(peer_id);
1510 // DEBUG: Test serialization
1511 std::ostringstream test_os;
1512 player->serialize(test_os);
1513 dstream<<"Player serialization test: \""<<test_os.str()
1515 std::istringstream test_is(test_os.str());
1516 player->deSerialize(test_is);
1519 // If failed, cancel
1522 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1523 <<": failed to emerge player"<<std::endl;
1528 // If a client is already connected to the player, cancel
1529 if(player->peer_id != 0)
1531 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1532 <<" tried to connect to "
1533 "an already connected player (peer_id="
1534 <<player->peer_id<<")"<<std::endl;
1537 // Set client of player
1538 player->peer_id = peer_id;
1541 // Check if player doesn't exist
1543 throw con::InvalidIncomingDataException
1544 ("Server::ProcessData(): INIT: Player doesn't exist");
1546 /*// update name if it was supplied
1547 if(datasize >= 20+3)
1550 player->updateName((const char*)&data[3]);
1553 // Now answer with a TOCLIENT_INIT
1555 SharedBuffer<u8> reply(2+1+6);
1556 writeU16(&reply[0], TOCLIENT_INIT);
1557 writeU8(&reply[2], deployed);
1558 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0)));
1560 m_con.Send(peer_id, 0, reply, true);
1564 if(command == TOSERVER_INIT2)
1566 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1567 <<peer->id<<std::endl;
1570 getClient(peer->id)->serialization_version
1571 = getClient(peer->id)->pending_serialization_version;
1574 Send some initialization data
1577 // Send player info to all players
1580 // Send inventory to player
1581 SendInventory(peer->id);
1585 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1586 m_time_of_day.get());
1587 m_con.Send(peer->id, 0, data, true);
1590 // Send information about server to player in chat
1592 std::wostringstream os(std::ios_base::binary);
1595 os<<L"uptime="<<m_uptime.get();
1596 // Information about clients
1598 for(core::map<u16, RemoteClient*>::Iterator
1599 i = m_clients.getIterator();
1600 i.atEnd() == false; i++)
1602 // Get client and check that it is valid
1603 RemoteClient *client = i.getNode()->getValue();
1604 assert(client->peer_id == i.getNode()->getKey());
1605 if(client->serialization_version == SER_FMT_VER_INVALID)
1608 Player *player = m_env.getPlayer(client->peer_id);
1609 // Get name of player
1610 std::wstring name = L"unknown";
1612 name = narrow_to_wide(player->getName());
1613 // Add name to information string
1617 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
1618 os<<" WARNING: Map saving is disabled."<<std::endl;
1620 SendChatMessage(peer_id, os.str());
1623 // Send information about joining in chat
1625 std::wstring name = L"unknown";
1626 Player *player = m_env.getPlayer(peer_id);
1628 name = narrow_to_wide(player->getName());
1630 std::wstring message;
1633 message += L" joined game";
1634 BroadcastChatMessage(message);
1640 if(peer_ser_ver == SER_FMT_VER_INVALID)
1642 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1643 " serialization format invalid or not initialized."
1644 " Skipping incoming command="<<command<<std::endl;
1648 Player *player = m_env.getPlayer(peer_id);
1651 derr_server<<"Server::ProcessData(): Cancelling: "
1652 "No player for peer_id="<<peer_id
1656 if(command == TOSERVER_PLAYERPOS)
1658 if(datasize < 2+12+12+4+4)
1662 v3s32 ps = readV3S32(&data[start+2]);
1663 v3s32 ss = readV3S32(&data[start+2+12]);
1664 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1665 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1666 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1667 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1668 pitch = wrapDegrees(pitch);
1669 yaw = wrapDegrees(yaw);
1670 player->setPosition(position);
1671 player->setSpeed(speed);
1672 player->setPitch(pitch);
1673 player->setYaw(yaw);
1675 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1676 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1677 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1679 else if(command == TOSERVER_GOTBLOCKS)
1692 u16 count = data[2];
1693 for(u16 i=0; i<count; i++)
1695 if((s16)datasize < 2+1+(i+1)*6)
1696 throw con::InvalidIncomingDataException
1697 ("GOTBLOCKS length is too short");
1698 v3s16 p = readV3S16(&data[2+1+i*6]);
1699 /*dstream<<"Server: GOTBLOCKS ("
1700 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1701 RemoteClient *client = getClient(peer_id);
1702 client->GotBlock(p);
1705 else if(command == TOSERVER_DELETEDBLOCKS)
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 ("DELETEDBLOCKS length is too short");
1724 v3s16 p = readV3S16(&data[2+1+i*6]);
1725 /*dstream<<"Server: DELETEDBLOCKS ("
1726 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1727 RemoteClient *client = getClient(peer_id);
1728 client->SetBlockNotSent(p);
1731 else if(command == TOSERVER_CLICK_OBJECT)
1738 [2] u8 button (0=left, 1=right)
1743 u8 button = readU8(&data[2]);
1745 p.X = readS16(&data[3]);
1746 p.Y = readS16(&data[5]);
1747 p.Z = readS16(&data[7]);
1748 s16 id = readS16(&data[9]);
1749 //u16 item_i = readU16(&data[11]);
1751 MapBlock *block = NULL;
1754 block = m_env.getMap().getBlockNoCreate(p);
1756 catch(InvalidPositionException &e)
1758 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1762 MapBlockObject *obj = block->getObject(id);
1766 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1770 //TODO: Check that object is reasonably close
1775 InventoryList *ilist = player->inventory.getList("main");
1776 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1779 // Skip if inventory has no free space
1780 if(ilist->getUsedSlots() == ilist->getSize())
1782 dout_server<<"Player inventory has no free space"<<std::endl;
1787 Create the inventory item
1789 InventoryItem *item = NULL;
1790 // If it is an item-object, take the item from it
1791 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1793 item = ((ItemObject*)obj)->createInventoryItem();
1795 // Else create an item of the object
1798 item = new MapBlockObjectItem
1799 (obj->getInventoryString());
1802 // Add to inventory and send inventory
1803 ilist->addItem(item);
1804 SendInventory(player->peer_id);
1807 // Remove from block
1808 block->removeObject(id);
1811 else if(command == TOSERVER_GROUND_ACTION)
1819 [3] v3s16 nodepos_undersurface
1820 [9] v3s16 nodepos_abovesurface
1825 2: stop digging (all parameters ignored)
1826 3: digging completed
1828 u8 action = readU8(&data[2]);
1830 p_under.X = readS16(&data[3]);
1831 p_under.Y = readS16(&data[5]);
1832 p_under.Z = readS16(&data[7]);
1834 p_over.X = readS16(&data[9]);
1835 p_over.Y = readS16(&data[11]);
1836 p_over.Z = readS16(&data[13]);
1837 u16 item_i = readU16(&data[15]);
1839 //TODO: Check that target is reasonably close
1847 NOTE: This can be used in the future to check if
1848 somebody is cheating, by checking the timing.
1855 else if(action == 2)
1858 RemoteClient *client = getClient(peer->id);
1859 JMutexAutoLock digmutex(client->m_dig_mutex);
1860 client->m_dig_tool_item = -1;
1865 3: Digging completed
1867 else if(action == 3)
1869 // Mandatory parameter; actually used for nothing
1870 core::map<v3s16, MapBlock*> modified_blocks;
1873 u8 mineral = MINERAL_NONE;
1877 MapNode n = m_env.getMap().getNode(p_under);
1878 // Get material at position
1880 // If it's not diggable, do nothing
1881 if(content_diggable(material) == false)
1883 derr_server<<"Server: Not finishing digging: Node not diggable"
1888 mineral = n.getMineral();
1890 catch(InvalidPositionException &e)
1892 derr_server<<"Server: Not finishing digging: Node not found."
1893 <<" Adding block to emerge queue."
1895 m_emerge_queue.addBlock(peer_id,
1896 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
1901 Send the removal to all other clients
1906 SharedBuffer<u8> reply(replysize);
1907 writeU16(&reply[0], TOCLIENT_REMOVENODE);
1908 writeS16(&reply[2], p_under.X);
1909 writeS16(&reply[4], p_under.Y);
1910 writeS16(&reply[6], p_under.Z);
1912 for(core::map<u16, RemoteClient*>::Iterator
1913 i = m_clients.getIterator();
1914 i.atEnd() == false; i++)
1916 // Get client and check that it is valid
1917 RemoteClient *client = i.getNode()->getValue();
1918 assert(client->peer_id == i.getNode()->getKey());
1919 if(client->serialization_version == SER_FMT_VER_INVALID)
1922 // Don't send if it's the same one
1923 if(peer_id == client->peer_id)
1927 m_con.Send(client->peer_id, 0, reply, true);
1931 Update and send inventory
1934 if(g_settings.getBool("creative_mode") == false)
1939 InventoryList *mlist = player->inventory.getList("main");
1942 InventoryItem *item = mlist->getItem(item_i);
1943 if(item && (std::string)item->getName() == "ToolItem")
1945 ToolItem *titem = (ToolItem*)item;
1946 std::string toolname = titem->getToolName();
1948 // Get digging properties for material and tool
1949 DiggingProperties prop =
1950 getDiggingProperties(material, toolname);
1952 if(prop.diggable == false)
1954 derr_server<<"Server: WARNING: Player digged"
1955 <<" with impossible material + tool"
1956 <<" combination"<<std::endl;
1959 bool weared_out = titem->addWear(prop.wear);
1963 mlist->deleteItem(item_i);
1969 Add dug item to inventory
1972 InventoryItem *item = NULL;
1974 if(mineral != MINERAL_NONE)
1975 item = getDiggedMineralItem(mineral);
1980 std::string &dug_s = content_features(material).dug_item;
1983 std::istringstream is(dug_s, std::ios::binary);
1984 item = InventoryItem::deSerialize(is);
1990 // Add a item to inventory
1991 player->inventory.addItem("main", item);
1994 SendInventory(player->peer_id);
2000 (this takes some time so it is done after the quick stuff)
2002 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2009 // Update water pressure around modification
2010 // This also adds it to m_flow_active_nodes if appropriate
2012 MapVoxelManipulator v(&m_env.getMap());
2013 v.m_disable_water_climb =
2014 g_settings.getBool("disable_water_climb");
2016 VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
2020 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2022 catch(ProcessingLimitException &e)
2024 dstream<<"Processing limit reached (1)"<<std::endl;
2027 v.blitBack(modified_blocks);
2034 else if(action == 1)
2037 InventoryList *ilist = player->inventory.getList("main");
2042 InventoryItem *item = ilist->getItem(item_i);
2044 // If there is no item, it is not possible to add it anywhere
2049 Handle material items
2051 if(std::string("MaterialItem") == item->getName())
2054 // Don't add a node if this is not a free space
2055 MapNode n2 = m_env.getMap().getNode(p_over);
2056 if(content_buildable_to(n2.d) == false)
2058 // Client probably has wrong data.
2059 // Set block not sent, so that client will get
2061 dstream<<"Client "<<peer_id<<" tried to place"
2062 <<" node in invalid position; setting"
2063 <<" MapBlock not sent."<<std::endl;
2064 RemoteClient *client = getClient(peer_id);
2065 v3s16 blockpos = getNodeBlockPos(p_over);
2066 client->SetBlockNotSent(blockpos);
2070 catch(InvalidPositionException &e)
2072 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2073 <<" Adding block to emerge queue."
2075 m_emerge_queue.addBlock(peer_id,
2076 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2080 // Reset build time counter
2081 getClient(peer->id)->m_time_from_building.set(0.0);
2084 MaterialItem *mitem = (MaterialItem*)item;
2086 n.d = mitem->getMaterial();
2087 if(content_features(n.d).wall_mounted)
2088 n.dir = packDir(p_under - p_over);
2092 u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
2093 SharedBuffer<u8> reply(replysize);
2094 writeU16(&reply[0], TOCLIENT_ADDNODE);
2095 writeS16(&reply[2], p_over.X);
2096 writeS16(&reply[4], p_over.Y);
2097 writeS16(&reply[6], p_over.Z);
2098 n.serialize(&reply[8], peer_ser_ver);
2100 m_con.SendToAll(0, reply, true);
2105 InventoryList *ilist = player->inventory.getList("main");
2106 if(g_settings.getBool("creative_mode") == false && ilist)
2108 // Remove from inventory and send inventory
2109 if(mitem->getCount() == 1)
2110 ilist->deleteItem(item_i);
2114 SendInventory(peer_id);
2120 This takes some time so it is done after the quick stuff
2122 core::map<v3s16, MapBlock*> modified_blocks;
2123 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2129 InventoryList *ilist = player->inventory.getList("main");
2130 if(g_settings.getBool("creative_mode") == false && ilist)
2132 // Remove from inventory and send inventory
2133 if(mitem->getCount() == 1)
2134 ilist->deleteItem(item_i);
2138 SendInventory(peer_id);
2144 This takes some time so it is done after the quick stuff
2146 core::map<v3s16, MapBlock*> modified_blocks;
2147 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2150 Set the modified blocks unsent for all the clients
2153 //JMutexAutoLock lock2(m_con_mutex);
2155 for(core::map<u16, RemoteClient*>::Iterator
2156 i = m_clients.getIterator();
2157 i.atEnd() == false; i++)
2159 RemoteClient *client = i.getNode()->getValue();
2161 if(modified_blocks.size() > 0)
2163 // Remove block from sent history
2164 client->SetBlocksNotSent(modified_blocks);
2174 // Update water pressure around modification
2175 // This also adds it to m_flow_active_nodes if appropriate
2177 MapVoxelManipulator v(&m_env.getMap());
2178 v.m_disable_water_climb =
2179 g_settings.getBool("disable_water_climb");
2181 VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
2185 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2187 catch(ProcessingLimitException &e)
2189 dstream<<"Processing limit reached (1)"<<std::endl;
2192 v.blitBack(modified_blocks);
2200 v3s16 blockpos = getNodeBlockPos(p_over);
2202 MapBlock *block = NULL;
2205 block = m_env.getMap().getBlockNoCreate(blockpos);
2207 catch(InvalidPositionException &e)
2209 derr_server<<"Error while placing object: "
2210 "block not found"<<std::endl;
2214 v3s16 block_pos_i_on_map = block->getPosRelative();
2215 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
2217 v3f pos = intToFloat(p_over);
2218 pos -= block_pos_f_on_map;
2220 /*dout_server<<"pos="
2221 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2224 MapBlockObject *obj = NULL;
2227 Handle block object items
2229 if(std::string("MBOItem") == item->getName())
2231 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2233 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2234 "inventorystring=\""
2235 <<oitem->getInventoryString()
2236 <<"\""<<std::endl;*/
2238 obj = oitem->createObject
2239 (pos, player->getYaw(), player->getPitch());
2246 dout_server<<"Placing a miscellaneous item on map"
2249 Create an ItemObject that contains the item.
2251 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2252 std::ostringstream os(std::ios_base::binary);
2253 item->serialize(os);
2254 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2255 iobj->setItemString(os.str());
2261 derr_server<<"WARNING: item resulted in NULL object, "
2262 <<"not placing onto map"
2267 block->addObject(obj);
2269 dout_server<<"Placed object"<<std::endl;
2271 InventoryList *ilist = player->inventory.getList("main");
2272 if(g_settings.getBool("creative_mode") == false && ilist)
2274 // Remove from inventory and send inventory
2275 ilist->deleteItem(item_i);
2277 SendInventory(peer_id);
2285 Catch invalid actions
2289 derr_server<<"WARNING: Server: Invalid action "
2290 <<action<<std::endl;
2294 else if(command == TOSERVER_RELEASE)
2303 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2306 else if(command == TOSERVER_SIGNTEXT)
2315 std::string datastring((char*)&data[2], datasize-2);
2316 std::istringstream is(datastring, std::ios_base::binary);
2319 is.read((char*)buf, 6);
2320 v3s16 blockpos = readV3S16(buf);
2321 is.read((char*)buf, 2);
2322 s16 id = readS16(buf);
2323 is.read((char*)buf, 2);
2324 u16 textlen = readU16(buf);
2326 for(u16 i=0; i<textlen; i++)
2328 is.read((char*)buf, 1);
2329 text += (char)buf[0];
2332 MapBlock *block = NULL;
2335 block = m_env.getMap().getBlockNoCreate(blockpos);
2337 catch(InvalidPositionException &e)
2339 derr_server<<"Error while setting sign text: "
2340 "block not found"<<std::endl;
2344 MapBlockObject *obj = block->getObject(id);
2347 derr_server<<"Error while setting sign text: "
2348 "object not found"<<std::endl;
2352 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2354 derr_server<<"Error while setting sign text: "
2355 "object is not a sign"<<std::endl;
2359 ((SignObject*)obj)->setText(text);
2361 obj->getBlock()->setChangedFlag();
2363 else if(command == TOSERVER_INVENTORY_ACTION)
2365 /*// Ignore inventory changes if in creative mode
2366 if(g_settings.getBool("creative_mode") == true)
2368 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2372 // Strip command and create a stream
2373 std::string datastring((char*)&data[2], datasize-2);
2374 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2375 std::istringstream is(datastring, std::ios_base::binary);
2377 InventoryAction *a = InventoryAction::deSerialize(is);
2381 Handle craftresult specially if not in creative mode
2383 bool disable_action = false;
2384 if(a->getType() == IACTION_MOVE
2385 && g_settings.getBool("creative_mode") == false)
2387 IMoveAction *ma = (IMoveAction*)a;
2388 // Don't allow moving anything to craftresult
2389 if(ma->to_name == "craftresult")
2392 disable_action = true;
2394 // When something is removed from craftresult
2395 if(ma->from_name == "craftresult")
2397 disable_action = true;
2398 // Remove stuff from craft
2399 InventoryList *clist = player->inventory.getList("craft");
2402 u16 count = ma->count;
2405 clist->decrementMaterials(count);
2408 // Feed action to player inventory
2409 a->apply(&player->inventory);
2412 // If something appeared in craftresult, throw it
2414 InventoryList *rlist = player->inventory.getList("craftresult");
2415 InventoryList *mlist = player->inventory.getList("main");
2416 if(rlist && mlist && rlist->getUsedSlots() == 1)
2418 InventoryItem *item1 = rlist->changeItem(0, NULL);
2419 mlist->addItem(item1);
2423 if(disable_action == false)
2425 // Feed action to player inventory
2426 a->apply(&player->inventory);
2431 SendInventory(player->peer_id);
2435 dstream<<"TOSERVER_INVENTORY_ACTION: "
2436 <<"InventoryAction::deSerialize() returned NULL"
2440 else if(command == TOSERVER_CHAT_MESSAGE)
2448 std::string datastring((char*)&data[2], datasize-2);
2449 std::istringstream is(datastring, std::ios_base::binary);
2452 is.read((char*)buf, 2);
2453 u16 len = readU16(buf);
2455 std::wstring message;
2456 for(u16 i=0; i<len; i++)
2458 is.read((char*)buf, 2);
2459 message += (wchar_t)readU16(buf);
2462 // Get player name of this client
2463 std::wstring name = narrow_to_wide(player->getName());
2465 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2467 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2470 Send the message to all other clients
2472 for(core::map<u16, RemoteClient*>::Iterator
2473 i = m_clients.getIterator();
2474 i.atEnd() == false; i++)
2476 // Get client and check that it is valid
2477 RemoteClient *client = i.getNode()->getValue();
2478 assert(client->peer_id == i.getNode()->getKey());
2479 if(client->serialization_version == SER_FMT_VER_INVALID)
2482 // Don't send if it's the same one
2483 if(peer_id == client->peer_id)
2486 SendChatMessage(client->peer_id, line);
2491 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2492 "unknown command "<<command<<std::endl;
2496 catch(SendFailedException &e)
2498 derr_server<<"Server::ProcessData(): SendFailedException: "
2504 /*void Server::Send(u16 peer_id, u16 channelnum,
2505 SharedBuffer<u8> data, bool reliable)
2507 JMutexAutoLock lock(m_con_mutex);
2508 m_con.Send(peer_id, channelnum, data, reliable);
2511 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2513 DSTACK(__FUNCTION_NAME);
2515 Create a packet with the block in the right format
2518 std::ostringstream os(std::ios_base::binary);
2519 block->serialize(os, ver);
2520 std::string s = os.str();
2521 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2523 u32 replysize = 8 + blockdata.getSize();
2524 SharedBuffer<u8> reply(replysize);
2525 v3s16 p = block->getPos();
2526 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2527 writeS16(&reply[2], p.X);
2528 writeS16(&reply[4], p.Y);
2529 writeS16(&reply[6], p.Z);
2530 memcpy(&reply[8], *blockdata, blockdata.getSize());
2532 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2533 <<": \tpacket size: "<<replysize<<std::endl;*/
2538 m_con.Send(peer_id, 1, reply, true);
2541 core::list<PlayerInfo> Server::getPlayerInfo()
2543 DSTACK(__FUNCTION_NAME);
2544 JMutexAutoLock envlock(m_env_mutex);
2545 JMutexAutoLock conlock(m_con_mutex);
2547 core::list<PlayerInfo> list;
2549 core::list<Player*> players = m_env.getPlayers();
2551 core::list<Player*>::Iterator i;
2552 for(i = players.begin();
2553 i != players.end(); i++)
2557 Player *player = *i;
2560 con::Peer *peer = m_con.GetPeer(player->peer_id);
2561 // Copy info from peer to info struct
2563 info.address = peer->address;
2564 info.avg_rtt = peer->avg_rtt;
2566 catch(con::PeerNotFoundException &e)
2568 // Set dummy peer info
2570 info.address = Address(0,0,0,0,0);
2574 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2575 info.position = player->getPosition();
2577 list.push_back(info);
2583 void Server::peerAdded(con::Peer *peer)
2585 DSTACK(__FUNCTION_NAME);
2586 dout_server<<"Server::peerAdded(): peer->id="
2587 <<peer->id<<std::endl;
2590 c.type = PEER_ADDED;
2591 c.peer_id = peer->id;
2593 m_peer_change_queue.push_back(c);
2596 void Server::deletingPeer(con::Peer *peer, bool timeout)
2598 DSTACK(__FUNCTION_NAME);
2599 dout_server<<"Server::deletingPeer(): peer->id="
2600 <<peer->id<<", timeout="<<timeout<<std::endl;
2603 c.type = PEER_REMOVED;
2604 c.peer_id = peer->id;
2605 c.timeout = timeout;
2606 m_peer_change_queue.push_back(c);
2609 void Server::SendObjectData(float dtime)
2611 DSTACK(__FUNCTION_NAME);
2613 core::map<v3s16, bool> stepped_blocks;
2615 for(core::map<u16, RemoteClient*>::Iterator
2616 i = m_clients.getIterator();
2617 i.atEnd() == false; i++)
2619 u16 peer_id = i.getNode()->getKey();
2620 RemoteClient *client = i.getNode()->getValue();
2621 assert(client->peer_id == peer_id);
2623 if(client->serialization_version == SER_FMT_VER_INVALID)
2626 client->SendObjectData(this, dtime, stepped_blocks);
2630 void Server::SendPlayerInfos()
2632 DSTACK(__FUNCTION_NAME);
2634 //JMutexAutoLock envlock(m_env_mutex);
2636 // Get connected players
2637 core::list<Player*> players = m_env.getPlayers(true);
2639 u32 player_count = players.getSize();
2640 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2642 SharedBuffer<u8> data(datasize);
2643 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2646 core::list<Player*>::Iterator i;
2647 for(i = players.begin();
2648 i != players.end(); i++)
2650 Player *player = *i;
2652 /*dstream<<"Server sending player info for player with "
2653 "peer_id="<<player->peer_id<<std::endl;*/
2655 writeU16(&data[start], player->peer_id);
2656 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2657 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2658 start += 2+PLAYERNAME_SIZE;
2661 //JMutexAutoLock conlock(m_con_mutex);
2664 m_con.SendToAll(0, data, true);
2682 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2688 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2694 enum ItemSpecType type;
2695 // Only other one of these is used
2701 items: a pointer to an array of 9 pointers to items
2702 specs: a pointer to an array of 9 ItemSpecs
2704 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2706 u16 items_min_x = 100;
2707 u16 items_max_x = 100;
2708 u16 items_min_y = 100;
2709 u16 items_max_y = 100;
2710 for(u16 y=0; y<3; y++)
2711 for(u16 x=0; x<3; x++)
2713 if(items[y*3 + x] == NULL)
2715 if(items_min_x == 100 || x < items_min_x)
2717 if(items_min_y == 100 || y < items_min_y)
2719 if(items_max_x == 100 || x > items_max_x)
2721 if(items_max_y == 100 || y > items_max_y)
2724 // No items at all, just return false
2725 if(items_min_x == 100)
2728 u16 items_w = items_max_x - items_min_x + 1;
2729 u16 items_h = items_max_y - items_min_y + 1;
2731 u16 specs_min_x = 100;
2732 u16 specs_max_x = 100;
2733 u16 specs_min_y = 100;
2734 u16 specs_max_y = 100;
2735 for(u16 y=0; y<3; y++)
2736 for(u16 x=0; x<3; x++)
2738 if(specs[y*3 + x].type == ITEM_NONE)
2740 if(specs_min_x == 100 || x < specs_min_x)
2742 if(specs_min_y == 100 || y < specs_min_y)
2744 if(specs_max_x == 100 || x > specs_max_x)
2746 if(specs_max_y == 100 || y > specs_max_y)
2749 // No specs at all, just return false
2750 if(specs_min_x == 100)
2753 u16 specs_w = specs_max_x - specs_min_x + 1;
2754 u16 specs_h = specs_max_y - specs_min_y + 1;
2757 if(items_w != specs_w || items_h != specs_h)
2760 for(u16 y=0; y<specs_h; y++)
2761 for(u16 x=0; x<specs_w; x++)
2763 u16 items_x = items_min_x + x;
2764 u16 items_y = items_min_y + y;
2765 u16 specs_x = specs_min_x + x;
2766 u16 specs_y = specs_min_y + y;
2767 InventoryItem *item = items[items_y * 3 + items_x];
2768 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2770 if(spec.type == ITEM_NONE)
2772 // Has to be no item
2778 // There should be an item
2782 std::string itemname = item->getName();
2784 if(spec.type == ITEM_MATERIAL)
2786 if(itemname != "MaterialItem")
2788 MaterialItem *mitem = (MaterialItem*)item;
2789 if(mitem->getMaterial() != spec.num)
2792 else if(spec.type == ITEM_CRAFT)
2794 if(itemname != "CraftItem")
2796 CraftItem *mitem = (CraftItem*)item;
2797 if(mitem->getSubName() != spec.name)
2800 else if(spec.type == ITEM_TOOL)
2802 // Not supported yet
2805 else if(spec.type == ITEM_MBO)
2807 // Not supported yet
2812 // Not supported yet
2820 void Server::SendInventory(u16 peer_id)
2822 DSTACK(__FUNCTION_NAME);
2824 Player* player = m_env.getPlayer(peer_id);
2827 Calculate crafting stuff
2829 if(g_settings.getBool("creative_mode") == false)
2831 InventoryList *clist = player->inventory.getList("craft");
2832 InventoryList *rlist = player->inventory.getList("craftresult");
2835 rlist->clearItems();
2839 InventoryItem *items[9];
2840 for(u16 i=0; i<9; i++)
2842 items[i] = clist->getItem(i);
2851 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2852 if(checkItemCombination(items, specs))
2854 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2863 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2864 if(checkItemCombination(items, specs))
2866 rlist->addItem(new CraftItem("Stick", 4));
2875 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2876 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2877 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2878 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2879 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2880 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2881 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2882 if(checkItemCombination(items, specs))
2884 rlist->addItem(new MapBlockObjectItem("Sign"));
2893 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2894 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2895 if(checkItemCombination(items, specs))
2897 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2906 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2907 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2908 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2909 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2910 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2911 if(checkItemCombination(items, specs))
2913 rlist->addItem(new ToolItem("WPick", 0));
2922 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2923 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2924 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2925 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2926 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2927 if(checkItemCombination(items, specs))
2929 rlist->addItem(new ToolItem("STPick", 0));
2938 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2939 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2940 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2941 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2942 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2943 if(checkItemCombination(items, specs))
2945 rlist->addItem(new ToolItem("MesePick", 0));
2950 } // if creative_mode == false
2956 std::ostringstream os;
2957 //os.imbue(std::locale("C"));
2959 player->inventory.serialize(os);
2961 std::string s = os.str();
2963 SharedBuffer<u8> data(s.size()+2);
2964 writeU16(&data[0], TOCLIENT_INVENTORY);
2965 memcpy(&data[2], s.c_str(), s.size());
2968 m_con.Send(peer_id, 0, data, true);
2971 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2973 DSTACK(__FUNCTION_NAME);
2975 std::ostringstream os(std::ios_base::binary);
2979 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2980 os.write((char*)buf, 2);
2983 writeU16(buf, message.size());
2984 os.write((char*)buf, 2);
2987 for(u32 i=0; i<message.size(); i++)
2991 os.write((char*)buf, 2);
2995 std::string s = os.str();
2996 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2998 m_con.Send(peer_id, 0, data, true);
3001 void Server::BroadcastChatMessage(const std::wstring &message)
3003 for(core::map<u16, RemoteClient*>::Iterator
3004 i = m_clients.getIterator();
3005 i.atEnd() == false; i++)
3007 // Get client and check that it is valid
3008 RemoteClient *client = i.getNode()->getValue();
3009 assert(client->peer_id == i.getNode()->getKey());
3010 if(client->serialization_version == SER_FMT_VER_INVALID)
3013 SendChatMessage(client->peer_id, message);
3017 void Server::SendBlocks(float dtime)
3019 DSTACK(__FUNCTION_NAME);
3021 JMutexAutoLock envlock(m_env_mutex);
3023 core::array<PrioritySortedBlockTransfer> queue;
3025 s32 total_sending = 0;
3027 for(core::map<u16, RemoteClient*>::Iterator
3028 i = m_clients.getIterator();
3029 i.atEnd() == false; i++)
3031 RemoteClient *client = i.getNode()->getValue();
3032 assert(client->peer_id == i.getNode()->getKey());
3034 total_sending += client->SendingCount();
3036 if(client->serialization_version == SER_FMT_VER_INVALID)
3039 client->GetNextBlocks(this, dtime, queue);
3043 // Lowest priority number comes first.
3044 // Lowest is most important.
3047 JMutexAutoLock conlock(m_con_mutex);
3049 for(u32 i=0; i<queue.size(); i++)
3051 //TODO: Calculate limit dynamically
3052 if(total_sending >= g_settings.getS32
3053 ("max_simultaneous_block_sends_server_total"))
3056 PrioritySortedBlockTransfer q = queue[i];
3058 MapBlock *block = NULL;
3061 block = m_env.getMap().getBlockNoCreate(q.pos);
3063 catch(InvalidPositionException &e)
3068 RemoteClient *client = getClient(q.peer_id);
3070 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3072 client->SentBlock(q.pos);
3079 RemoteClient* Server::getClient(u16 peer_id)
3081 DSTACK(__FUNCTION_NAME);
3082 //JMutexAutoLock lock(m_con_mutex);
3083 core::map<u16, RemoteClient*>::Node *n;
3084 n = m_clients.find(peer_id);
3085 // A client should exist for all peers
3087 return n->getValue();
3090 void setCreativeInventory(Player *player)
3092 player->resetInventory();
3094 // Give some good picks
3096 InventoryItem *item = new ToolItem("STPick", 0);
3097 void* r = player->inventory.addItem("main", item);
3101 InventoryItem *item = new ToolItem("MesePick", 0);
3102 void* r = player->inventory.addItem("main", item);
3109 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3112 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3113 player->inventory.addItem("main", item);
3116 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3118 // Skip some materials
3119 if(i == CONTENT_WATER || i == CONTENT_TORCH
3120 || i == CONTENT_COALSTONE)
3123 InventoryItem *item = new MaterialItem(i, 1);
3124 player->inventory.addItem("main", item);
3128 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3129 void* r = player->inventory.addItem("main", item);
3134 Player *Server::emergePlayer(const char *name, const char *password,
3138 Try to get an existing player
3140 Player *player = m_env.getPlayer(name);
3143 // If player is already connected, cancel
3144 if(player->peer_id != 0)
3146 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3151 player->peer_id = peer_id;
3153 // Reset inventory to creative if in creative mode
3154 if(g_settings.getBool("creative_mode"))
3156 setCreativeInventory(player);
3163 If player with the wanted peer_id already exists, cancel.
3165 if(m_env.getPlayer(peer_id) != NULL)
3167 dstream<<"emergePlayer(): Player with wrong name but same"
3168 " peer_id already exists"<<std::endl;
3176 player = new ServerRemotePlayer();
3177 //player->peer_id = c.peer_id;
3178 //player->peer_id = PEER_ID_INEXISTENT;
3179 player->peer_id = peer_id;
3180 player->updateName(name);
3186 dstream<<"Server: Finding spawn place for player \""
3187 <<player->getName()<<"\""<<std::endl;
3191 player->setPosition(intToFloat(v3s16(
3198 f32 groundheight = 0;
3200 // Try to find a good place a few times
3201 for(s32 i=0; i<500; i++)
3204 // We're going to try to throw the player to this position
3205 nodepos = v2s16(-range + (myrand()%(range*2)),
3206 -range + (myrand()%(range*2)));
3207 v2s16 sectorpos = getNodeSectorPos(nodepos);
3209 m_env.getMap().emergeSector(sectorpos);
3210 // Get ground height at point
3211 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3212 // The sector should have been generated -> groundheight exists
3213 assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3214 // Don't go underwater
3215 if(groundheight < WATER_LEVEL)
3217 //dstream<<"-> Underwater"<<std::endl;
3220 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3221 // Get block at point
3223 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3224 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3225 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3226 // Don't go inside ground
3228 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3229 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3230 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3231 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3232 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3233 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3235 dstream<<"-> Inside ground"<<std::endl;
3239 }catch(InvalidPositionException &e)
3241 dstream<<"-> Invalid position"<<std::endl;
3242 // Ignore invalid position
3246 // Found a good place
3247 dstream<<"Searched through "<<i<<" places."<<std::endl;
3252 // If no suitable place was not found, go above water at least.
3253 if(groundheight < WATER_LEVEL)
3254 groundheight = WATER_LEVEL;
3256 player->setPosition(intToFloat(v3s16(
3265 Add player to environment
3268 m_env.addPlayer(player);
3271 Add stuff to inventory
3274 if(g_settings.getBool("creative_mode"))
3276 setCreativeInventory(player);
3281 InventoryItem *item = new ToolItem("WPick", 32000);
3282 void* r = player->inventory.addItem("main", item);
3286 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3287 void* r = player->inventory.addItem("main", item);
3291 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3292 void* r = player->inventory.addItem("main", item);
3296 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3297 void* r = player->inventory.addItem("main", item);
3301 InventoryItem *item = new CraftItem("Stick", 4);
3302 void* r = player->inventory.addItem("main", item);
3306 InventoryItem *item = new ToolItem("WPick", 32000);
3307 void* r = player->inventory.addItem("main", item);
3311 InventoryItem *item = new ToolItem("STPick", 32000);
3312 void* r = player->inventory.addItem("main", item);
3315 /*// Give some lights
3317 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3318 bool r = player->inventory.addItem("main", item);
3322 for(u16 i=0; i<4; i++)
3324 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3325 bool r = player->inventory.addItem("main", item);
3328 /*// Give some other stuff
3330 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3331 bool r = player->inventory.addItem("main", item);
3338 } // create new player
3342 void Server::UpdateBlockWaterPressure(MapBlock *block,
3343 core::map<v3s16, MapBlock*> &modified_blocks)
3345 MapVoxelManipulator v(&m_env.getMap());
3346 v.m_disable_water_climb =
3347 g_settings.getBool("disable_water_climb");
3349 VoxelArea area(block->getPosRelative(),
3350 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3354 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3356 catch(ProcessingLimitException &e)
3358 dstream<<"Processing limit reached (1)"<<std::endl;
3361 v.blitBack(modified_blocks);
3365 void Server::handlePeerChange(PeerChange &c)
3367 JMutexAutoLock envlock(m_env_mutex);
3368 JMutexAutoLock conlock(m_con_mutex);
3370 if(c.type == PEER_ADDED)
3377 core::map<u16, RemoteClient*>::Node *n;
3378 n = m_clients.find(c.peer_id);
3379 // The client shouldn't already exist
3383 RemoteClient *client = new RemoteClient();
3384 client->peer_id = c.peer_id;
3385 m_clients.insert(client->peer_id, client);
3388 else if(c.type == PEER_REMOVED)
3395 core::map<u16, RemoteClient*>::Node *n;
3396 n = m_clients.find(c.peer_id);
3397 // The client should exist
3400 // Collect information about leaving in chat
3401 std::wstring message;
3403 std::wstring name = L"unknown";
3404 Player *player = m_env.getPlayer(c.peer_id);
3406 name = narrow_to_wide(player->getName());
3410 message += L" left game";
3412 message += L" (timed out)";
3417 m_env.removePlayer(c.peer_id);
3420 // Set player client disconnected
3422 Player *player = m_env.getPlayer(c.peer_id);
3424 player->peer_id = 0;
3428 delete m_clients[c.peer_id];
3429 m_clients.remove(c.peer_id);
3431 // Send player info to all remaining clients
3434 // Send leave chat message to all remaining clients
3435 BroadcastChatMessage(message);
3444 void Server::handlePeerChanges()
3446 while(m_peer_change_queue.size() > 0)
3448 PeerChange c = m_peer_change_queue.pop_front();
3450 dout_server<<"Server: Handling peer change: "
3451 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3454 handlePeerChange(c);
3458 void dedicated_server_loop(Server &server)
3460 DSTACK(__FUNCTION_NAME);
3462 std::cout<<std::endl;
3463 std::cout<<"========================"<<std::endl;
3464 std::cout<<"Running dedicated server"<<std::endl;
3465 std::cout<<"========================"<<std::endl;
3466 std::cout<<std::endl;
3470 // This is kind of a hack but can be done like this
3471 // because server.step() is very light
3475 static int counter = 0;
3481 core::list<PlayerInfo> list = server.getPlayerInfo();
3482 core::list<PlayerInfo>::Iterator i;
3483 static u32 sum_old = 0;
3484 u32 sum = PIChecksum(list);
3487 std::cout<<DTIME<<"Player info:"<<std::endl;
3488 for(i=list.begin(); i!=list.end(); i++)
3490 i->PrintLine(&std::cout);