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.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
33 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
35 void * ServerThread::Thread()
39 DSTACK(__FUNCTION_NAME);
41 BEGIN_DEBUG_EXCEPTION_HANDLER
46 //TimeTaker timer("AsyncRunStep() + Receive()");
49 //TimeTaker timer("AsyncRunStep()");
50 m_server->AsyncRunStep();
53 //dout_server<<"Running m_server->Receive()"<<std::endl;
56 catch(con::NoIncomingDataException &e)
59 catch(con::PeerNotFoundException &e)
61 dout_server<<"Server: PeerNotFoundException"<<std::endl;
65 END_DEBUG_EXCEPTION_HANDLER
70 void * EmergeThread::Thread()
74 DSTACK(__FUNCTION_NAME);
78 BEGIN_DEBUG_EXCEPTION_HANDLER
81 Get block info from queue, emerge them and send them
84 After queue is empty, exit.
88 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
92 SharedPtr<QueuedBlockEmerge> q(qptr);
98 Do not generate over-limit
100 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
101 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
108 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
110 //TimeTaker timer("block emerge");
113 Try to emerge it from somewhere.
115 If it is only wanted as optional, only loading from disk
120 Check if any peer wants it as non-optional. In that case it
123 Also decrement the emerge queue count in clients.
126 bool optional = true;
129 core::map<u16, u8>::Iterator i;
130 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
132 //u16 peer_id = i.getNode()->getKey();
135 u8 flags = i.getNode()->getValue();
136 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
142 /*dstream<<"EmergeThread: p="
143 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
144 <<"optional="<<optional<<std::endl;*/
146 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
148 core::map<v3s16, MapBlock*> changed_blocks;
149 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
151 MapBlock *block = NULL;
152 bool got_block = true;
153 core::map<v3s16, MapBlock*> modified_blocks;
155 bool only_from_disk = false;
158 only_from_disk = true;
160 v2s16 chunkpos = map.sector_to_chunk(p2d);
162 bool generate_chunk = false;
163 if(only_from_disk == false)
165 JMutexAutoLock envlock(m_server->m_env_mutex);
166 if(map.chunkNonVolatile(chunkpos) == false)
167 generate_chunk = true;
174 JMutexAutoLock envlock(m_server->m_env_mutex);
175 map.initChunkMake(data, chunkpos);
181 JMutexAutoLock envlock(m_server->m_env_mutex);
182 map.finishChunkMake(data, changed_blocks);
187 Fetch block from map or generate a single block
190 JMutexAutoLock envlock(m_server->m_env_mutex);
192 // Load sector if it isn't loaded
193 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
194 map.loadSectorFull(p2d);
196 block = map.getBlockNoCreateNoEx(p);
197 if(!block || block->isDummy())
205 // Get, load or create sector
206 ServerMapSector *sector =
207 (ServerMapSector*)map.createSector(p2d);
209 block = map.generateBlock(p, block, sector, changed_blocks,
210 lighting_invalidated_blocks);
217 if(block->getLightingExpired()){
218 lighting_invalidated_blocks[block->getPos()] = block;
222 // TODO: Some additional checking and lighting updating,
227 JMutexAutoLock envlock(m_server->m_env_mutex);
232 Collect a list of blocks that have been modified in
233 addition to the fetched one.
236 if(lighting_invalidated_blocks.size() > 0)
238 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
239 <<" blocks"<<std::endl;*/
241 // 50-100ms for single block generation
242 //TimeTaker timer("** EmergeThread updateLighting");
244 // Update lighting without locking the environment mutex,
245 // add modified blocks to changed blocks
246 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
249 // Add all from changed_blocks to modified_blocks
250 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
251 i.atEnd() == false; i++)
253 MapBlock *block = i.getNode()->getValue();
254 modified_blocks.insert(block->getPos(), block);
257 // If we got no block, there should be no invalidated blocks
260 assert(lighting_invalidated_blocks.size() == 0);
266 Set sent status of modified blocks on clients
269 // NOTE: Server's clients are also behind the connection mutex
270 JMutexAutoLock lock(m_server->m_con_mutex);
273 Add the originally fetched block to the modified list
277 modified_blocks.insert(p, block);
281 Set the modified blocks unsent for all the clients
284 for(core::map<u16, RemoteClient*>::Iterator
285 i = m_server->m_clients.getIterator();
286 i.atEnd() == false; i++)
288 RemoteClient *client = i.getNode()->getValue();
290 if(modified_blocks.size() > 0)
292 // Remove block from sent history
293 client->SetBlocksNotSent(modified_blocks);
299 END_DEBUG_EXCEPTION_HANDLER
304 void RemoteClient::GetNextBlocks(Server *server, float dtime,
305 core::array<PrioritySortedBlockTransfer> &dest)
307 DSTACK(__FUNCTION_NAME);
310 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
313 m_nearest_unsent_reset_timer += dtime;
314 m_nothing_to_send_pause_timer -= dtime;
316 if(m_nothing_to_send_pause_timer >= 0)
319 // Won't send anything if already sending
320 if(m_blocks_sending.size() >= g_settings.getU16
321 ("max_simultaneous_block_sends_per_client"))
323 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
327 Player *player = server->m_env.getPlayer(peer_id);
329 assert(player != NULL);
331 v3f playerpos = player->getPosition();
332 v3f playerspeed = player->getSpeed();
334 v3s16 center_nodepos = floatToInt(playerpos, BS);
336 v3s16 center = getNodeBlockPos(center_nodepos);
338 // Camera position and direction
340 playerpos + v3f(0, BS+BS/2, 0);
341 v3f camera_dir = v3f(0,0,1);
342 camera_dir.rotateYZBy(player->getPitch());
343 camera_dir.rotateXZBy(player->getYaw());
346 Get the starting value of the block finder radius.
349 if(m_last_center != center)
351 m_nearest_unsent_d = 0;
352 m_last_center = center;
355 /*dstream<<"m_nearest_unsent_reset_timer="
356 <<m_nearest_unsent_reset_timer<<std::endl;*/
357 if(m_nearest_unsent_reset_timer > 5.0)
359 m_nearest_unsent_reset_timer = 0;
360 m_nearest_unsent_d = 0;
361 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
364 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
365 s16 d_start = m_nearest_unsent_d;
367 //dstream<<"d_start="<<d_start<<std::endl;
369 u16 max_simul_sends_setting = g_settings.getU16
370 ("max_simultaneous_block_sends_per_client");
371 u16 max_simul_sends_usually = max_simul_sends_setting;
374 Check the time from last addNode/removeNode.
376 Decrease send rate if player is building stuff.
378 m_time_from_building += dtime;
379 if(m_time_from_building < g_settings.getFloat(
380 "full_block_send_enable_min_time_from_building"))
382 max_simul_sends_usually
383 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
387 Number of blocks sending + number of blocks selected for sending
389 u32 num_blocks_selected = m_blocks_sending.size();
392 next time d will be continued from the d from which the nearest
393 unsent block was found this time.
395 This is because not necessarily any of the blocks found this
396 time are actually sent.
398 s32 new_nearest_unsent_d = -1;
400 s16 d_max = g_settings.getS16("max_block_send_distance");
401 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
403 //dstream<<"Starting from "<<d_start<<std::endl;
405 bool sending_something = false;
407 for(s16 d = d_start; d <= d_max; d++)
409 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
412 If m_nearest_unsent_d was changed by the EmergeThread
413 (it can change it to 0 through SetBlockNotSent),
415 Else update m_nearest_unsent_d
417 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
419 d = m_nearest_unsent_d;
420 last_nearest_unsent_d = m_nearest_unsent_d;
424 Get the border/face dot coordinates of a "d-radiused"
427 core::list<v3s16> list;
428 getFacePositions(list, d);
430 core::list<v3s16>::Iterator li;
431 for(li=list.begin(); li!=list.end(); li++)
433 v3s16 p = *li + center;
437 - Don't allow too many simultaneous transfers
438 - EXCEPT when the blocks are very close
440 Also, don't send blocks that are already flying.
443 // Start with the usual maximum
444 u16 max_simul_dynamic = max_simul_sends_usually;
446 // If block is very close, allow full maximum
447 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
448 max_simul_dynamic = max_simul_sends_setting;
450 // Don't select too many blocks for sending
451 if(num_blocks_selected >= max_simul_dynamic)
454 // Don't send blocks that are currently being transferred
455 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
490 // Block center y in nodes
491 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
492 // Don't generate if it's very high or very low
493 if(y < -64 || y > 64)
497 v2s16 p2d_nodes_center(
501 // Get ground height in nodes
502 s16 gh = server->m_env.getServerMap().findGroundLevel(
505 // If differs a lot, don't generate
506 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
508 // Actually, don't even send it
515 Don't generate or send if not in sight
518 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
524 Don't send already sent blocks
527 if(m_blocks_sent.find(p) != NULL)
532 Check if map has this block
534 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
536 bool surely_not_found_on_disk = false;
537 bool block_is_invalid = false;
542 surely_not_found_on_disk = true;
545 if(block->isValid() == false)
547 block_is_invalid = true;
550 /*if(block->isFullyGenerated() == false)
552 block_is_invalid = true;
556 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
557 v2s16 chunkpos = map->sector_to_chunk(p2d);
558 if(map->chunkNonVolatile(chunkpos) == false)
559 block_is_invalid = true;
562 If block is not close, don't send it unless it is near
565 Block is not near ground level if night-time mesh
566 doesn't differ from day-time mesh.
570 if(block->dayNightDiffed() == false)
577 If block has been marked to not exist on disk (dummy)
578 and generating new ones is not wanted, skip block.
580 if(generate == false && surely_not_found_on_disk == true)
587 Record the lowest d from which a a block has been
588 found being not sent and possibly to exist
590 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
593 new_nearest_unsent_d = d;
597 Add inexistent block to emerge queue.
599 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
601 //TODO: Get value from somewhere
602 // Allow only one block in emerge queue
603 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
604 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
606 //dstream<<"Adding block to emerge queue"<<std::endl;
608 // Add it to the emerge queue and trigger the thread
611 if(generate == false)
612 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
614 server->m_emerge_queue.addBlock(peer_id, p, flags);
615 server->m_emergethread.trigger();
623 Add block to send queue
626 PrioritySortedBlockTransfer q((float)d, p, peer_id);
630 num_blocks_selected += 1;
631 sending_something = true;
636 if(new_nearest_unsent_d != -1)
638 m_nearest_unsent_d = new_nearest_unsent_d;
641 if(sending_something == false)
643 m_nothing_to_send_counter++;
644 if(m_nothing_to_send_counter >= 3)
646 // Pause time in seconds
647 m_nothing_to_send_pause_timer = 2.0;
652 m_nothing_to_send_counter = 0;
655 /*timer_result = timer.stop(true);
656 if(timer_result != 0)
657 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
660 void RemoteClient::SendObjectData(
663 core::map<v3s16, bool> &stepped_blocks
666 DSTACK(__FUNCTION_NAME);
668 // Can't send anything without knowing version
669 if(serialization_version == SER_FMT_VER_INVALID)
671 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
677 Send a TOCLIENT_OBJECTDATA packet.
681 u16 number of player positions
692 std::ostringstream os(std::ios_base::binary);
696 writeU16(buf, TOCLIENT_OBJECTDATA);
697 os.write((char*)buf, 2);
700 Get and write player data
703 // Get connected players
704 core::list<Player*> players = server->m_env.getPlayers(true);
706 // Write player count
707 u16 playercount = players.size();
708 writeU16(buf, playercount);
709 os.write((char*)buf, 2);
711 core::list<Player*>::Iterator i;
712 for(i = players.begin();
713 i != players.end(); i++)
717 v3f pf = player->getPosition();
718 v3f sf = player->getSpeed();
720 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
721 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
722 s32 pitch_i (player->getPitch() * 100);
723 s32 yaw_i (player->getYaw() * 100);
725 writeU16(buf, player->peer_id);
726 os.write((char*)buf, 2);
727 writeV3S32(buf, position_i);
728 os.write((char*)buf, 12);
729 writeV3S32(buf, speed_i);
730 os.write((char*)buf, 12);
731 writeS32(buf, pitch_i);
732 os.write((char*)buf, 4);
733 writeS32(buf, yaw_i);
734 os.write((char*)buf, 4);
738 Get and write object data
744 For making players to be able to build to their nearby
745 environment (building is not possible on blocks that are not
748 - Add blocks to emerge queue if they are not found
750 SUGGESTION: These could be ignored from the backside of the player
753 Player *player = server->m_env.getPlayer(peer_id);
757 v3f playerpos = player->getPosition();
758 v3f playerspeed = player->getSpeed();
760 v3s16 center_nodepos = floatToInt(playerpos, BS);
761 v3s16 center = getNodeBlockPos(center_nodepos);
763 s16 d_max = g_settings.getS16("active_object_range");
765 // Number of blocks whose objects were written to bos
768 std::ostringstream bos(std::ios_base::binary);
770 for(s16 d = 0; d <= d_max; d++)
772 core::list<v3s16> list;
773 getFacePositions(list, d);
775 core::list<v3s16>::Iterator li;
776 for(li=list.begin(); li!=list.end(); li++)
778 v3s16 p = *li + center;
781 Ignore blocks that haven't been sent to the client
784 if(m_blocks_sent.find(p) == NULL)
788 // Try stepping block and add it to a send queue
793 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
796 Step block if not in stepped_blocks and add to stepped_blocks.
798 if(stepped_blocks.find(p) == NULL)
800 block->stepObjects(dtime, true, server->getDayNightRatio());
801 stepped_blocks.insert(p, true);
802 block->setChangedFlag();
805 // Skip block if there are no objects
806 if(block->getObjectCount() == 0)
815 bos.write((char*)buf, 6);
818 block->serializeObjects(bos, serialization_version);
823 Stop collecting objects if data is already too big
825 // Sum of player and object data sizes
826 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
827 // break out if data too big
828 if(sum > MAX_OBJECTDATA_SIZE)
830 goto skip_subsequent;
834 catch(InvalidPositionException &e)
837 // Add it to the emerge queue and trigger the thread.
838 // Fetch the block only if it is on disk.
840 // Grab and increment counter
841 /*SharedPtr<JMutexAutoLock> lock
842 (m_num_blocks_in_emerge_queue.getLock());
843 m_num_blocks_in_emerge_queue.m_value++;*/
845 // Add to queue as an anonymous fetch from disk
846 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
847 server->m_emerge_queue.addBlock(0, p, flags);
848 server->m_emergethread.trigger();
856 writeU16(buf, blockcount);
857 os.write((char*)buf, 2);
859 // Write block objects
866 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
869 std::string s = os.str();
870 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
871 // Send as unreliable
872 server->m_con.Send(peer_id, 0, data, false);
875 void RemoteClient::GotBlock(v3s16 p)
877 if(m_blocks_sending.find(p) != NULL)
878 m_blocks_sending.remove(p);
881 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
882 " m_blocks_sending"<<std::endl;*/
883 m_excess_gotblocks++;
885 m_blocks_sent.insert(p, true);
888 void RemoteClient::SentBlock(v3s16 p)
890 if(m_blocks_sending.find(p) == NULL)
891 m_blocks_sending.insert(p, 0.0);
893 dstream<<"RemoteClient::SentBlock(): Sent block"
894 " already in m_blocks_sending"<<std::endl;
897 void RemoteClient::SetBlockNotSent(v3s16 p)
899 m_nearest_unsent_d = 0;
901 if(m_blocks_sending.find(p) != NULL)
902 m_blocks_sending.remove(p);
903 if(m_blocks_sent.find(p) != NULL)
904 m_blocks_sent.remove(p);
907 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
909 m_nearest_unsent_d = 0;
911 for(core::map<v3s16, MapBlock*>::Iterator
912 i = blocks.getIterator();
913 i.atEnd()==false; i++)
915 v3s16 p = i.getNode()->getKey();
917 if(m_blocks_sending.find(p) != NULL)
918 m_blocks_sending.remove(p);
919 if(m_blocks_sent.find(p) != NULL)
920 m_blocks_sent.remove(p);
928 PlayerInfo::PlayerInfo()
934 void PlayerInfo::PrintLine(std::ostream *s)
937 (*s)<<"\""<<name<<"\" ("
938 <<(position.X/10)<<","<<(position.Y/10)
939 <<","<<(position.Z/10)<<") ";
941 (*s)<<" avg_rtt="<<avg_rtt;
945 u32 PIChecksum(core::list<PlayerInfo> &l)
947 core::list<PlayerInfo>::Iterator i;
950 for(i=l.begin(); i!=l.end(); i++)
952 checksum += a * (i->id+1);
953 checksum ^= 0x435aafcd;
964 std::string mapsavedir
966 m_env(new ServerMap(mapsavedir), this),
967 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
969 m_emergethread(this),
972 m_time_of_day_send_timer(0),
974 m_mapsavedir(mapsavedir),
975 m_shutdown_requested(false),
976 m_ignore_map_edit_events(false),
977 m_ignore_map_edit_events_peer_id(0)
979 m_liquid_transform_timer = 0.0;
980 m_print_info_timer = 0.0;
981 m_objectdata_timer = 0.0;
982 m_emergethread_trigger_timer = 0.0;
983 m_savemap_timer = 0.0;
987 m_step_dtime_mutex.Init();
990 m_env.getMap().addEventReceiver(this);
993 m_env.deSerializePlayers(m_mapsavedir);
998 dstream<<"Server::~Server()"<<std::endl;
1001 Send shutdown message
1004 JMutexAutoLock conlock(m_con_mutex);
1006 std::wstring line = L"*** Server shutting down";
1009 Send the message to clients
1011 for(core::map<u16, RemoteClient*>::Iterator
1012 i = m_clients.getIterator();
1013 i.atEnd() == false; i++)
1015 // Get client and check that it is valid
1016 RemoteClient *client = i.getNode()->getValue();
1017 assert(client->peer_id == i.getNode()->getKey());
1018 if(client->serialization_version == SER_FMT_VER_INVALID)
1022 SendChatMessage(client->peer_id, line);
1024 catch(con::PeerNotFoundException &e)
1032 dstream<<"Server: Saving players"<<std::endl;
1033 m_env.serializePlayers(m_mapsavedir);
1044 JMutexAutoLock clientslock(m_con_mutex);
1046 for(core::map<u16, RemoteClient*>::Iterator
1047 i = m_clients.getIterator();
1048 i.atEnd() == false; i++)
1051 // NOTE: These are removed by env destructor
1053 u16 peer_id = i.getNode()->getKey();
1054 JMutexAutoLock envlock(m_env_mutex);
1055 m_env.removePlayer(peer_id);
1059 delete i.getNode()->getValue();
1064 void Server::start(unsigned short port)
1066 DSTACK(__FUNCTION_NAME);
1067 // Stop thread if already running
1070 // Initialize connection
1071 m_con.setTimeoutMs(30);
1075 m_thread.setRun(true);
1078 dout_server<<"Server: Started on port "<<port<<std::endl;
1083 DSTACK(__FUNCTION_NAME);
1085 // Stop threads (set run=false first so both start stopping)
1086 m_thread.setRun(false);
1087 m_emergethread.setRun(false);
1089 m_emergethread.stop();
1091 dout_server<<"Server: Threads stopped"<<std::endl;
1094 void Server::step(float dtime)
1096 DSTACK(__FUNCTION_NAME);
1101 JMutexAutoLock lock(m_step_dtime_mutex);
1102 m_step_dtime += dtime;
1106 void Server::AsyncRunStep()
1108 DSTACK(__FUNCTION_NAME);
1112 JMutexAutoLock lock1(m_step_dtime_mutex);
1113 dtime = m_step_dtime;
1116 // Send blocks to clients
1122 //dstream<<"Server steps "<<dtime<<std::endl;
1123 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1126 JMutexAutoLock lock1(m_step_dtime_mutex);
1127 m_step_dtime -= dtime;
1134 m_uptime.set(m_uptime.get() + dtime);
1138 Update m_time_of_day
1141 m_time_counter += dtime;
1142 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1143 u32 units = (u32)(m_time_counter*speed);
1144 m_time_counter -= (f32)units / speed;
1145 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1147 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1150 Send to clients at constant intervals
1153 m_time_of_day_send_timer -= dtime;
1154 if(m_time_of_day_send_timer < 0.0)
1156 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1158 //JMutexAutoLock envlock(m_env_mutex);
1159 JMutexAutoLock conlock(m_con_mutex);
1161 for(core::map<u16, RemoteClient*>::Iterator
1162 i = m_clients.getIterator();
1163 i.atEnd() == false; i++)
1165 RemoteClient *client = i.getNode()->getValue();
1166 //Player *player = m_env.getPlayer(client->peer_id);
1168 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1169 m_time_of_day.get());
1171 m_con.Send(client->peer_id, 0, data, true);
1177 // Process connection's timeouts
1178 JMutexAutoLock lock2(m_con_mutex);
1179 m_con.RunTimeouts(dtime);
1183 // This has to be called so that the client list gets synced
1184 // with the peer list of the connection
1185 handlePeerChanges();
1190 // This also runs Map's timers
1191 JMutexAutoLock lock(m_env_mutex);
1202 m_liquid_transform_timer += dtime;
1203 if(m_liquid_transform_timer >= 1.00)
1205 m_liquid_transform_timer -= 1.00;
1207 JMutexAutoLock lock(m_env_mutex);
1209 core::map<v3s16, MapBlock*> modified_blocks;
1210 m_env.getMap().transformLiquids(modified_blocks);
1215 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1216 ServerMap &map = ((ServerMap&)m_env.getMap());
1217 map.updateLighting(modified_blocks, lighting_modified_blocks);
1219 // Add blocks modified by lighting to modified_blocks
1220 for(core::map<v3s16, MapBlock*>::Iterator
1221 i = lighting_modified_blocks.getIterator();
1222 i.atEnd() == false; i++)
1224 MapBlock *block = i.getNode()->getValue();
1225 modified_blocks.insert(block->getPos(), block);
1229 Set the modified blocks unsent for all the clients
1232 JMutexAutoLock lock2(m_con_mutex);
1234 for(core::map<u16, RemoteClient*>::Iterator
1235 i = m_clients.getIterator();
1236 i.atEnd() == false; i++)
1238 RemoteClient *client = i.getNode()->getValue();
1240 if(modified_blocks.size() > 0)
1242 // Remove block from sent history
1243 client->SetBlocksNotSent(modified_blocks);
1248 // Periodically print some info
1250 float &counter = m_print_info_timer;
1256 JMutexAutoLock lock2(m_con_mutex);
1258 for(core::map<u16, RemoteClient*>::Iterator
1259 i = m_clients.getIterator();
1260 i.atEnd() == false; i++)
1262 //u16 peer_id = i.getNode()->getKey();
1263 RemoteClient *client = i.getNode()->getValue();
1264 Player *player = m_env.getPlayer(client->peer_id);
1267 std::cout<<player->getName()<<"\t";
1268 client->PrintInfo(std::cout);
1273 //if(g_settings.getBool("enable_experimental"))
1277 Check added and deleted active objects
1280 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1282 JMutexAutoLock envlock(m_env_mutex);
1283 JMutexAutoLock conlock(m_con_mutex);
1285 // Radius inside which objects are active
1288 for(core::map<u16, RemoteClient*>::Iterator
1289 i = m_clients.getIterator();
1290 i.atEnd() == false; i++)
1292 RemoteClient *client = i.getNode()->getValue();
1293 Player *player = m_env.getPlayer(client->peer_id);
1296 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1297 <<" has no associated player"<<std::endl;
1300 v3s16 pos = floatToInt(player->getPosition(), BS);
1302 core::map<u16, bool> removed_objects;
1303 core::map<u16, bool> added_objects;
1304 m_env.getRemovedActiveObjects(pos, radius,
1305 client->m_known_objects, removed_objects);
1306 m_env.getAddedActiveObjects(pos, radius,
1307 client->m_known_objects, added_objects);
1309 // Ignore if nothing happened
1310 if(removed_objects.size() == 0 && added_objects.size() == 0)
1312 //dstream<<"INFO: active objects: none changed"<<std::endl;
1316 std::string data_buffer;
1320 // Handle removed objects
1321 writeU16((u8*)buf, removed_objects.size());
1322 data_buffer.append(buf, 2);
1323 for(core::map<u16, bool>::Iterator
1324 i = removed_objects.getIterator();
1325 i.atEnd()==false; i++)
1328 u16 id = i.getNode()->getKey();
1329 ServerActiveObject* obj = m_env.getActiveObject(id);
1331 // Add to data buffer for sending
1332 writeU16((u8*)buf, i.getNode()->getKey());
1333 data_buffer.append(buf, 2);
1335 // Remove from known objects
1336 client->m_known_objects.remove(i.getNode()->getKey());
1338 if(obj && obj->m_known_by_count > 0)
1339 obj->m_known_by_count--;
1342 // Handle added objects
1343 writeU16((u8*)buf, added_objects.size());
1344 data_buffer.append(buf, 2);
1345 for(core::map<u16, bool>::Iterator
1346 i = added_objects.getIterator();
1347 i.atEnd()==false; i++)
1350 u16 id = i.getNode()->getKey();
1351 ServerActiveObject* obj = m_env.getActiveObject(id);
1354 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1356 dstream<<"WARNING: "<<__FUNCTION_NAME
1357 <<": NULL object"<<std::endl;
1359 type = obj->getType();
1361 // Add to data buffer for sending
1362 writeU16((u8*)buf, id);
1363 data_buffer.append(buf, 2);
1364 writeU8((u8*)buf, type);
1365 data_buffer.append(buf, 1);
1368 data_buffer.append(serializeLongString(
1369 obj->getClientInitializationData()));
1371 data_buffer.append(serializeLongString(""));
1373 // Add to known objects
1374 client->m_known_objects.insert(i.getNode()->getKey(), false);
1377 obj->m_known_by_count++;
1381 SharedBuffer<u8> reply(2 + data_buffer.size());
1382 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1383 memcpy((char*)&reply[2], data_buffer.c_str(),
1384 data_buffer.size());
1386 m_con.Send(client->peer_id, 0, reply, true);
1388 dstream<<"INFO: Server: Sent object remove/add: "
1389 <<removed_objects.size()<<" removed, "
1390 <<added_objects.size()<<" added, "
1391 <<"packet size is "<<reply.getSize()<<std::endl;
1396 Collect a list of all the objects known by the clients
1397 and report it back to the environment.
1400 core::map<u16, bool> all_known_objects;
1402 for(core::map<u16, RemoteClient*>::Iterator
1403 i = m_clients.getIterator();
1404 i.atEnd() == false; i++)
1406 RemoteClient *client = i.getNode()->getValue();
1407 // Go through all known objects of client
1408 for(core::map<u16, bool>::Iterator
1409 i = client->m_known_objects.getIterator();
1410 i.atEnd()==false; i++)
1412 u16 id = i.getNode()->getKey();
1413 all_known_objects[id] = true;
1417 m_env.setKnownActiveObjects(whatever);
1423 Send object messages
1426 JMutexAutoLock envlock(m_env_mutex);
1427 JMutexAutoLock conlock(m_con_mutex);
1430 // Value = data sent by object
1431 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1433 // Get active object messages from environment
1436 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1440 core::list<ActiveObjectMessage>* message_list = NULL;
1441 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1442 n = buffered_messages.find(aom.id);
1445 message_list = new core::list<ActiveObjectMessage>;
1446 buffered_messages.insert(aom.id, message_list);
1450 message_list = n->getValue();
1452 message_list->push_back(aom);
1455 // Route data to every client
1456 for(core::map<u16, RemoteClient*>::Iterator
1457 i = m_clients.getIterator();
1458 i.atEnd()==false; i++)
1460 RemoteClient *client = i.getNode()->getValue();
1461 std::string reliable_data;
1462 std::string unreliable_data;
1463 // Go through all objects in message buffer
1464 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1465 j = buffered_messages.getIterator();
1466 j.atEnd()==false; j++)
1468 // If object is not known by client, skip it
1469 u16 id = j.getNode()->getKey();
1470 if(client->m_known_objects.find(id) == NULL)
1472 // Get message list of object
1473 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1474 // Go through every message
1475 for(core::list<ActiveObjectMessage>::Iterator
1476 k = list->begin(); k != list->end(); k++)
1478 // Compose the full new data with header
1479 ActiveObjectMessage aom = *k;
1480 std::string new_data;
1483 writeU16((u8*)&buf[0], aom.id);
1484 new_data.append(buf, 2);
1486 new_data += serializeString(aom.datastring);
1487 // Add data to buffer
1489 reliable_data += new_data;
1491 unreliable_data += new_data;
1495 reliable_data and unreliable_data are now ready.
1498 if(reliable_data.size() > 0)
1500 SharedBuffer<u8> reply(2 + reliable_data.size());
1501 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1502 memcpy((char*)&reply[2], reliable_data.c_str(),
1503 reliable_data.size());
1505 m_con.Send(client->peer_id, 0, reply, true);
1507 if(unreliable_data.size() > 0)
1509 SharedBuffer<u8> reply(2 + unreliable_data.size());
1510 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1511 memcpy((char*)&reply[2], unreliable_data.c_str(),
1512 unreliable_data.size());
1513 // Send as unreliable
1514 m_con.Send(client->peer_id, 0, reply, false);
1517 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1519 dstream<<"INFO: Server: Size of object message data: "
1520 <<"reliable: "<<reliable_data.size()
1521 <<", unreliable: "<<unreliable_data.size()
1526 // Clear buffered_messages
1527 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1528 i = buffered_messages.getIterator();
1529 i.atEnd()==false; i++)
1531 delete i.getNode()->getValue();
1535 } // enable_experimental
1538 Send queued-for-sending map edit events.
1541 while(m_unsent_map_edit_queue.size() != 0)
1543 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1545 if(event->type == MEET_ADDNODE)
1547 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1548 sendAddNode(event->p, event->n, event->already_known_by_peer);
1550 else if(event->type == MEET_REMOVENODE)
1552 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1553 sendRemoveNode(event->p, event->already_known_by_peer);
1555 else if(event->type == MEET_OTHER)
1557 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1562 dstream<<"WARNING: Server: Unknown MapEditEvent "
1563 <<((u32)event->type)<<std::endl;
1571 Send object positions
1572 TODO: Get rid of MapBlockObjects
1575 float &counter = m_objectdata_timer;
1577 if(counter >= g_settings.getFloat("objectdata_interval"))
1579 JMutexAutoLock lock1(m_env_mutex);
1580 JMutexAutoLock lock2(m_con_mutex);
1581 SendObjectData(counter);
1591 //TimeTaker timer("Step node metadata");
1593 JMutexAutoLock envlock(m_env_mutex);
1594 JMutexAutoLock conlock(m_con_mutex);
1596 core::map<v3s16, MapBlock*> changed_blocks;
1597 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1599 for(core::map<v3s16, MapBlock*>::Iterator
1600 i = changed_blocks.getIterator();
1601 i.atEnd() == false; i++)
1603 MapBlock *block = i.getNode()->getValue();
1605 for(core::map<u16, RemoteClient*>::Iterator
1606 i = m_clients.getIterator();
1607 i.atEnd()==false; i++)
1609 RemoteClient *client = i.getNode()->getValue();
1610 client->SetBlockNotSent(block->getPos());
1616 Trigger emergethread (it somehow gets to a non-triggered but
1617 bysy state sometimes)
1620 float &counter = m_emergethread_trigger_timer;
1626 m_emergethread.trigger();
1632 float &counter = m_savemap_timer;
1634 if(counter >= g_settings.getFloat("server_map_save_interval"))
1638 JMutexAutoLock lock(m_env_mutex);
1640 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1642 // Save only changed parts
1643 m_env.getMap().save(true);
1645 // Delete unused sectors
1646 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1647 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1648 if(deleted_count > 0)
1650 dout_server<<"Server: Unloaded "<<deleted_count
1651 <<" sectors from memory"<<std::endl;
1655 m_env.serializePlayers(m_mapsavedir);
1661 void Server::Receive()
1663 DSTACK(__FUNCTION_NAME);
1664 u32 data_maxsize = 10000;
1665 Buffer<u8> data(data_maxsize);
1670 JMutexAutoLock conlock(m_con_mutex);
1671 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1674 // This has to be called so that the client list gets synced
1675 // with the peer list of the connection
1676 handlePeerChanges();
1678 ProcessData(*data, datasize, peer_id);
1680 catch(con::InvalidIncomingDataException &e)
1682 derr_server<<"Server::Receive(): "
1683 "InvalidIncomingDataException: what()="
1684 <<e.what()<<std::endl;
1686 catch(con::PeerNotFoundException &e)
1688 //NOTE: This is not needed anymore
1690 // The peer has been disconnected.
1691 // Find the associated player and remove it.
1693 /*JMutexAutoLock envlock(m_env_mutex);
1695 dout_server<<"ServerThread: peer_id="<<peer_id
1696 <<" has apparently closed connection. "
1697 <<"Removing player."<<std::endl;
1699 m_env.removePlayer(peer_id);*/
1703 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1705 DSTACK(__FUNCTION_NAME);
1706 // Environment is locked first.
1707 JMutexAutoLock envlock(m_env_mutex);
1708 JMutexAutoLock conlock(m_con_mutex);
1712 peer = m_con.GetPeer(peer_id);
1714 catch(con::PeerNotFoundException &e)
1716 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1717 <<peer_id<<" not found"<<std::endl;
1721 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1729 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1731 if(command == TOSERVER_INIT)
1733 // [0] u16 TOSERVER_INIT
1734 // [2] u8 SER_FMT_VER_HIGHEST
1735 // [3] u8[20] player_name
1740 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1741 <<peer->id<<std::endl;
1743 // First byte after command is maximum supported
1744 // serialization version
1745 u8 client_max = data[2];
1746 u8 our_max = SER_FMT_VER_HIGHEST;
1747 // Use the highest version supported by both
1748 u8 deployed = core::min_(client_max, our_max);
1749 // If it's lower than the lowest supported, give up.
1750 if(deployed < SER_FMT_VER_LOWEST)
1751 deployed = SER_FMT_VER_INVALID;
1753 //peer->serialization_version = deployed;
1754 getClient(peer->id)->pending_serialization_version = deployed;
1756 if(deployed == SER_FMT_VER_INVALID)
1758 derr_server<<DTIME<<"Server: Cannot negotiate "
1759 "serialization version with peer "
1760 <<peer_id<<std::endl;
1769 const u32 playername_size = 20;
1770 char playername[playername_size];
1771 for(u32 i=0; i<playername_size-1; i++)
1773 playername[i] = data[3+i];
1775 playername[playername_size-1] = 0;
1778 Player *player = emergePlayer(playername, "", peer_id);
1779 //Player *player = m_env.getPlayer(peer_id);
1782 // DEBUG: Test serialization
1783 std::ostringstream test_os;
1784 player->serialize(test_os);
1785 dstream<<"Player serialization test: \""<<test_os.str()
1787 std::istringstream test_is(test_os.str());
1788 player->deSerialize(test_is);
1791 // If failed, cancel
1794 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1795 <<": failed to emerge player"<<std::endl;
1800 // If a client is already connected to the player, cancel
1801 if(player->peer_id != 0)
1803 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1804 <<" tried to connect to "
1805 "an already connected player (peer_id="
1806 <<player->peer_id<<")"<<std::endl;
1809 // Set client of player
1810 player->peer_id = peer_id;
1813 // Check if player doesn't exist
1815 throw con::InvalidIncomingDataException
1816 ("Server::ProcessData(): INIT: Player doesn't exist");
1818 /*// update name if it was supplied
1819 if(datasize >= 20+3)
1822 player->updateName((const char*)&data[3]);
1826 Answer with a TOCLIENT_INIT
1829 SharedBuffer<u8> reply(2+1+6+8);
1830 writeU16(&reply[0], TOCLIENT_INIT);
1831 writeU8(&reply[2], deployed);
1832 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1833 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1834 writeU64(&reply[2+1+6], 0); // no seed
1837 m_con.Send(peer_id, 0, reply, true);
1841 Send complete position information
1843 SendMovePlayer(player);
1848 if(command == TOSERVER_INIT2)
1850 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1851 <<peer->id<<std::endl;
1854 getClient(peer->id)->serialization_version
1855 = getClient(peer->id)->pending_serialization_version;
1858 Send some initialization data
1861 // Send player info to all players
1864 // Send inventory to player
1865 UpdateCrafting(peer->id);
1866 SendInventory(peer->id);
1870 Player *player = m_env.getPlayer(peer_id);
1871 SendPlayerHP(player);
1876 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1877 m_time_of_day.get());
1878 m_con.Send(peer->id, 0, data, true);
1881 // Send information about server to player in chat
1882 SendChatMessage(peer_id, getStatusString());
1884 // Send information about joining in chat
1886 std::wstring name = L"unknown";
1887 Player *player = m_env.getPlayer(peer_id);
1889 name = narrow_to_wide(player->getName());
1891 std::wstring message;
1894 message += L" joined game";
1895 BroadcastChatMessage(message);
1901 if(peer_ser_ver == SER_FMT_VER_INVALID)
1903 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1904 " serialization format invalid or not initialized."
1905 " Skipping incoming command="<<command<<std::endl;
1909 Player *player = m_env.getPlayer(peer_id);
1912 derr_server<<"Server::ProcessData(): Cancelling: "
1913 "No player for peer_id="<<peer_id
1917 if(command == TOSERVER_PLAYERPOS)
1919 if(datasize < 2+12+12+4+4)
1923 v3s32 ps = readV3S32(&data[start+2]);
1924 v3s32 ss = readV3S32(&data[start+2+12]);
1925 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1926 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1927 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1928 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1929 pitch = wrapDegrees(pitch);
1930 yaw = wrapDegrees(yaw);
1931 player->setPosition(position);
1932 player->setSpeed(speed);
1933 player->setPitch(pitch);
1934 player->setYaw(yaw);
1936 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1937 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1938 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1940 else if(command == TOSERVER_GOTBLOCKS)
1953 u16 count = data[2];
1954 for(u16 i=0; i<count; i++)
1956 if((s16)datasize < 2+1+(i+1)*6)
1957 throw con::InvalidIncomingDataException
1958 ("GOTBLOCKS length is too short");
1959 v3s16 p = readV3S16(&data[2+1+i*6]);
1960 /*dstream<<"Server: GOTBLOCKS ("
1961 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1962 RemoteClient *client = getClient(peer_id);
1963 client->GotBlock(p);
1966 else if(command == TOSERVER_DELETEDBLOCKS)
1979 u16 count = data[2];
1980 for(u16 i=0; i<count; i++)
1982 if((s16)datasize < 2+1+(i+1)*6)
1983 throw con::InvalidIncomingDataException
1984 ("DELETEDBLOCKS length is too short");
1985 v3s16 p = readV3S16(&data[2+1+i*6]);
1986 /*dstream<<"Server: DELETEDBLOCKS ("
1987 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1988 RemoteClient *client = getClient(peer_id);
1989 client->SetBlockNotSent(p);
1992 else if(command == TOSERVER_CLICK_OBJECT)
1999 [2] u8 button (0=left, 1=right)
2004 u8 button = readU8(&data[2]);
2006 p.X = readS16(&data[3]);
2007 p.Y = readS16(&data[5]);
2008 p.Z = readS16(&data[7]);
2009 s16 id = readS16(&data[9]);
2010 //u16 item_i = readU16(&data[11]);
2012 MapBlock *block = NULL;
2015 block = m_env.getMap().getBlockNoCreate(p);
2017 catch(InvalidPositionException &e)
2019 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2023 MapBlockObject *obj = block->getObject(id);
2027 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2031 //TODO: Check that object is reasonably close
2036 InventoryList *ilist = player->inventory.getList("main");
2037 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2040 // Skip if inventory has no free space
2041 if(ilist->getUsedSlots() == ilist->getSize())
2043 dout_server<<"Player inventory has no free space"<<std::endl;
2048 Create the inventory item
2050 InventoryItem *item = NULL;
2051 // If it is an item-object, take the item from it
2052 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2054 item = ((ItemObject*)obj)->createInventoryItem();
2056 // Else create an item of the object
2059 item = new MapBlockObjectItem
2060 (obj->getInventoryString());
2063 // Add to inventory and send inventory
2064 ilist->addItem(item);
2065 UpdateCrafting(player->peer_id);
2066 SendInventory(player->peer_id);
2069 // Remove from block
2070 block->removeObject(id);
2073 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2081 [2] u8 button (0=left, 1=right)
2085 u8 button = readU8(&data[2]);
2086 u16 id = readS16(&data[3]);
2087 u16 item_i = readU16(&data[11]);
2089 ServerActiveObject *obj = m_env.getActiveObject(id);
2093 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2098 //TODO: Check that object is reasonably close
2100 // Left click, pick object up (usually)
2103 InventoryList *ilist = player->inventory.getList("main");
2104 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2107 // Skip if inventory has no free space
2108 if(ilist->getUsedSlots() == ilist->getSize())
2110 dout_server<<"Player inventory has no free space"<<std::endl;
2114 // Skip if object has been removed
2119 Create the inventory item
2121 InventoryItem *item = obj->createPickedUpItem();
2125 // Add to inventory and send inventory
2126 ilist->addItem(item);
2127 UpdateCrafting(player->peer_id);
2128 SendInventory(player->peer_id);
2130 // Remove object from environment
2131 obj->m_removed = true;
2136 Item cannot be picked up. Punch it instead.
2139 ToolItem *titem = NULL;
2140 std::string toolname = "";
2142 InventoryList *mlist = player->inventory.getList("main");
2145 InventoryItem *item = mlist->getItem(item_i);
2146 if(item && (std::string)item->getName() == "ToolItem")
2148 titem = (ToolItem*)item;
2149 toolname = titem->getToolName();
2153 u16 wear = obj->punch(toolname);
2157 bool weared_out = titem->addWear(wear);
2159 mlist->deleteItem(item_i);
2160 SendInventory(player->peer_id);
2166 else if(command == TOSERVER_GROUND_ACTION)
2174 [3] v3s16 nodepos_undersurface
2175 [9] v3s16 nodepos_abovesurface
2180 2: stop digging (all parameters ignored)
2181 3: digging completed
2183 u8 action = readU8(&data[2]);
2185 p_under.X = readS16(&data[3]);
2186 p_under.Y = readS16(&data[5]);
2187 p_under.Z = readS16(&data[7]);
2189 p_over.X = readS16(&data[9]);
2190 p_over.Y = readS16(&data[11]);
2191 p_over.Z = readS16(&data[13]);
2192 u16 item_i = readU16(&data[15]);
2194 //TODO: Check that target is reasonably close
2202 NOTE: This can be used in the future to check if
2203 somebody is cheating, by checking the timing.
2210 else if(action == 2)
2213 RemoteClient *client = getClient(peer->id);
2214 JMutexAutoLock digmutex(client->m_dig_mutex);
2215 client->m_dig_tool_item = -1;
2220 3: Digging completed
2222 else if(action == 3)
2224 // Mandatory parameter; actually used for nothing
2225 core::map<v3s16, MapBlock*> modified_blocks;
2228 u8 mineral = MINERAL_NONE;
2230 bool cannot_remove_node = false;
2234 MapNode n = m_env.getMap().getNode(p_under);
2236 mineral = n.getMineral();
2237 // Get material at position
2239 // If not yet cancelled
2240 if(cannot_remove_node == false)
2242 // If it's not diggable, do nothing
2243 if(content_diggable(material) == false)
2245 derr_server<<"Server: Not finishing digging: "
2246 <<"Node not diggable"
2248 cannot_remove_node = true;
2251 // If not yet cancelled
2252 if(cannot_remove_node == false)
2254 // Get node metadata
2255 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2256 if(meta && meta->nodeRemovalDisabled() == true)
2258 derr_server<<"Server: Not finishing digging: "
2259 <<"Node metadata disables removal"
2261 cannot_remove_node = true;
2265 catch(InvalidPositionException &e)
2267 derr_server<<"Server: Not finishing digging: Node not found."
2268 <<" Adding block to emerge queue."
2270 m_emerge_queue.addBlock(peer_id,
2271 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2272 cannot_remove_node = true;
2276 If node can't be removed, set block to be re-sent to
2279 if(cannot_remove_node)
2281 derr_server<<"Server: Not finishing digging."<<std::endl;
2283 // Client probably has wrong data.
2284 // Set block not sent, so that client will get
2286 dstream<<"Client "<<peer_id<<" tried to dig "
2287 <<"node; but node cannot be removed."
2288 <<" setting MapBlock not sent."<<std::endl;
2289 RemoteClient *client = getClient(peer_id);
2290 v3s16 blockpos = getNodeBlockPos(p_under);
2291 client->SetBlockNotSent(blockpos);
2297 Send the removal to all other clients.
2298 - If other player is close, send REMOVENODE
2299 - Otherwise set blocks not sent
2301 core::list<u16> far_players;
2302 sendRemoveNode(p_under, peer_id, &far_players, 100);
2305 Update and send inventory
2308 if(g_settings.getBool("creative_mode") == false)
2313 InventoryList *mlist = player->inventory.getList("main");
2316 InventoryItem *item = mlist->getItem(item_i);
2317 if(item && (std::string)item->getName() == "ToolItem")
2319 ToolItem *titem = (ToolItem*)item;
2320 std::string toolname = titem->getToolName();
2322 // Get digging properties for material and tool
2323 DiggingProperties prop =
2324 getDiggingProperties(material, toolname);
2326 if(prop.diggable == false)
2328 derr_server<<"Server: WARNING: Player digged"
2329 <<" with impossible material + tool"
2330 <<" combination"<<std::endl;
2333 bool weared_out = titem->addWear(prop.wear);
2337 mlist->deleteItem(item_i);
2343 Add dug item to inventory
2346 InventoryItem *item = NULL;
2348 if(mineral != MINERAL_NONE)
2349 item = getDiggedMineralItem(mineral);
2354 std::string &dug_s = content_features(material).dug_item;
2357 std::istringstream is(dug_s, std::ios::binary);
2358 item = InventoryItem::deSerialize(is);
2364 // Add a item to inventory
2365 player->inventory.addItem("main", item);
2368 UpdateCrafting(player->peer_id);
2369 SendInventory(player->peer_id);
2375 (this takes some time so it is done after the quick stuff)
2377 m_ignore_map_edit_events = true;
2378 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2379 m_ignore_map_edit_events = false;
2382 Set blocks not sent to far players
2384 for(core::list<u16>::Iterator
2385 i = far_players.begin();
2386 i != far_players.end(); i++)
2389 RemoteClient *client = getClient(peer_id);
2392 client->SetBlocksNotSent(modified_blocks);
2399 else if(action == 1)
2402 InventoryList *ilist = player->inventory.getList("main");
2407 InventoryItem *item = ilist->getItem(item_i);
2409 // If there is no item, it is not possible to add it anywhere
2414 Handle material items
2416 if(std::string("MaterialItem") == item->getName())
2419 // Don't add a node if this is not a free space
2420 MapNode n2 = m_env.getMap().getNode(p_over);
2421 if(content_buildable_to(n2.d) == false)
2423 // Client probably has wrong data.
2424 // Set block not sent, so that client will get
2426 dstream<<"Client "<<peer_id<<" tried to place"
2427 <<" node in invalid position; setting"
2428 <<" MapBlock not sent."<<std::endl;
2429 RemoteClient *client = getClient(peer_id);
2430 v3s16 blockpos = getNodeBlockPos(p_over);
2431 client->SetBlockNotSent(blockpos);
2435 catch(InvalidPositionException &e)
2437 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2438 <<" Adding block to emerge queue."
2440 m_emerge_queue.addBlock(peer_id,
2441 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2445 // Reset build time counter
2446 getClient(peer->id)->m_time_from_building = 0.0;
2449 MaterialItem *mitem = (MaterialItem*)item;
2451 n.d = mitem->getMaterial();
2452 if(content_features(n.d).wall_mounted)
2453 n.dir = packDir(p_under - p_over);
2458 core::list<u16> far_players;
2459 sendAddNode(p_over, n, 0, &far_players, 100);
2464 InventoryList *ilist = player->inventory.getList("main");
2465 if(g_settings.getBool("creative_mode") == false && ilist)
2467 // Remove from inventory and send inventory
2468 if(mitem->getCount() == 1)
2469 ilist->deleteItem(item_i);
2473 UpdateCrafting(peer_id);
2474 SendInventory(peer_id);
2480 This takes some time so it is done after the quick stuff
2482 core::map<v3s16, MapBlock*> modified_blocks;
2483 m_ignore_map_edit_events = true;
2484 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2485 m_ignore_map_edit_events = false;
2488 Set blocks not sent to far players
2490 for(core::list<u16>::Iterator
2491 i = far_players.begin();
2492 i != far_players.end(); i++)
2495 RemoteClient *client = getClient(peer_id);
2498 client->SetBlocksNotSent(modified_blocks);
2502 Calculate special events
2505 /*if(n.d == CONTENT_MESE)
2508 for(s16 z=-1; z<=1; z++)
2509 for(s16 y=-1; y<=1; y++)
2510 for(s16 x=-1; x<=1; x++)
2517 Place other item (not a block)
2521 v3s16 blockpos = getNodeBlockPos(p_over);
2524 Check that the block is loaded so that the item
2525 can properly be added to the static list too
2527 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2530 derr_server<<"Error while placing object: "
2531 "block not found"<<std::endl;
2535 dout_server<<"Placing a miscellaneous item on map"
2538 // Calculate a position for it
2539 v3f pos = intToFloat(p_over, BS);
2541 pos.Y -= BS*0.25; // let it drop a bit
2543 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2544 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2549 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2553 derr_server<<"WARNING: item resulted in NULL object, "
2554 <<"not placing onto map"
2559 // Add the object to the environment
2560 m_env.addActiveObject(obj);
2562 dout_server<<"Placed object"<<std::endl;
2564 if(g_settings.getBool("creative_mode") == false)
2566 // Delete the right amount of items from the slot
2567 u16 dropcount = item->getDropCount();
2569 // Delete item if all gone
2570 if(item->getCount() <= dropcount)
2572 if(item->getCount() < dropcount)
2573 dstream<<"WARNING: Server: dropped more items"
2574 <<" than the slot contains"<<std::endl;
2576 InventoryList *ilist = player->inventory.getList("main");
2578 // Remove from inventory and send inventory
2579 ilist->deleteItem(item_i);
2581 // Else decrement it
2583 item->remove(dropcount);
2586 UpdateCrafting(peer_id);
2587 SendInventory(peer_id);
2595 Catch invalid actions
2599 derr_server<<"WARNING: Server: Invalid action "
2600 <<action<<std::endl;
2604 else if(command == TOSERVER_RELEASE)
2613 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2616 else if(command == TOSERVER_SIGNTEXT)
2625 std::string datastring((char*)&data[2], datasize-2);
2626 std::istringstream is(datastring, std::ios_base::binary);
2629 is.read((char*)buf, 6);
2630 v3s16 blockpos = readV3S16(buf);
2631 is.read((char*)buf, 2);
2632 s16 id = readS16(buf);
2633 is.read((char*)buf, 2);
2634 u16 textlen = readU16(buf);
2636 for(u16 i=0; i<textlen; i++)
2638 is.read((char*)buf, 1);
2639 text += (char)buf[0];
2642 MapBlock *block = NULL;
2645 block = m_env.getMap().getBlockNoCreate(blockpos);
2647 catch(InvalidPositionException &e)
2649 derr_server<<"Error while setting sign text: "
2650 "block not found"<<std::endl;
2654 MapBlockObject *obj = block->getObject(id);
2657 derr_server<<"Error while setting sign text: "
2658 "object not found"<<std::endl;
2662 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2664 derr_server<<"Error while setting sign text: "
2665 "object is not a sign"<<std::endl;
2669 ((SignObject*)obj)->setText(text);
2671 obj->getBlock()->setChangedFlag();
2673 else if(command == TOSERVER_SIGNNODETEXT)
2681 std::string datastring((char*)&data[2], datasize-2);
2682 std::istringstream is(datastring, std::ios_base::binary);
2685 is.read((char*)buf, 6);
2686 v3s16 p = readV3S16(buf);
2687 is.read((char*)buf, 2);
2688 u16 textlen = readU16(buf);
2690 for(u16 i=0; i<textlen; i++)
2692 is.read((char*)buf, 1);
2693 text += (char)buf[0];
2696 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2699 if(meta->typeId() != CONTENT_SIGN_WALL)
2701 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2702 signmeta->setText(text);
2704 v3s16 blockpos = getNodeBlockPos(p);
2705 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2708 block->setChangedFlag();
2711 for(core::map<u16, RemoteClient*>::Iterator
2712 i = m_clients.getIterator();
2713 i.atEnd()==false; i++)
2715 RemoteClient *client = i.getNode()->getValue();
2716 client->SetBlockNotSent(blockpos);
2719 else if(command == TOSERVER_INVENTORY_ACTION)
2721 /*// Ignore inventory changes if in creative mode
2722 if(g_settings.getBool("creative_mode") == true)
2724 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2728 // Strip command and create a stream
2729 std::string datastring((char*)&data[2], datasize-2);
2730 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2731 std::istringstream is(datastring, std::ios_base::binary);
2733 InventoryAction *a = InventoryAction::deSerialize(is);
2738 c.current_player = player;
2741 Handle craftresult specially if not in creative mode
2743 bool disable_action = false;
2744 if(a->getType() == IACTION_MOVE
2745 && g_settings.getBool("creative_mode") == false)
2747 IMoveAction *ma = (IMoveAction*)a;
2748 if(ma->to_inv == "current_player" &&
2749 ma->from_inv == "current_player")
2751 InventoryList *rlist = player->inventory.getList("craftresult");
2753 InventoryList *clist = player->inventory.getList("craft");
2755 InventoryList *mlist = player->inventory.getList("main");
2758 Craftresult is no longer preview if something
2761 if(ma->to_list == "craftresult"
2762 && ma->from_list != "craftresult")
2764 // If it currently is a preview, remove
2766 if(player->craftresult_is_preview)
2768 rlist->deleteItem(0);
2770 player->craftresult_is_preview = false;
2773 Crafting takes place if this condition is true.
2775 if(player->craftresult_is_preview &&
2776 ma->from_list == "craftresult")
2778 player->craftresult_is_preview = false;
2779 clist->decrementMaterials(1);
2782 If the craftresult is placed on itself, move it to
2783 main inventory instead of doing the action
2785 if(ma->to_list == "craftresult"
2786 && ma->from_list == "craftresult")
2788 disable_action = true;
2790 InventoryItem *item1 = rlist->changeItem(0, NULL);
2791 mlist->addItem(item1);
2796 if(disable_action == false)
2798 // Feed action to player inventory
2806 UpdateCrafting(player->peer_id);
2807 SendInventory(player->peer_id);
2812 dstream<<"TOSERVER_INVENTORY_ACTION: "
2813 <<"InventoryAction::deSerialize() returned NULL"
2817 else if(command == TOSERVER_CHAT_MESSAGE)
2825 std::string datastring((char*)&data[2], datasize-2);
2826 std::istringstream is(datastring, std::ios_base::binary);
2829 is.read((char*)buf, 2);
2830 u16 len = readU16(buf);
2832 std::wstring message;
2833 for(u16 i=0; i<len; i++)
2835 is.read((char*)buf, 2);
2836 message += (wchar_t)readU16(buf);
2839 // Get player name of this client
2840 std::wstring name = narrow_to_wide(player->getName());
2842 // Line to send to players
2844 // Whether to send to the player that sent the line
2845 bool send_to_sender = false;
2846 // Whether to send to other players
2847 bool send_to_others = false;
2850 std::wstring commandprefix = L"/#";
2851 if(message.substr(0, commandprefix.size()) == commandprefix)
2853 line += L"Server: ";
2855 message = message.substr(commandprefix.size());
2856 // Get player name as narrow string
2857 std::string name_s = player->getName();
2858 // Convert message to narrow string
2859 std::string message_s = wide_to_narrow(message);
2860 // Operator is the single name defined in config.
2861 std::string operator_name = g_settings.get("name");
2862 bool is_operator = (operator_name != "" &&
2863 wide_to_narrow(name) == operator_name);
2864 bool valid_command = false;
2865 if(message_s == "help")
2867 line += L"-!- Available commands: ";
2871 line += L"shutdown setting time ";
2876 send_to_sender = true;
2877 valid_command = true;
2879 else if(message_s == "status")
2881 line = getStatusString();
2882 send_to_sender = true;
2883 valid_command = true;
2885 else if(is_operator)
2887 if(message_s == "shutdown")
2889 dstream<<DTIME<<" Server: Operator requested shutdown."
2891 m_shutdown_requested.set(true);
2893 line += L"*** Server shutting down (operator request)";
2894 send_to_sender = true;
2895 valid_command = true;
2897 else if(message_s.substr(0,8) == "setting ")
2899 std::string confline = message_s.substr(8);
2900 g_settings.parseConfigLine(confline);
2901 line += L"-!- Setting changed.";
2902 send_to_sender = true;
2903 valid_command = true;
2905 else if(message_s.substr(0,5) == "time ")
2907 u32 time = stoi(message_s.substr(5));
2908 m_time_of_day.set(time);
2909 m_time_of_day_send_timer = 0;
2910 line += L"-!- time_of_day changed.";
2911 send_to_sender = true;
2912 valid_command = true;
2916 if(valid_command == false)
2918 line += L"-!- Invalid command: " + message;
2919 send_to_sender = true;
2930 send_to_others = true;
2935 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2938 Send the message to clients
2940 for(core::map<u16, RemoteClient*>::Iterator
2941 i = m_clients.getIterator();
2942 i.atEnd() == false; i++)
2944 // Get client and check that it is valid
2945 RemoteClient *client = i.getNode()->getValue();
2946 assert(client->peer_id == i.getNode()->getKey());
2947 if(client->serialization_version == SER_FMT_VER_INVALID)
2951 bool sender_selected = (peer_id == client->peer_id);
2952 if(sender_selected == true && send_to_sender == false)
2954 if(sender_selected == false && send_to_others == false)
2957 SendChatMessage(client->peer_id, line);
2961 else if(command == TOSERVER_DAMAGE)
2963 if(g_settings.getBool("enable_damage"))
2965 std::string datastring((char*)&data[2], datasize-2);
2966 std::istringstream is(datastring, std::ios_base::binary);
2967 u8 damage = readU8(is);
2968 if(player->hp > damage)
2970 player->hp -= damage;
2976 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2979 v3f pos = findSpawnPos(m_env.getServerMap());
2980 player->setPosition(pos);
2982 SendMovePlayer(player);
2983 SendPlayerHP(player);
2985 //TODO: Throw items around
2989 SendPlayerHP(player);
2993 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2994 "unknown command "<<command<<std::endl;
2998 catch(SendFailedException &e)
3000 derr_server<<"Server::ProcessData(): SendFailedException: "
3006 void Server::onMapEditEvent(MapEditEvent *event)
3008 dstream<<"Server::onMapEditEvent()"<<std::endl;
3009 if(m_ignore_map_edit_events)
3011 MapEditEvent *e = event->clone();
3012 m_unsent_map_edit_queue.push_back(e);
3015 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3017 if(id == "current_player")
3019 assert(c->current_player);
3020 return &(c->current_player->inventory);
3024 std::string id0 = fn.next(":");
3026 if(id0 == "nodemeta")
3029 p.X = stoi(fn.next(","));
3030 p.Y = stoi(fn.next(","));
3031 p.Z = stoi(fn.next(","));
3032 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3034 return meta->getInventory();
3035 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3036 <<"no metadata found"<<std::endl;
3040 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3043 void Server::inventoryModified(InventoryContext *c, std::string id)
3045 if(id == "current_player")
3047 assert(c->current_player);
3049 UpdateCrafting(c->current_player->peer_id);
3050 SendInventory(c->current_player->peer_id);
3055 std::string id0 = fn.next(":");
3057 if(id0 == "nodemeta")
3060 p.X = stoi(fn.next(","));
3061 p.Y = stoi(fn.next(","));
3062 p.Z = stoi(fn.next(","));
3063 v3s16 blockpos = getNodeBlockPos(p);
3065 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3067 meta->inventoryModified();
3069 for(core::map<u16, RemoteClient*>::Iterator
3070 i = m_clients.getIterator();
3071 i.atEnd()==false; i++)
3073 RemoteClient *client = i.getNode()->getValue();
3074 client->SetBlockNotSent(blockpos);
3080 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3083 core::list<PlayerInfo> Server::getPlayerInfo()
3085 DSTACK(__FUNCTION_NAME);
3086 JMutexAutoLock envlock(m_env_mutex);
3087 JMutexAutoLock conlock(m_con_mutex);
3089 core::list<PlayerInfo> list;
3091 core::list<Player*> players = m_env.getPlayers();
3093 core::list<Player*>::Iterator i;
3094 for(i = players.begin();
3095 i != players.end(); i++)
3099 Player *player = *i;
3102 con::Peer *peer = m_con.GetPeer(player->peer_id);
3103 // Copy info from peer to info struct
3105 info.address = peer->address;
3106 info.avg_rtt = peer->avg_rtt;
3108 catch(con::PeerNotFoundException &e)
3110 // Set dummy peer info
3112 info.address = Address(0,0,0,0,0);
3116 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3117 info.position = player->getPosition();
3119 list.push_back(info);
3126 void Server::peerAdded(con::Peer *peer)
3128 DSTACK(__FUNCTION_NAME);
3129 dout_server<<"Server::peerAdded(): peer->id="
3130 <<peer->id<<std::endl;
3133 c.type = PEER_ADDED;
3134 c.peer_id = peer->id;
3136 m_peer_change_queue.push_back(c);
3139 void Server::deletingPeer(con::Peer *peer, bool timeout)
3141 DSTACK(__FUNCTION_NAME);
3142 dout_server<<"Server::deletingPeer(): peer->id="
3143 <<peer->id<<", timeout="<<timeout<<std::endl;
3146 c.type = PEER_REMOVED;
3147 c.peer_id = peer->id;
3148 c.timeout = timeout;
3149 m_peer_change_queue.push_back(c);
3156 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3158 DSTACK(__FUNCTION_NAME);
3159 std::ostringstream os(std::ios_base::binary);
3161 writeU16(os, TOCLIENT_HP);
3165 std::string s = os.str();
3166 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3168 con.Send(peer_id, 0, data, true);
3172 Non-static send methods
3175 void Server::SendObjectData(float dtime)
3177 DSTACK(__FUNCTION_NAME);
3179 core::map<v3s16, bool> stepped_blocks;
3181 for(core::map<u16, RemoteClient*>::Iterator
3182 i = m_clients.getIterator();
3183 i.atEnd() == false; i++)
3185 u16 peer_id = i.getNode()->getKey();
3186 RemoteClient *client = i.getNode()->getValue();
3187 assert(client->peer_id == peer_id);
3189 if(client->serialization_version == SER_FMT_VER_INVALID)
3192 client->SendObjectData(this, dtime, stepped_blocks);
3196 void Server::SendPlayerInfos()
3198 DSTACK(__FUNCTION_NAME);
3200 //JMutexAutoLock envlock(m_env_mutex);
3202 // Get connected players
3203 core::list<Player*> players = m_env.getPlayers(true);
3205 u32 player_count = players.getSize();
3206 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3208 SharedBuffer<u8> data(datasize);
3209 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3212 core::list<Player*>::Iterator i;
3213 for(i = players.begin();
3214 i != players.end(); i++)
3216 Player *player = *i;
3218 /*dstream<<"Server sending player info for player with "
3219 "peer_id="<<player->peer_id<<std::endl;*/
3221 writeU16(&data[start], player->peer_id);
3222 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3223 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3224 start += 2+PLAYERNAME_SIZE;
3227 //JMutexAutoLock conlock(m_con_mutex);
3230 m_con.SendToAll(0, data, true);
3233 void Server::SendInventory(u16 peer_id)
3235 DSTACK(__FUNCTION_NAME);
3237 Player* player = m_env.getPlayer(peer_id);
3244 std::ostringstream os;
3245 //os.imbue(std::locale("C"));
3247 player->inventory.serialize(os);
3249 std::string s = os.str();
3251 SharedBuffer<u8> data(s.size()+2);
3252 writeU16(&data[0], TOCLIENT_INVENTORY);
3253 memcpy(&data[2], s.c_str(), s.size());
3256 m_con.Send(peer_id, 0, data, true);
3259 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3261 DSTACK(__FUNCTION_NAME);
3263 std::ostringstream os(std::ios_base::binary);
3267 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3268 os.write((char*)buf, 2);
3271 writeU16(buf, message.size());
3272 os.write((char*)buf, 2);
3275 for(u32 i=0; i<message.size(); i++)
3279 os.write((char*)buf, 2);
3283 std::string s = os.str();
3284 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3286 m_con.Send(peer_id, 0, data, true);
3289 void Server::BroadcastChatMessage(const std::wstring &message)
3291 for(core::map<u16, RemoteClient*>::Iterator
3292 i = m_clients.getIterator();
3293 i.atEnd() == false; i++)
3295 // Get client and check that it is valid
3296 RemoteClient *client = i.getNode()->getValue();
3297 assert(client->peer_id == i.getNode()->getKey());
3298 if(client->serialization_version == SER_FMT_VER_INVALID)
3301 SendChatMessage(client->peer_id, message);
3305 void Server::SendPlayerHP(Player *player)
3307 SendHP(m_con, player->peer_id, player->hp);
3310 void Server::SendMovePlayer(Player *player)
3312 DSTACK(__FUNCTION_NAME);
3313 std::ostringstream os(std::ios_base::binary);
3315 writeU16(os, TOCLIENT_MOVE_PLAYER);
3316 writeV3F1000(os, player->getPosition());
3317 writeF1000(os, player->getPitch());
3318 writeF1000(os, player->getYaw());
3321 v3f pos = player->getPosition();
3322 f32 pitch = player->getPitch();
3323 f32 yaw = player->getYaw();
3324 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3325 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3332 std::string s = os.str();
3333 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3335 m_con.Send(player->peer_id, 0, data, true);
3338 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3339 core::list<u16> *far_players, float far_d_nodes)
3341 float maxd = far_d_nodes*BS;
3342 v3f p_f = intToFloat(p, BS);
3346 SharedBuffer<u8> reply(replysize);
3347 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3348 writeS16(&reply[2], p.X);
3349 writeS16(&reply[4], p.Y);
3350 writeS16(&reply[6], p.Z);
3352 for(core::map<u16, RemoteClient*>::Iterator
3353 i = m_clients.getIterator();
3354 i.atEnd() == false; i++)
3356 // Get client and check that it is valid
3357 RemoteClient *client = i.getNode()->getValue();
3358 assert(client->peer_id == i.getNode()->getKey());
3359 if(client->serialization_version == SER_FMT_VER_INVALID)
3362 // Don't send if it's the same one
3363 if(client->peer_id == ignore_id)
3369 Player *player = m_env.getPlayer(client->peer_id);
3372 // If player is far away, only set modified blocks not sent
3373 v3f player_pos = player->getPosition();
3374 if(player_pos.getDistanceFrom(p_f) > maxd)
3376 far_players->push_back(client->peer_id);
3383 m_con.Send(client->peer_id, 0, reply, true);
3387 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3388 core::list<u16> *far_players, float far_d_nodes)
3390 float maxd = far_d_nodes*BS;
3391 v3f p_f = intToFloat(p, BS);
3393 for(core::map<u16, RemoteClient*>::Iterator
3394 i = m_clients.getIterator();
3395 i.atEnd() == false; i++)
3397 // Get client and check that it is valid
3398 RemoteClient *client = i.getNode()->getValue();
3399 assert(client->peer_id == i.getNode()->getKey());
3400 if(client->serialization_version == SER_FMT_VER_INVALID)
3403 // Don't send if it's the same one
3404 if(client->peer_id == ignore_id)
3410 Player *player = m_env.getPlayer(client->peer_id);
3413 // If player is far away, only set modified blocks not sent
3414 v3f player_pos = player->getPosition();
3415 if(player_pos.getDistanceFrom(p_f) > maxd)
3417 far_players->push_back(client->peer_id);
3424 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3425 SharedBuffer<u8> reply(replysize);
3426 writeU16(&reply[0], TOCLIENT_ADDNODE);
3427 writeS16(&reply[2], p.X);
3428 writeS16(&reply[4], p.Y);
3429 writeS16(&reply[6], p.Z);
3430 n.serialize(&reply[8], client->serialization_version);
3433 m_con.Send(client->peer_id, 0, reply, true);
3437 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3439 DSTACK(__FUNCTION_NAME);
3441 v3s16 p = block->getPos();
3445 bool completely_air = true;
3446 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3447 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3448 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3450 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3452 completely_air = false;
3453 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3458 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3460 dstream<<"[completely air] ";
3465 Create a packet with the block in the right format
3468 std::ostringstream os(std::ios_base::binary);
3469 block->serialize(os, ver);
3470 std::string s = os.str();
3471 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3473 u32 replysize = 8 + blockdata.getSize();
3474 SharedBuffer<u8> reply(replysize);
3475 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3476 writeS16(&reply[2], p.X);
3477 writeS16(&reply[4], p.Y);
3478 writeS16(&reply[6], p.Z);
3479 memcpy(&reply[8], *blockdata, blockdata.getSize());
3481 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3482 <<": \tpacket size: "<<replysize<<std::endl;*/
3487 m_con.Send(peer_id, 1, reply, true);
3490 void Server::SendBlocks(float dtime)
3492 DSTACK(__FUNCTION_NAME);
3494 JMutexAutoLock envlock(m_env_mutex);
3495 JMutexAutoLock conlock(m_con_mutex);
3497 //TimeTaker timer("Server::SendBlocks");
3499 core::array<PrioritySortedBlockTransfer> queue;
3501 s32 total_sending = 0;
3503 for(core::map<u16, RemoteClient*>::Iterator
3504 i = m_clients.getIterator();
3505 i.atEnd() == false; i++)
3507 RemoteClient *client = i.getNode()->getValue();
3508 assert(client->peer_id == i.getNode()->getKey());
3510 total_sending += client->SendingCount();
3512 if(client->serialization_version == SER_FMT_VER_INVALID)
3515 client->GetNextBlocks(this, dtime, queue);
3519 // Lowest priority number comes first.
3520 // Lowest is most important.
3523 for(u32 i=0; i<queue.size(); i++)
3525 //TODO: Calculate limit dynamically
3526 if(total_sending >= g_settings.getS32
3527 ("max_simultaneous_block_sends_server_total"))
3530 PrioritySortedBlockTransfer q = queue[i];
3532 MapBlock *block = NULL;
3535 block = m_env.getMap().getBlockNoCreate(q.pos);
3537 catch(InvalidPositionException &e)
3542 RemoteClient *client = getClient(q.peer_id);
3544 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3546 client->SentBlock(q.pos);
3556 void Server::UpdateCrafting(u16 peer_id)
3558 DSTACK(__FUNCTION_NAME);
3560 Player* player = m_env.getPlayer(peer_id);
3564 Calculate crafting stuff
3566 if(g_settings.getBool("creative_mode") == false)
3568 InventoryList *clist = player->inventory.getList("craft");
3569 InventoryList *rlist = player->inventory.getList("craftresult");
3571 if(rlist->getUsedSlots() == 0)
3572 player->craftresult_is_preview = true;
3574 if(rlist && player->craftresult_is_preview)
3576 rlist->clearItems();
3578 if(clist && rlist && player->craftresult_is_preview)
3580 InventoryItem *items[9];
3581 for(u16 i=0; i<9; i++)
3583 items[i] = clist->getItem(i);
3592 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3593 if(checkItemCombination(items, specs))
3595 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3604 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3605 if(checkItemCombination(items, specs))
3607 rlist->addItem(new CraftItem("Stick", 4));
3616 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3617 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3618 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3619 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3620 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3621 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3622 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3623 if(checkItemCombination(items, specs))
3625 //rlist->addItem(new MapBlockObjectItem("Sign"));
3626 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3635 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3636 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3637 if(checkItemCombination(items, specs))
3639 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3648 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3649 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3650 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3651 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3652 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3653 if(checkItemCombination(items, specs))
3655 rlist->addItem(new ToolItem("WPick", 0));
3664 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3665 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3666 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3667 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3668 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3669 if(checkItemCombination(items, specs))
3671 rlist->addItem(new ToolItem("STPick", 0));
3680 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3681 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3682 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3683 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3684 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3685 if(checkItemCombination(items, specs))
3687 rlist->addItem(new ToolItem("SteelPick", 0));
3696 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3697 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3698 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3699 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3700 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3701 if(checkItemCombination(items, specs))
3703 rlist->addItem(new ToolItem("MesePick", 0));
3712 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3713 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3714 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3715 if(checkItemCombination(items, specs))
3717 rlist->addItem(new ToolItem("WShovel", 0));
3726 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3727 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3728 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3729 if(checkItemCombination(items, specs))
3731 rlist->addItem(new ToolItem("STShovel", 0));
3740 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3741 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3742 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3743 if(checkItemCombination(items, specs))
3745 rlist->addItem(new ToolItem("SteelShovel", 0));
3754 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3755 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3756 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3757 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3758 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3759 if(checkItemCombination(items, specs))
3761 rlist->addItem(new ToolItem("WAxe", 0));
3770 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3771 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3772 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3773 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3774 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3775 if(checkItemCombination(items, specs))
3777 rlist->addItem(new ToolItem("STAxe", 0));
3786 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3787 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3788 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3789 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3790 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3791 if(checkItemCombination(items, specs))
3793 rlist->addItem(new ToolItem("SteelAxe", 0));
3802 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3803 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3804 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3805 if(checkItemCombination(items, specs))
3807 rlist->addItem(new ToolItem("WSword", 0));
3816 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3817 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3818 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3819 if(checkItemCombination(items, specs))
3821 rlist->addItem(new ToolItem("STSword", 0));
3830 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3831 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3832 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3833 if(checkItemCombination(items, specs))
3835 rlist->addItem(new ToolItem("SteelSword", 0));
3844 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3845 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3846 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3847 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3848 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3849 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3850 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3851 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3852 if(checkItemCombination(items, specs))
3854 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3863 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3864 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3865 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3866 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3867 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3868 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3869 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3870 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3871 if(checkItemCombination(items, specs))
3873 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3882 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3883 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3884 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3885 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3886 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3887 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3888 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3889 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3890 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3891 if(checkItemCombination(items, specs))
3893 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3899 } // if creative_mode == false
3902 RemoteClient* Server::getClient(u16 peer_id)
3904 DSTACK(__FUNCTION_NAME);
3905 //JMutexAutoLock lock(m_con_mutex);
3906 core::map<u16, RemoteClient*>::Node *n;
3907 n = m_clients.find(peer_id);
3908 // A client should exist for all peers
3910 return n->getValue();
3913 std::wstring Server::getStatusString()
3915 std::wostringstream os(std::ios_base::binary);
3918 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3920 os<<L", uptime="<<m_uptime.get();
3921 // Information about clients
3923 for(core::map<u16, RemoteClient*>::Iterator
3924 i = m_clients.getIterator();
3925 i.atEnd() == false; i++)
3927 // Get client and check that it is valid
3928 RemoteClient *client = i.getNode()->getValue();
3929 assert(client->peer_id == i.getNode()->getKey());
3930 if(client->serialization_version == SER_FMT_VER_INVALID)
3933 Player *player = m_env.getPlayer(client->peer_id);
3934 // Get name of player
3935 std::wstring name = L"unknown";
3937 name = narrow_to_wide(player->getName());
3938 // Add name to information string
3942 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3943 os<<" WARNING: Map saving is disabled."<<std::endl;
3948 void setCreativeInventory(Player *player)
3950 player->resetInventory();
3952 // Give some good tools
3954 InventoryItem *item = new ToolItem("MesePick", 0);
3955 void* r = player->inventory.addItem("main", item);
3959 InventoryItem *item = new ToolItem("SteelPick", 0);
3960 void* r = player->inventory.addItem("main", item);
3964 InventoryItem *item = new ToolItem("SteelAxe", 0);
3965 void* r = player->inventory.addItem("main", item);
3969 InventoryItem *item = new ToolItem("SteelShovel", 0);
3970 void* r = player->inventory.addItem("main", item);
3978 // CONTENT_IGNORE-terminated list
3979 u8 material_items[] = {
3988 CONTENT_WATERSOURCE,
3996 u8 *mip = material_items;
3997 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3999 if(*mip == CONTENT_IGNORE)
4002 InventoryItem *item = new MaterialItem(*mip, 1);
4003 player->inventory.addItem("main", item);
4009 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4012 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4013 player->inventory.addItem("main", item);
4016 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4018 // Skip some materials
4019 if(i == CONTENT_WATER || i == CONTENT_TORCH
4020 || i == CONTENT_COALSTONE)
4023 InventoryItem *item = new MaterialItem(i, 1);
4024 player->inventory.addItem("main", item);
4030 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4031 void* r = player->inventory.addItem("main", item);
4036 v3f findSpawnPos(ServerMap &map)
4038 //return v3f(50,50,50)*BS;
4041 s16 groundheight = 0;
4043 // Try to find a good place a few times
4044 for(s32 i=0; i<1000; i++)
4047 // We're going to try to throw the player to this position
4048 nodepos = v2s16(-range + (myrand()%(range*2)),
4049 -range + (myrand()%(range*2)));
4050 v2s16 sectorpos = getNodeSectorPos(nodepos);
4051 // Get sector (NOTE: Don't get because it's slow)
4052 //m_env.getMap().emergeSector(sectorpos);
4053 // Get ground height at point (fallbacks to heightmap function)
4054 groundheight = map.findGroundLevel(nodepos);
4055 // Don't go underwater
4056 if(groundheight < WATER_LEVEL)
4058 //dstream<<"-> Underwater"<<std::endl;
4061 // Don't go to high places
4062 if(groundheight > WATER_LEVEL + 4)
4064 //dstream<<"-> Underwater"<<std::endl;
4068 // Found a good place
4069 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4073 // If no suitable place was not found, go above water at least.
4074 if(groundheight < WATER_LEVEL)
4075 groundheight = WATER_LEVEL;
4077 return intToFloat(v3s16(
4084 Player *Server::emergePlayer(const char *name, const char *password,
4088 Try to get an existing player
4090 Player *player = m_env.getPlayer(name);
4093 // If player is already connected, cancel
4094 if(player->peer_id != 0)
4096 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4101 player->peer_id = peer_id;
4103 // Reset inventory to creative if in creative mode
4104 if(g_settings.getBool("creative_mode"))
4106 setCreativeInventory(player);
4113 If player with the wanted peer_id already exists, cancel.
4115 if(m_env.getPlayer(peer_id) != NULL)
4117 dstream<<"emergePlayer(): Player with wrong name but same"
4118 " peer_id already exists"<<std::endl;
4126 player = new ServerRemotePlayer();
4127 //player->peer_id = c.peer_id;
4128 //player->peer_id = PEER_ID_INEXISTENT;
4129 player->peer_id = peer_id;
4130 player->updateName(name);
4136 dstream<<"Server: Finding spawn place for player \""
4137 <<player->getName()<<"\""<<std::endl;
4139 v3f pos = findSpawnPos(m_env.getServerMap());
4141 player->setPosition(pos);
4144 Add player to environment
4147 m_env.addPlayer(player);
4150 Add stuff to inventory
4153 if(g_settings.getBool("creative_mode"))
4155 setCreativeInventory(player);
4157 else if(g_settings.getBool("give_initial_stuff"))
4160 InventoryItem *item = new ToolItem("SteelPick", 0);
4161 void* r = player->inventory.addItem("main", item);
4165 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4166 void* r = player->inventory.addItem("main", item);
4170 InventoryItem *item = new ToolItem("SteelAxe", 0);
4171 void* r = player->inventory.addItem("main", item);
4175 InventoryItem *item = new ToolItem("SteelShovel", 0);
4176 void* r = player->inventory.addItem("main", item);
4180 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4181 void* r = player->inventory.addItem("main", item);
4185 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4186 void* r = player->inventory.addItem("main", item);
4190 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4191 void* r = player->inventory.addItem("main", item);
4195 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4196 void* r = player->inventory.addItem("main", item);
4200 InventoryItem *item = new CraftItem("Stick", 4);
4201 void* r = player->inventory.addItem("main", item);
4205 InventoryItem *item = new ToolItem("WPick", 32000);
4206 void* r = player->inventory.addItem("main", item);
4210 InventoryItem *item = new ToolItem("STPick", 32000);
4211 void* r = player->inventory.addItem("main", item);
4215 for(u16 i=0; i<4; i++)
4217 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4218 bool r = player->inventory.addItem("main", item);
4221 /*// Give some other stuff
4223 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4224 bool r = player->inventory.addItem("main", item);
4231 } // create new player
4234 void Server::handlePeerChange(PeerChange &c)
4236 JMutexAutoLock envlock(m_env_mutex);
4237 JMutexAutoLock conlock(m_con_mutex);
4239 if(c.type == PEER_ADDED)
4246 core::map<u16, RemoteClient*>::Node *n;
4247 n = m_clients.find(c.peer_id);
4248 // The client shouldn't already exist
4252 RemoteClient *client = new RemoteClient();
4253 client->peer_id = c.peer_id;
4254 m_clients.insert(client->peer_id, client);
4257 else if(c.type == PEER_REMOVED)
4264 core::map<u16, RemoteClient*>::Node *n;
4265 n = m_clients.find(c.peer_id);
4266 // The client should exist
4270 Mark objects to be not known by the client
4272 RemoteClient *client = n->getValue();
4274 for(core::map<u16, bool>::Iterator
4275 i = client->m_known_objects.getIterator();
4276 i.atEnd()==false; i++)
4279 u16 id = i.getNode()->getKey();
4280 ServerActiveObject* obj = m_env.getActiveObject(id);
4282 if(obj && obj->m_known_by_count > 0)
4283 obj->m_known_by_count--;
4286 // Collect information about leaving in chat
4287 std::wstring message;
4289 std::wstring name = L"unknown";
4290 Player *player = m_env.getPlayer(c.peer_id);
4292 name = narrow_to_wide(player->getName());
4296 message += L" left game";
4298 message += L" (timed out)";
4303 m_env.removePlayer(c.peer_id);
4306 // Set player client disconnected
4308 Player *player = m_env.getPlayer(c.peer_id);
4310 player->peer_id = 0;
4314 delete m_clients[c.peer_id];
4315 m_clients.remove(c.peer_id);
4317 // Send player info to all remaining clients
4320 // Send leave chat message to all remaining clients
4321 BroadcastChatMessage(message);
4330 void Server::handlePeerChanges()
4332 while(m_peer_change_queue.size() > 0)
4334 PeerChange c = m_peer_change_queue.pop_front();
4336 dout_server<<"Server: Handling peer change: "
4337 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4340 handlePeerChange(c);
4344 void dedicated_server_loop(Server &server, bool &kill)
4346 DSTACK(__FUNCTION_NAME);
4348 std::cout<<DTIME<<std::endl;
4349 std::cout<<"========================"<<std::endl;
4350 std::cout<<"Running dedicated server"<<std::endl;
4351 std::cout<<"========================"<<std::endl;
4352 std::cout<<std::endl;
4356 // This is kind of a hack but can be done like this
4357 // because server.step() is very light
4361 if(server.getShutdownRequested() || kill)
4363 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4367 static int counter = 0;
4373 core::list<PlayerInfo> list = server.getPlayerInfo();
4374 core::list<PlayerInfo>::Iterator i;
4375 static u32 sum_old = 0;
4376 u32 sum = PIChecksum(list);
4379 std::cout<<DTIME<<"Player info:"<<std::endl;
4380 for(i=list.begin(); i!=list.end(); i++)
4382 i->PrintLine(&std::cout);