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"
32 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
34 void * ServerThread::Thread()
38 DSTACK(__FUNCTION_NAME);
40 BEGIN_DEBUG_EXCEPTION_HANDLER
45 //TimeTaker timer("AsyncRunStep() + Receive()");
48 //TimeTaker timer("AsyncRunStep()");
49 m_server->AsyncRunStep();
52 //dout_server<<"Running m_server->Receive()"<<std::endl;
55 catch(con::NoIncomingDataException &e)
58 catch(con::PeerNotFoundException &e)
60 dout_server<<"Server: PeerNotFoundException"<<std::endl;
64 END_DEBUG_EXCEPTION_HANDLER
69 void * EmergeThread::Thread()
73 DSTACK(__FUNCTION_NAME);
77 BEGIN_DEBUG_EXCEPTION_HANDLER
80 Get block info from queue, emerge them and send them
83 After queue is empty, exit.
87 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
91 SharedPtr<QueuedBlockEmerge> q(qptr);
95 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
97 //TimeTaker timer("block emerge");
100 Try to emerge it from somewhere.
102 If it is only wanted as optional, only loading from disk
107 Check if any peer wants it as non-optional. In that case it
110 Also decrement the emerge queue count in clients.
113 bool optional = true;
116 core::map<u16, u8>::Iterator i;
117 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
119 //u16 peer_id = i.getNode()->getKey();
122 u8 flags = i.getNode()->getValue();
123 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
129 /*dstream<<"EmergeThread: p="
130 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
131 <<"optional="<<optional<<std::endl;*/
133 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
135 core::map<v3s16, MapBlock*> changed_blocks;
136 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
138 MapBlock *block = NULL;
139 bool got_block = true;
140 core::map<v3s16, MapBlock*> modified_blocks;
144 //TimeTaker envlockwaittimer("block emerge envlock wait time");
147 JMutexAutoLock envlock(m_server->m_env_mutex);
149 //envlockwaittimer.stop();
151 //TimeTaker timer("block emerge (while env locked)");
154 bool only_from_disk = false;
157 only_from_disk = true;
159 // First check if the block already exists
160 //block = map.getBlockNoCreate(p);
164 //dstream<<"Calling emergeBlock"<<std::endl;
165 block = map.emergeBlock(
169 lighting_invalidated_blocks);
173 While we're at it, generate some other blocks too
181 lighting_invalidated_blocks);
186 lighting_invalidated_blocks);
188 catch(InvalidPositionException &e)
194 // If it is a dummy, block was not found on disk
197 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
200 if(only_from_disk == false)
202 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
207 catch(InvalidPositionException &e)
210 // This happens when position is over limit.
216 if(debug && changed_blocks.size() > 0)
218 dout_server<<DTIME<<"Got changed_blocks: ";
219 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
220 i.atEnd() == false; i++)
222 MapBlock *block = i.getNode()->getValue();
223 v3s16 p = block->getPos();
224 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
226 dout_server<<std::endl;
230 Collect a list of blocks that have been modified in
231 addition to the fetched one.
234 if(lighting_invalidated_blocks.size() > 0)
236 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
237 <<" blocks"<<std::endl;*/
239 // 50-100ms for single block generation
240 //TimeTaker timer("** EmergeThread updateLighting");
242 // Update lighting without locking the environment mutex,
243 // add modified blocks to changed blocks
244 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
247 // Add all from changed_blocks to modified_blocks
248 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
249 i.atEnd() == false; i++)
251 MapBlock *block = i.getNode()->getValue();
252 modified_blocks.insert(block->getPos(), block);
255 // If we got no block, there should be no invalidated blocks
258 assert(lighting_invalidated_blocks.size() == 0);
264 Set sent status of modified blocks on clients
267 // NOTE: Server's clients are also behind the connection mutex
268 JMutexAutoLock lock(m_server->m_con_mutex);
271 Add the originally fetched block to the modified list
275 modified_blocks.insert(p, block);
279 Set the modified blocks unsent for all the clients
282 for(core::map<u16, RemoteClient*>::Iterator
283 i = m_server->m_clients.getIterator();
284 i.atEnd() == false; i++)
286 RemoteClient *client = i.getNode()->getValue();
288 if(modified_blocks.size() > 0)
290 // Remove block from sent history
291 client->SetBlocksNotSent(modified_blocks);
297 END_DEBUG_EXCEPTION_HANDLER
302 void RemoteClient::GetNextBlocks(Server *server, float dtime,
303 core::array<PrioritySortedBlockTransfer> &dest)
305 DSTACK(__FUNCTION_NAME);
308 m_nearest_unsent_reset_timer += dtime;
310 // Won't send anything if already sending
311 if(m_blocks_sending.size() >= g_settings.getU16
312 ("max_simultaneous_block_sends_per_client"))
314 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
318 Player *player = server->m_env.getPlayer(peer_id);
320 assert(player != NULL);
322 v3f playerpos = player->getPosition();
323 v3f playerspeed = player->getSpeed();
325 v3s16 center_nodepos = floatToInt(playerpos, BS);
327 v3s16 center = getNodeBlockPos(center_nodepos);
329 // Camera position and direction
331 playerpos + v3f(0, BS+BS/2, 0);
332 v3f camera_dir = v3f(0,0,1);
333 camera_dir.rotateYZBy(player->getPitch());
334 camera_dir.rotateXZBy(player->getYaw());
337 Get the starting value of the block finder radius.
339 s16 last_nearest_unsent_d;
342 if(m_last_center != center)
344 m_nearest_unsent_d = 0;
345 m_last_center = center;
348 /*dstream<<"m_nearest_unsent_reset_timer="
349 <<m_nearest_unsent_reset_timer<<std::endl;*/
350 if(m_nearest_unsent_reset_timer > 5.0)
352 m_nearest_unsent_reset_timer = 0;
353 m_nearest_unsent_d = 0;
354 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
357 last_nearest_unsent_d = m_nearest_unsent_d;
359 d_start = m_nearest_unsent_d;
361 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
362 ("max_simultaneous_block_sends_per_client");
363 u16 maximum_simultaneous_block_sends =
364 maximum_simultaneous_block_sends_setting;
367 Check the time from last addNode/removeNode.
369 Decrease send rate if player is building stuff.
371 m_time_from_building += dtime;
372 if(m_time_from_building < g_settings.getFloat(
373 "full_block_send_enable_min_time_from_building"))
375 maximum_simultaneous_block_sends
376 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
379 u32 num_blocks_selected = m_blocks_sending.size();
382 next time d will be continued from the d from which the nearest
383 unsent block was found this time.
385 This is because not necessarily any of the blocks found this
386 time are actually sent.
388 s32 new_nearest_unsent_d = -1;
390 s16 d_max = g_settings.getS16("max_block_send_distance");
391 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
393 //dstream<<"Starting from "<<d_start<<std::endl;
395 for(s16 d = d_start; d <= d_max; d++)
397 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
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;
412 Get the border/face dot coordinates of a "d-radiused"
415 core::list<v3s16> list;
416 getFacePositions(list, d);
418 core::list<v3s16>::Iterator li;
419 for(li=list.begin(); li!=list.end(); li++)
421 v3s16 p = *li + center;
425 - Don't allow too many simultaneous transfers
426 - EXCEPT when the blocks are very close
428 Also, don't send blocks that are already flying.
431 u16 maximum_simultaneous_block_sends_now =
432 maximum_simultaneous_block_sends;
434 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
436 maximum_simultaneous_block_sends_now =
437 maximum_simultaneous_block_sends_setting;
440 // Limit is dynamically lowered when building
441 if(num_blocks_selected
442 >= maximum_simultaneous_block_sends_now)
444 /*dstream<<"Not sending more blocks. Queue full. "
445 <<m_blocks_sending.size()
450 if(m_blocks_sending.find(p) != NULL)
456 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
457 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
458 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
459 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
460 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
461 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
464 // If this is true, inexistent block will be made from scratch
465 bool generate = d <= d_max_gen;
468 /*// Limit the generating area vertically to 2/3
469 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
472 // Limit the send area vertically to 2/3
473 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
479 If block is far away, don't generate it unless it is
482 NOTE: We can't know the ground level this way with the
488 MapSector *sector = NULL;
491 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
493 catch(InvalidPositionException &e)
499 // Get center ground height in nodes
500 f32 gh = sector->getGroundHeight(
501 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
502 // Block center y in nodes
503 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
504 // If differs a lot, don't generate
505 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
512 Don't generate or send if not in sight
515 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
521 Don't send already sent blocks
524 if(m_blocks_sent.find(p) != NULL)
529 Check if map has this block
531 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
533 bool surely_not_found_on_disk = false;
534 bool block_is_invalid = false;
539 surely_not_found_on_disk = true;
542 if(block->isValid() == false)
544 block_is_invalid = true;
547 /*if(block->isFullyGenerated() == false)
549 block_is_invalid = true;
553 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
554 v2s16 chunkpos = map->sector_to_chunk(p2d);
555 if(map->chunkNonVolatile(chunkpos) == false)
556 block_is_invalid = true;
560 If block has been marked to not exist on disk (dummy)
561 and generating new ones is not wanted, skip block.
563 if(generate == false && surely_not_found_on_disk == true)
570 Record the lowest d from which a a block has been
571 found being not sent and possibly to exist
573 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
575 new_nearest_unsent_d = d;
579 Add inexistent block to emerge queue.
581 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
583 //TODO: Get value from somewhere
584 // Allow only one block in emerge queue
585 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
586 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
588 //dstream<<"Adding block to emerge queue"<<std::endl;
590 // Add it to the emerge queue and trigger the thread
593 if(generate == false)
594 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
596 server->m_emerge_queue.addBlock(peer_id, p, flags);
597 server->m_emergethread.trigger();
605 Add block to send queue
608 PrioritySortedBlockTransfer q((float)d, p, peer_id);
612 num_blocks_selected += 1;
617 if(new_nearest_unsent_d != -1)
619 m_nearest_unsent_d = new_nearest_unsent_d;
623 void RemoteClient::SendObjectData(
626 core::map<v3s16, bool> &stepped_blocks
629 DSTACK(__FUNCTION_NAME);
631 // Can't send anything without knowing version
632 if(serialization_version == SER_FMT_VER_INVALID)
634 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
640 Send a TOCLIENT_OBJECTDATA packet.
644 u16 number of player positions
655 std::ostringstream os(std::ios_base::binary);
659 writeU16(buf, TOCLIENT_OBJECTDATA);
660 os.write((char*)buf, 2);
663 Get and write player data
666 // Get connected players
667 core::list<Player*> players = server->m_env.getPlayers(true);
669 // Write player count
670 u16 playercount = players.size();
671 writeU16(buf, playercount);
672 os.write((char*)buf, 2);
674 core::list<Player*>::Iterator i;
675 for(i = players.begin();
676 i != players.end(); i++)
680 v3f pf = player->getPosition();
681 v3f sf = player->getSpeed();
683 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
684 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
685 s32 pitch_i (player->getPitch() * 100);
686 s32 yaw_i (player->getYaw() * 100);
688 writeU16(buf, player->peer_id);
689 os.write((char*)buf, 2);
690 writeV3S32(buf, position_i);
691 os.write((char*)buf, 12);
692 writeV3S32(buf, speed_i);
693 os.write((char*)buf, 12);
694 writeS32(buf, pitch_i);
695 os.write((char*)buf, 4);
696 writeS32(buf, yaw_i);
697 os.write((char*)buf, 4);
701 Get and write object data
707 For making players to be able to build to their nearby
708 environment (building is not possible on blocks that are not
711 - Add blocks to emerge queue if they are not found
713 SUGGESTION: These could be ignored from the backside of the player
716 Player *player = server->m_env.getPlayer(peer_id);
720 v3f playerpos = player->getPosition();
721 v3f playerspeed = player->getSpeed();
723 v3s16 center_nodepos = floatToInt(playerpos, BS);
724 v3s16 center = getNodeBlockPos(center_nodepos);
726 s16 d_max = g_settings.getS16("active_object_range");
728 // Number of blocks whose objects were written to bos
731 std::ostringstream bos(std::ios_base::binary);
733 for(s16 d = 0; d <= d_max; d++)
735 core::list<v3s16> list;
736 getFacePositions(list, d);
738 core::list<v3s16>::Iterator li;
739 for(li=list.begin(); li!=list.end(); li++)
741 v3s16 p = *li + center;
744 Ignore blocks that haven't been sent to the client
747 if(m_blocks_sent.find(p) == NULL)
751 // Try stepping block and add it to a send queue
756 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
759 Step block if not in stepped_blocks and add to stepped_blocks.
761 if(stepped_blocks.find(p) == NULL)
763 block->stepObjects(dtime, true, server->getDayNightRatio());
764 stepped_blocks.insert(p, true);
765 block->setChangedFlag();
768 // Skip block if there are no objects
769 if(block->getObjectCount() == 0)
778 bos.write((char*)buf, 6);
781 block->serializeObjects(bos, serialization_version);
786 Stop collecting objects if data is already too big
788 // Sum of player and object data sizes
789 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
790 // break out if data too big
791 if(sum > MAX_OBJECTDATA_SIZE)
793 goto skip_subsequent;
797 catch(InvalidPositionException &e)
800 // Add it to the emerge queue and trigger the thread.
801 // Fetch the block only if it is on disk.
803 // Grab and increment counter
804 /*SharedPtr<JMutexAutoLock> lock
805 (m_num_blocks_in_emerge_queue.getLock());
806 m_num_blocks_in_emerge_queue.m_value++;*/
808 // Add to queue as an anonymous fetch from disk
809 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
810 server->m_emerge_queue.addBlock(0, p, flags);
811 server->m_emergethread.trigger();
819 writeU16(buf, blockcount);
820 os.write((char*)buf, 2);
822 // Write block objects
829 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
832 std::string s = os.str();
833 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
834 // Send as unreliable
835 server->m_con.Send(peer_id, 0, data, false);
838 void RemoteClient::GotBlock(v3s16 p)
840 if(m_blocks_sending.find(p) != NULL)
841 m_blocks_sending.remove(p);
844 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
845 " m_blocks_sending"<<std::endl;*/
846 m_excess_gotblocks++;
848 m_blocks_sent.insert(p, true);
851 void RemoteClient::SentBlock(v3s16 p)
853 if(m_blocks_sending.find(p) == NULL)
854 m_blocks_sending.insert(p, 0.0);
856 dstream<<"RemoteClient::SentBlock(): Sent block"
857 " already in m_blocks_sending"<<std::endl;
860 void RemoteClient::SetBlockNotSent(v3s16 p)
862 m_nearest_unsent_d = 0;
864 if(m_blocks_sending.find(p) != NULL)
865 m_blocks_sending.remove(p);
866 if(m_blocks_sent.find(p) != NULL)
867 m_blocks_sent.remove(p);
870 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
872 m_nearest_unsent_d = 0;
874 for(core::map<v3s16, MapBlock*>::Iterator
875 i = blocks.getIterator();
876 i.atEnd()==false; i++)
878 v3s16 p = i.getNode()->getKey();
880 if(m_blocks_sending.find(p) != NULL)
881 m_blocks_sending.remove(p);
882 if(m_blocks_sent.find(p) != NULL)
883 m_blocks_sent.remove(p);
891 PlayerInfo::PlayerInfo()
896 void PlayerInfo::PrintLine(std::ostream *s)
899 (*s)<<"\""<<name<<"\" ("
900 <<(position.X/10)<<","<<(position.Y/10)
901 <<","<<(position.Z/10)<<") ";
903 (*s)<<" avg_rtt="<<avg_rtt;
907 u32 PIChecksum(core::list<PlayerInfo> &l)
909 core::list<PlayerInfo>::Iterator i;
912 for(i=l.begin(); i!=l.end(); i++)
914 checksum += a * (i->id+1);
915 checksum ^= 0x435aafcd;
926 std::string mapsavedir
928 m_env(new ServerMap(mapsavedir), this),
929 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
931 m_emergethread(this),
934 m_time_of_day_send_timer(0),
936 m_mapsavedir(mapsavedir),
937 m_shutdown_requested(false),
938 m_ignore_map_edit_events(false),
939 m_ignore_map_edit_events_peer_id(0)
941 m_liquid_transform_timer = 0.0;
942 m_print_info_timer = 0.0;
943 m_objectdata_timer = 0.0;
944 m_emergethread_trigger_timer = 0.0;
945 m_savemap_timer = 0.0;
949 m_step_dtime_mutex.Init();
952 m_env.getMap().addEventReceiver(this);
955 m_env.deSerializePlayers(m_mapsavedir);
961 Send shutdown message
964 JMutexAutoLock conlock(m_con_mutex);
966 std::wstring line = L"*** Server shutting down";
969 Send the message to clients
971 for(core::map<u16, RemoteClient*>::Iterator
972 i = m_clients.getIterator();
973 i.atEnd() == false; i++)
975 // Get client and check that it is valid
976 RemoteClient *client = i.getNode()->getValue();
977 assert(client->peer_id == i.getNode()->getKey());
978 if(client->serialization_version == SER_FMT_VER_INVALID)
981 SendChatMessage(client->peer_id, line);
988 m_env.serializePlayers(m_mapsavedir);
999 JMutexAutoLock clientslock(m_con_mutex);
1001 for(core::map<u16, RemoteClient*>::Iterator
1002 i = m_clients.getIterator();
1003 i.atEnd() == false; i++)
1006 // NOTE: These are removed by env destructor
1008 u16 peer_id = i.getNode()->getKey();
1009 JMutexAutoLock envlock(m_env_mutex);
1010 m_env.removePlayer(peer_id);
1014 delete i.getNode()->getValue();
1019 void Server::start(unsigned short port)
1021 DSTACK(__FUNCTION_NAME);
1022 // Stop thread if already running
1025 // Initialize connection
1026 m_con.setTimeoutMs(30);
1030 m_thread.setRun(true);
1033 dout_server<<"Server: Started on port "<<port<<std::endl;
1038 DSTACK(__FUNCTION_NAME);
1040 // Stop threads (set run=false first so both start stopping)
1041 m_thread.setRun(false);
1042 m_emergethread.setRun(false);
1044 m_emergethread.stop();
1046 dout_server<<"Server: Threads stopped"<<std::endl;
1048 dout_server<<"Server: Saving players"<<std::endl;
1050 // FIXME: Apparently this does not do anything here
1051 //m_env.serializePlayers(m_mapsavedir);
1054 void Server::step(float dtime)
1056 DSTACK(__FUNCTION_NAME);
1061 JMutexAutoLock lock(m_step_dtime_mutex);
1062 m_step_dtime += dtime;
1066 void Server::AsyncRunStep()
1068 DSTACK(__FUNCTION_NAME);
1072 JMutexAutoLock lock1(m_step_dtime_mutex);
1073 dtime = m_step_dtime;
1076 // Send blocks to clients
1082 //dstream<<"Server steps "<<dtime<<std::endl;
1083 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1086 JMutexAutoLock lock1(m_step_dtime_mutex);
1087 m_step_dtime -= dtime;
1094 m_uptime.set(m_uptime.get() + dtime);
1098 Update m_time_of_day
1101 m_time_counter += dtime;
1102 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1103 u32 units = (u32)(m_time_counter*speed);
1104 m_time_counter -= (f32)units / speed;
1105 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1107 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1110 Send to clients at constant intervals
1113 m_time_of_day_send_timer -= dtime;
1114 if(m_time_of_day_send_timer < 0.0)
1116 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1118 //JMutexAutoLock envlock(m_env_mutex);
1119 JMutexAutoLock conlock(m_con_mutex);
1121 for(core::map<u16, RemoteClient*>::Iterator
1122 i = m_clients.getIterator();
1123 i.atEnd() == false; i++)
1125 RemoteClient *client = i.getNode()->getValue();
1126 //Player *player = m_env.getPlayer(client->peer_id);
1128 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1129 m_time_of_day.get());
1131 m_con.Send(client->peer_id, 0, data, true);
1137 // Process connection's timeouts
1138 JMutexAutoLock lock2(m_con_mutex);
1139 m_con.RunTimeouts(dtime);
1143 // This has to be called so that the client list gets synced
1144 // with the peer list of the connection
1145 handlePeerChanges();
1150 // This also runs Map's timers
1151 JMutexAutoLock lock(m_env_mutex);
1162 m_liquid_transform_timer += dtime;
1163 if(m_liquid_transform_timer >= 1.00)
1165 m_liquid_transform_timer -= 1.00;
1167 JMutexAutoLock lock(m_env_mutex);
1169 core::map<v3s16, MapBlock*> modified_blocks;
1170 m_env.getMap().transformLiquids(modified_blocks);
1175 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1176 ServerMap &map = ((ServerMap&)m_env.getMap());
1177 map.updateLighting(modified_blocks, lighting_modified_blocks);
1179 // Add blocks modified by lighting to modified_blocks
1180 for(core::map<v3s16, MapBlock*>::Iterator
1181 i = lighting_modified_blocks.getIterator();
1182 i.atEnd() == false; i++)
1184 MapBlock *block = i.getNode()->getValue();
1185 modified_blocks.insert(block->getPos(), block);
1189 Set the modified blocks unsent for all the clients
1192 JMutexAutoLock lock2(m_con_mutex);
1194 for(core::map<u16, RemoteClient*>::Iterator
1195 i = m_clients.getIterator();
1196 i.atEnd() == false; i++)
1198 RemoteClient *client = i.getNode()->getValue();
1200 if(modified_blocks.size() > 0)
1202 // Remove block from sent history
1203 client->SetBlocksNotSent(modified_blocks);
1208 // Periodically print some info
1210 float &counter = m_print_info_timer;
1216 JMutexAutoLock lock2(m_con_mutex);
1218 for(core::map<u16, RemoteClient*>::Iterator
1219 i = m_clients.getIterator();
1220 i.atEnd() == false; i++)
1222 //u16 peer_id = i.getNode()->getKey();
1223 RemoteClient *client = i.getNode()->getValue();
1224 client->PrintInfo(std::cout);
1229 if(g_settings.getBool("enable_experimental"))
1233 Check added and deleted active objects
1236 JMutexAutoLock envlock(m_env_mutex);
1237 JMutexAutoLock conlock(m_con_mutex);
1239 // Radius inside which objects are active
1242 for(core::map<u16, RemoteClient*>::Iterator
1243 i = m_clients.getIterator();
1244 i.atEnd() == false; i++)
1246 RemoteClient *client = i.getNode()->getValue();
1247 Player *player = m_env.getPlayer(client->peer_id);
1250 v3s16 pos = floatToInt(player->getPosition(), BS);
1252 core::map<u16, bool> removed_objects;
1253 core::map<u16, bool> added_objects;
1254 m_env.getRemovedActiveObjects(pos, radius,
1255 client->m_known_objects, removed_objects);
1256 m_env.getAddedActiveObjects(pos, radius,
1257 client->m_known_objects, added_objects);
1259 // Ignore if nothing happened
1260 if(removed_objects.size() == 0 && added_objects.size() == 0)
1263 std::string data_buffer;
1267 // Handle removed objects
1268 writeU16((u8*)buf, removed_objects.size());
1269 data_buffer.append(buf, 2);
1270 for(core::map<u16, bool>::Iterator
1271 i = removed_objects.getIterator();
1272 i.atEnd()==false; i++)
1275 u16 id = i.getNode()->getKey();
1276 ServerActiveObject* obj = m_env.getActiveObject(id);
1278 // Add to data buffer for sending
1279 writeU16((u8*)buf, i.getNode()->getKey());
1280 data_buffer.append(buf, 2);
1282 // Remove from known objects
1283 client->m_known_objects.remove(i.getNode()->getKey());
1285 if(obj && obj->m_known_by_count > 0)
1286 obj->m_known_by_count--;
1289 // Handle added objects
1290 writeU16((u8*)buf, added_objects.size());
1291 data_buffer.append(buf, 2);
1292 for(core::map<u16, bool>::Iterator
1293 i = added_objects.getIterator();
1294 i.atEnd()==false; i++)
1297 u16 id = i.getNode()->getKey();
1298 ServerActiveObject* obj = m_env.getActiveObject(id);
1301 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1303 dstream<<"WARNING: "<<__FUNCTION_NAME
1304 <<": NULL object"<<std::endl;
1306 type = obj->getType();
1308 // Add to data buffer for sending
1309 writeU16((u8*)buf, id);
1310 data_buffer.append(buf, 2);
1311 writeU8((u8*)buf, type);
1312 data_buffer.append(buf, 1);
1314 data_buffer.append(serializeLongString(
1315 obj->getClientInitializationData()));
1317 // Add to known objects
1318 client->m_known_objects.insert(i.getNode()->getKey(), false);
1321 obj->m_known_by_count++;
1325 SharedBuffer<u8> reply(2 + data_buffer.size());
1326 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1327 memcpy((char*)&reply[2], data_buffer.c_str(),
1328 data_buffer.size());
1330 m_con.Send(client->peer_id, 0, reply, true);
1332 dstream<<"INFO: Server: Sent object remove/add: "
1333 <<removed_objects.size()<<" removed, "
1334 <<added_objects.size()<<" added, "
1335 <<"packet size is "<<reply.getSize()<<std::endl;
1340 Send object messages
1343 JMutexAutoLock envlock(m_env_mutex);
1344 JMutexAutoLock conlock(m_con_mutex);
1347 // Value = data sent by object
1348 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1350 // Get active object messages from environment
1353 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1357 core::list<ActiveObjectMessage>* message_list = NULL;
1358 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1359 n = buffered_messages.find(aom.id);
1362 message_list = new core::list<ActiveObjectMessage>;
1363 buffered_messages.insert(aom.id, message_list);
1367 message_list = n->getValue();
1369 message_list->push_back(aom);
1372 // Route data to every client
1373 for(core::map<u16, RemoteClient*>::Iterator
1374 i = m_clients.getIterator();
1375 i.atEnd()==false; i++)
1377 RemoteClient *client = i.getNode()->getValue();
1378 std::string reliable_data;
1379 std::string unreliable_data;
1380 // Go through all objects in message buffer
1381 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1382 j = buffered_messages.getIterator();
1383 j.atEnd()==false; j++)
1385 // If object is not known by client, skip it
1386 u16 id = j.getNode()->getKey();
1387 if(client->m_known_objects.find(id) == NULL)
1389 // Get message list of object
1390 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1391 // Go through every message
1392 for(core::list<ActiveObjectMessage>::Iterator
1393 k = list->begin(); k != list->end(); k++)
1395 // Compose the full new data with header
1396 ActiveObjectMessage aom = *k;
1397 std::string new_data;
1400 writeU16((u8*)&buf[0], aom.id);
1401 new_data.append(buf, 2);
1403 new_data += serializeString(aom.datastring);
1404 // Add data to buffer
1406 reliable_data += new_data;
1408 unreliable_data += new_data;
1412 reliable_data and unreliable_data are now ready.
1415 if(reliable_data.size() > 0)
1417 SharedBuffer<u8> reply(2 + reliable_data.size());
1418 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1419 memcpy((char*)&reply[2], reliable_data.c_str(),
1420 reliable_data.size());
1422 m_con.Send(client->peer_id, 0, reply, true);
1424 if(unreliable_data.size() > 0)
1426 SharedBuffer<u8> reply(2 + unreliable_data.size());
1427 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1428 memcpy((char*)&reply[2], unreliable_data.c_str(),
1429 unreliable_data.size());
1430 // Send as unreliable
1431 m_con.Send(client->peer_id, 0, reply, false);
1434 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1436 dstream<<"INFO: Server: Size of object message data: "
1437 <<"reliable: "<<reliable_data.size()
1438 <<", unreliable: "<<unreliable_data.size()
1443 // Clear buffered_messages
1444 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1445 i = buffered_messages.getIterator();
1446 i.atEnd()==false; i++)
1448 delete i.getNode()->getValue();
1452 } // enable_experimental
1455 Send queued-for-sending map edit events.
1458 while(m_unsent_map_edit_queue.size() != 0)
1460 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1462 if(event->type == MEET_ADDNODE)
1464 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1465 sendAddNode(event->p, event->n, event->already_known_by_peer);
1467 else if(event->type == MEET_REMOVENODE)
1469 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1470 sendRemoveNode(event->p, event->already_known_by_peer);
1472 else if(event->type == MEET_OTHER)
1474 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1479 dstream<<"WARNING: Server: Unknown MapEditEvent "
1480 <<((u32)event->type)<<std::endl;
1488 Send object positions
1489 TODO: Get rid of MapBlockObjects
1492 float &counter = m_objectdata_timer;
1494 if(counter >= g_settings.getFloat("objectdata_interval"))
1496 JMutexAutoLock lock1(m_env_mutex);
1497 JMutexAutoLock lock2(m_con_mutex);
1498 SendObjectData(counter);
1505 Trigger emergethread (it somehow gets to a non-triggered but
1506 bysy state sometimes)
1509 float &counter = m_emergethread_trigger_timer;
1515 m_emergethread.trigger();
1521 float &counter = m_savemap_timer;
1523 if(counter >= g_settings.getFloat("server_map_save_interval"))
1527 JMutexAutoLock lock(m_env_mutex);
1529 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1531 // Save only changed parts
1532 m_env.getMap().save(true);
1534 // Delete unused sectors
1535 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1536 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1537 if(deleted_count > 0)
1539 dout_server<<"Server: Unloaded "<<deleted_count
1540 <<" sectors from memory"<<std::endl;
1544 m_env.serializePlayers(m_mapsavedir);
1550 void Server::Receive()
1552 DSTACK(__FUNCTION_NAME);
1553 u32 data_maxsize = 10000;
1554 Buffer<u8> data(data_maxsize);
1559 JMutexAutoLock conlock(m_con_mutex);
1560 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1563 // This has to be called so that the client list gets synced
1564 // with the peer list of the connection
1565 handlePeerChanges();
1567 ProcessData(*data, datasize, peer_id);
1569 catch(con::InvalidIncomingDataException &e)
1571 derr_server<<"Server::Receive(): "
1572 "InvalidIncomingDataException: what()="
1573 <<e.what()<<std::endl;
1575 catch(con::PeerNotFoundException &e)
1577 //NOTE: This is not needed anymore
1579 // The peer has been disconnected.
1580 // Find the associated player and remove it.
1582 /*JMutexAutoLock envlock(m_env_mutex);
1584 dout_server<<"ServerThread: peer_id="<<peer_id
1585 <<" has apparently closed connection. "
1586 <<"Removing player."<<std::endl;
1588 m_env.removePlayer(peer_id);*/
1592 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1594 DSTACK(__FUNCTION_NAME);
1595 // Environment is locked first.
1596 JMutexAutoLock envlock(m_env_mutex);
1597 JMutexAutoLock conlock(m_con_mutex);
1601 peer = m_con.GetPeer(peer_id);
1603 catch(con::PeerNotFoundException &e)
1605 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1606 <<peer_id<<" not found"<<std::endl;
1610 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1618 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1620 if(command == TOSERVER_INIT)
1622 // [0] u16 TOSERVER_INIT
1623 // [2] u8 SER_FMT_VER_HIGHEST
1624 // [3] u8[20] player_name
1629 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1630 <<peer->id<<std::endl;
1632 // First byte after command is maximum supported
1633 // serialization version
1634 u8 client_max = data[2];
1635 u8 our_max = SER_FMT_VER_HIGHEST;
1636 // Use the highest version supported by both
1637 u8 deployed = core::min_(client_max, our_max);
1638 // If it's lower than the lowest supported, give up.
1639 if(deployed < SER_FMT_VER_LOWEST)
1640 deployed = SER_FMT_VER_INVALID;
1642 //peer->serialization_version = deployed;
1643 getClient(peer->id)->pending_serialization_version = deployed;
1645 if(deployed == SER_FMT_VER_INVALID)
1647 derr_server<<DTIME<<"Server: Cannot negotiate "
1648 "serialization version with peer "
1649 <<peer_id<<std::endl;
1658 const u32 playername_size = 20;
1659 char playername[playername_size];
1660 for(u32 i=0; i<playername_size-1; i++)
1662 playername[i] = data[3+i];
1664 playername[playername_size-1] = 0;
1667 Player *player = emergePlayer(playername, "", peer_id);
1668 //Player *player = m_env.getPlayer(peer_id);
1671 // DEBUG: Test serialization
1672 std::ostringstream test_os;
1673 player->serialize(test_os);
1674 dstream<<"Player serialization test: \""<<test_os.str()
1676 std::istringstream test_is(test_os.str());
1677 player->deSerialize(test_is);
1680 // If failed, cancel
1683 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1684 <<": failed to emerge player"<<std::endl;
1689 // If a client is already connected to the player, cancel
1690 if(player->peer_id != 0)
1692 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1693 <<" tried to connect to "
1694 "an already connected player (peer_id="
1695 <<player->peer_id<<")"<<std::endl;
1698 // Set client of player
1699 player->peer_id = peer_id;
1702 // Check if player doesn't exist
1704 throw con::InvalidIncomingDataException
1705 ("Server::ProcessData(): INIT: Player doesn't exist");
1707 /*// update name if it was supplied
1708 if(datasize >= 20+3)
1711 player->updateName((const char*)&data[3]);
1714 // Now answer with a TOCLIENT_INIT
1716 SharedBuffer<u8> reply(2+1+6+8);
1717 writeU16(&reply[0], TOCLIENT_INIT);
1718 writeU8(&reply[2], deployed);
1719 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1720 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1723 m_con.Send(peer_id, 0, reply, true);
1727 if(command == TOSERVER_INIT2)
1729 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1730 <<peer->id<<std::endl;
1733 getClient(peer->id)->serialization_version
1734 = getClient(peer->id)->pending_serialization_version;
1737 Send some initialization data
1740 // Send player info to all players
1743 // Send inventory to player
1744 SendInventory(peer->id);
1748 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1749 m_time_of_day.get());
1750 m_con.Send(peer->id, 0, data, true);
1753 // Send information about server to player in chat
1754 SendChatMessage(peer_id, getStatusString());
1756 // Send information about joining in chat
1758 std::wstring name = L"unknown";
1759 Player *player = m_env.getPlayer(peer_id);
1761 name = narrow_to_wide(player->getName());
1763 std::wstring message;
1766 message += L" joined game";
1767 BroadcastChatMessage(message);
1773 if(peer_ser_ver == SER_FMT_VER_INVALID)
1775 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1776 " serialization format invalid or not initialized."
1777 " Skipping incoming command="<<command<<std::endl;
1781 Player *player = m_env.getPlayer(peer_id);
1784 derr_server<<"Server::ProcessData(): Cancelling: "
1785 "No player for peer_id="<<peer_id
1789 if(command == TOSERVER_PLAYERPOS)
1791 if(datasize < 2+12+12+4+4)
1795 v3s32 ps = readV3S32(&data[start+2]);
1796 v3s32 ss = readV3S32(&data[start+2+12]);
1797 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1798 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1799 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1800 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1801 pitch = wrapDegrees(pitch);
1802 yaw = wrapDegrees(yaw);
1803 player->setPosition(position);
1804 player->setSpeed(speed);
1805 player->setPitch(pitch);
1806 player->setYaw(yaw);
1808 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1809 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1810 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1812 else if(command == TOSERVER_GOTBLOCKS)
1825 u16 count = data[2];
1826 for(u16 i=0; i<count; i++)
1828 if((s16)datasize < 2+1+(i+1)*6)
1829 throw con::InvalidIncomingDataException
1830 ("GOTBLOCKS length is too short");
1831 v3s16 p = readV3S16(&data[2+1+i*6]);
1832 /*dstream<<"Server: GOTBLOCKS ("
1833 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1834 RemoteClient *client = getClient(peer_id);
1835 client->GotBlock(p);
1838 else if(command == TOSERVER_DELETEDBLOCKS)
1851 u16 count = data[2];
1852 for(u16 i=0; i<count; i++)
1854 if((s16)datasize < 2+1+(i+1)*6)
1855 throw con::InvalidIncomingDataException
1856 ("DELETEDBLOCKS length is too short");
1857 v3s16 p = readV3S16(&data[2+1+i*6]);
1858 /*dstream<<"Server: DELETEDBLOCKS ("
1859 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1860 RemoteClient *client = getClient(peer_id);
1861 client->SetBlockNotSent(p);
1864 else if(command == TOSERVER_CLICK_OBJECT)
1871 [2] u8 button (0=left, 1=right)
1876 u8 button = readU8(&data[2]);
1878 p.X = readS16(&data[3]);
1879 p.Y = readS16(&data[5]);
1880 p.Z = readS16(&data[7]);
1881 s16 id = readS16(&data[9]);
1882 //u16 item_i = readU16(&data[11]);
1884 MapBlock *block = NULL;
1887 block = m_env.getMap().getBlockNoCreate(p);
1889 catch(InvalidPositionException &e)
1891 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1895 MapBlockObject *obj = block->getObject(id);
1899 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1903 //TODO: Check that object is reasonably close
1908 InventoryList *ilist = player->inventory.getList("main");
1909 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1912 // Skip if inventory has no free space
1913 if(ilist->getUsedSlots() == ilist->getSize())
1915 dout_server<<"Player inventory has no free space"<<std::endl;
1920 Create the inventory item
1922 InventoryItem *item = NULL;
1923 // If it is an item-object, take the item from it
1924 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1926 item = ((ItemObject*)obj)->createInventoryItem();
1928 // Else create an item of the object
1931 item = new MapBlockObjectItem
1932 (obj->getInventoryString());
1935 // Add to inventory and send inventory
1936 ilist->addItem(item);
1937 SendInventory(player->peer_id);
1940 // Remove from block
1941 block->removeObject(id);
1944 else if(command == TOSERVER_GROUND_ACTION)
1952 [3] v3s16 nodepos_undersurface
1953 [9] v3s16 nodepos_abovesurface
1958 2: stop digging (all parameters ignored)
1959 3: digging completed
1961 u8 action = readU8(&data[2]);
1963 p_under.X = readS16(&data[3]);
1964 p_under.Y = readS16(&data[5]);
1965 p_under.Z = readS16(&data[7]);
1967 p_over.X = readS16(&data[9]);
1968 p_over.Y = readS16(&data[11]);
1969 p_over.Z = readS16(&data[13]);
1970 u16 item_i = readU16(&data[15]);
1972 //TODO: Check that target is reasonably close
1980 NOTE: This can be used in the future to check if
1981 somebody is cheating, by checking the timing.
1988 else if(action == 2)
1991 RemoteClient *client = getClient(peer->id);
1992 JMutexAutoLock digmutex(client->m_dig_mutex);
1993 client->m_dig_tool_item = -1;
1998 3: Digging completed
2000 else if(action == 3)
2002 // Mandatory parameter; actually used for nothing
2003 core::map<v3s16, MapBlock*> modified_blocks;
2006 u8 mineral = MINERAL_NONE;
2010 MapNode n = m_env.getMap().getNode(p_under);
2011 // Get material at position
2013 // If it's not diggable, do nothing
2014 if(content_diggable(material) == false)
2016 derr_server<<"Server: Not finishing digging: Node not diggable"
2019 // Client probably has wrong data.
2020 // Set block not sent, so that client will get
2022 dstream<<"Client "<<peer_id<<" tried to dig "
2023 <<"node from invalid position; setting"
2024 <<" MapBlock not sent."<<std::endl;
2025 RemoteClient *client = getClient(peer_id);
2026 v3s16 blockpos = getNodeBlockPos(p_under);
2027 client->SetBlockNotSent(blockpos);
2032 mineral = n.getMineral();
2034 catch(InvalidPositionException &e)
2036 derr_server<<"Server: Not finishing digging: Node not found."
2037 <<" Adding block to emerge queue."
2039 m_emerge_queue.addBlock(peer_id,
2040 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2045 Send the removal to all other clients
2047 sendRemoveNode(p_under, peer_id);
2050 Update and send inventory
2053 if(g_settings.getBool("creative_mode") == false)
2058 InventoryList *mlist = player->inventory.getList("main");
2061 InventoryItem *item = mlist->getItem(item_i);
2062 if(item && (std::string)item->getName() == "ToolItem")
2064 ToolItem *titem = (ToolItem*)item;
2065 std::string toolname = titem->getToolName();
2067 // Get digging properties for material and tool
2068 DiggingProperties prop =
2069 getDiggingProperties(material, toolname);
2071 if(prop.diggable == false)
2073 derr_server<<"Server: WARNING: Player digged"
2074 <<" with impossible material + tool"
2075 <<" combination"<<std::endl;
2078 bool weared_out = titem->addWear(prop.wear);
2082 mlist->deleteItem(item_i);
2088 Add dug item to inventory
2091 InventoryItem *item = NULL;
2093 if(mineral != MINERAL_NONE)
2094 item = getDiggedMineralItem(mineral);
2099 std::string &dug_s = content_features(material).dug_item;
2102 std::istringstream is(dug_s, std::ios::binary);
2103 item = InventoryItem::deSerialize(is);
2109 // Add a item to inventory
2110 player->inventory.addItem("main", item);
2113 SendInventory(player->peer_id);
2119 (this takes some time so it is done after the quick stuff)
2121 m_ignore_map_edit_events = true;
2122 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2123 m_ignore_map_edit_events = false;
2129 else if(action == 1)
2132 InventoryList *ilist = player->inventory.getList("main");
2137 InventoryItem *item = ilist->getItem(item_i);
2139 // If there is no item, it is not possible to add it anywhere
2144 Handle material items
2146 if(std::string("MaterialItem") == item->getName())
2149 // Don't add a node if this is not a free space
2150 MapNode n2 = m_env.getMap().getNode(p_over);
2151 if(content_buildable_to(n2.d) == false)
2153 // Client probably has wrong data.
2154 // Set block not sent, so that client will get
2156 dstream<<"Client "<<peer_id<<" tried to place"
2157 <<" node in invalid position; setting"
2158 <<" MapBlock not sent."<<std::endl;
2159 RemoteClient *client = getClient(peer_id);
2160 v3s16 blockpos = getNodeBlockPos(p_over);
2161 client->SetBlockNotSent(blockpos);
2165 catch(InvalidPositionException &e)
2167 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2168 <<" Adding block to emerge queue."
2170 m_emerge_queue.addBlock(peer_id,
2171 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2175 // Reset build time counter
2176 getClient(peer->id)->m_time_from_building = 0.0;
2179 MaterialItem *mitem = (MaterialItem*)item;
2181 n.d = mitem->getMaterial();
2182 if(content_features(n.d).wall_mounted)
2183 n.dir = packDir(p_under - p_over);
2188 sendAddNode(p_over, n, 0);
2193 InventoryList *ilist = player->inventory.getList("main");
2194 if(g_settings.getBool("creative_mode") == false && ilist)
2196 // Remove from inventory and send inventory
2197 if(mitem->getCount() == 1)
2198 ilist->deleteItem(item_i);
2202 SendInventory(peer_id);
2208 This takes some time so it is done after the quick stuff
2210 core::map<v3s16, MapBlock*> modified_blocks;
2211 m_ignore_map_edit_events = true;
2212 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2213 m_ignore_map_edit_events = false;
2216 Calculate special events
2219 /*if(n.d == CONTENT_MESE)
2222 for(s16 z=-1; z<=1; z++)
2223 for(s16 y=-1; y<=1; y++)
2224 for(s16 x=-1; x<=1; x++)
2235 v3s16 blockpos = getNodeBlockPos(p_over);
2237 MapBlock *block = NULL;
2240 block = m_env.getMap().getBlockNoCreate(blockpos);
2242 catch(InvalidPositionException &e)
2244 derr_server<<"Error while placing object: "
2245 "block not found"<<std::endl;
2249 v3s16 block_pos_i_on_map = block->getPosRelative();
2250 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2252 v3f pos = intToFloat(p_over, BS);
2253 pos -= block_pos_f_on_map;
2255 /*dout_server<<"pos="
2256 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2259 MapBlockObject *obj = NULL;
2262 Handle block object items
2264 if(std::string("MBOItem") == item->getName())
2266 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2268 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2269 "inventorystring=\""
2270 <<oitem->getInventoryString()
2271 <<"\""<<std::endl;*/
2273 obj = oitem->createObject
2274 (pos, player->getYaw(), player->getPitch());
2281 dout_server<<"Placing a miscellaneous item on map"
2284 Create an ItemObject that contains the item.
2286 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2287 std::ostringstream os(std::ios_base::binary);
2288 item->serialize(os);
2289 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2290 iobj->setItemString(os.str());
2296 derr_server<<"WARNING: item resulted in NULL object, "
2297 <<"not placing onto map"
2302 block->addObject(obj);
2304 dout_server<<"Placed object"<<std::endl;
2306 InventoryList *ilist = player->inventory.getList("main");
2307 if(g_settings.getBool("creative_mode") == false && ilist)
2309 // Remove from inventory and send inventory
2310 ilist->deleteItem(item_i);
2312 SendInventory(peer_id);
2320 Catch invalid actions
2324 derr_server<<"WARNING: Server: Invalid action "
2325 <<action<<std::endl;
2329 else if(command == TOSERVER_RELEASE)
2338 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2341 else if(command == TOSERVER_SIGNTEXT)
2350 std::string datastring((char*)&data[2], datasize-2);
2351 std::istringstream is(datastring, std::ios_base::binary);
2354 is.read((char*)buf, 6);
2355 v3s16 blockpos = readV3S16(buf);
2356 is.read((char*)buf, 2);
2357 s16 id = readS16(buf);
2358 is.read((char*)buf, 2);
2359 u16 textlen = readU16(buf);
2361 for(u16 i=0; i<textlen; i++)
2363 is.read((char*)buf, 1);
2364 text += (char)buf[0];
2367 MapBlock *block = NULL;
2370 block = m_env.getMap().getBlockNoCreate(blockpos);
2372 catch(InvalidPositionException &e)
2374 derr_server<<"Error while setting sign text: "
2375 "block not found"<<std::endl;
2379 MapBlockObject *obj = block->getObject(id);
2382 derr_server<<"Error while setting sign text: "
2383 "object not found"<<std::endl;
2387 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2389 derr_server<<"Error while setting sign text: "
2390 "object is not a sign"<<std::endl;
2394 ((SignObject*)obj)->setText(text);
2396 obj->getBlock()->setChangedFlag();
2398 else if(command == TOSERVER_INVENTORY_ACTION)
2400 /*// Ignore inventory changes if in creative mode
2401 if(g_settings.getBool("creative_mode") == true)
2403 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2407 // Strip command and create a stream
2408 std::string datastring((char*)&data[2], datasize-2);
2409 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2410 std::istringstream is(datastring, std::ios_base::binary);
2412 InventoryAction *a = InventoryAction::deSerialize(is);
2416 Handle craftresult specially if not in creative mode
2418 bool disable_action = false;
2419 if(a->getType() == IACTION_MOVE
2420 && g_settings.getBool("creative_mode") == false)
2422 IMoveAction *ma = (IMoveAction*)a;
2423 // Don't allow moving anything to craftresult
2424 if(ma->to_name == "craftresult")
2427 disable_action = true;
2429 // When something is removed from craftresult
2430 if(ma->from_name == "craftresult")
2432 disable_action = true;
2433 // Remove stuff from craft
2434 InventoryList *clist = player->inventory.getList("craft");
2437 u16 count = ma->count;
2440 clist->decrementMaterials(count);
2443 // Feed action to player inventory
2444 a->apply(&player->inventory);
2447 // If something appeared in craftresult, throw it
2449 InventoryList *rlist = player->inventory.getList("craftresult");
2450 InventoryList *mlist = player->inventory.getList("main");
2451 if(rlist && mlist && rlist->getUsedSlots() == 1)
2453 InventoryItem *item1 = rlist->changeItem(0, NULL);
2454 mlist->addItem(item1);
2458 if(disable_action == false)
2460 // Feed action to player inventory
2461 a->apply(&player->inventory);
2466 SendInventory(player->peer_id);
2470 dstream<<"TOSERVER_INVENTORY_ACTION: "
2471 <<"InventoryAction::deSerialize() returned NULL"
2475 else if(command == TOSERVER_CHAT_MESSAGE)
2483 std::string datastring((char*)&data[2], datasize-2);
2484 std::istringstream is(datastring, std::ios_base::binary);
2487 is.read((char*)buf, 2);
2488 u16 len = readU16(buf);
2490 std::wstring message;
2491 for(u16 i=0; i<len; i++)
2493 is.read((char*)buf, 2);
2494 message += (wchar_t)readU16(buf);
2497 // Get player name of this client
2498 std::wstring name = narrow_to_wide(player->getName());
2500 // Line to send to players
2502 // Whether to send to the player that sent the line
2503 bool send_to_sender = false;
2504 // Whether to send to other players
2505 bool send_to_others = false;
2508 std::wstring commandprefix = L"/#";
2509 if(message.substr(0, commandprefix.size()) == commandprefix)
2511 line += L"Server: ";
2513 message = message.substr(commandprefix.size());
2514 // Get player name as narrow string
2515 std::string name_s = player->getName();
2516 // Convert message to narrow string
2517 std::string message_s = wide_to_narrow(message);
2518 // Operator is the single name defined in config.
2519 std::string operator_name = g_settings.get("name");
2520 bool is_operator = (operator_name != "" &&
2521 wide_to_narrow(name) == operator_name);
2522 bool valid_command = false;
2523 if(message_s == "help")
2525 line += L"-!- Available commands: ";
2529 line += L"shutdown setting ";
2534 send_to_sender = true;
2535 valid_command = true;
2537 else if(message_s == "status")
2539 line = getStatusString();
2540 send_to_sender = true;
2541 valid_command = true;
2543 else if(is_operator)
2545 if(message_s == "shutdown")
2547 dstream<<DTIME<<" Server: Operator requested shutdown."
2549 m_shutdown_requested.set(true);
2551 line += L"*** Server shutting down (operator request)";
2552 send_to_sender = true;
2553 valid_command = true;
2555 else if(message_s.substr(0,8) == "setting ")
2557 std::string confline = message_s.substr(8);
2558 g_settings.parseConfigLine(confline);
2559 line += L"-!- Setting changed.";
2560 send_to_sender = true;
2561 valid_command = true;
2565 if(valid_command == false)
2567 line += L"-!- Invalid command: " + message;
2568 send_to_sender = true;
2579 send_to_others = true;
2584 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2587 Send the message to clients
2589 for(core::map<u16, RemoteClient*>::Iterator
2590 i = m_clients.getIterator();
2591 i.atEnd() == false; i++)
2593 // Get client and check that it is valid
2594 RemoteClient *client = i.getNode()->getValue();
2595 assert(client->peer_id == i.getNode()->getKey());
2596 if(client->serialization_version == SER_FMT_VER_INVALID)
2600 bool sender_selected = (peer_id == client->peer_id);
2601 if(sender_selected == true && send_to_sender == false)
2603 if(sender_selected == false && send_to_others == false)
2606 SendChatMessage(client->peer_id, line);
2612 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2613 "unknown command "<<command<<std::endl;
2617 catch(SendFailedException &e)
2619 derr_server<<"Server::ProcessData(): SendFailedException: "
2625 void Server::onMapEditEvent(MapEditEvent *event)
2627 dstream<<"Server::onMapEditEvent()"<<std::endl;
2628 if(m_ignore_map_edit_events)
2630 MapEditEvent *e = event->clone();
2631 m_unsent_map_edit_queue.push_back(e);
2634 core::list<PlayerInfo> Server::getPlayerInfo()
2636 DSTACK(__FUNCTION_NAME);
2637 JMutexAutoLock envlock(m_env_mutex);
2638 JMutexAutoLock conlock(m_con_mutex);
2640 core::list<PlayerInfo> list;
2642 core::list<Player*> players = m_env.getPlayers();
2644 core::list<Player*>::Iterator i;
2645 for(i = players.begin();
2646 i != players.end(); i++)
2650 Player *player = *i;
2653 con::Peer *peer = m_con.GetPeer(player->peer_id);
2654 // Copy info from peer to info struct
2656 info.address = peer->address;
2657 info.avg_rtt = peer->avg_rtt;
2659 catch(con::PeerNotFoundException &e)
2661 // Set dummy peer info
2663 info.address = Address(0,0,0,0,0);
2667 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2668 info.position = player->getPosition();
2670 list.push_back(info);
2677 void Server::peerAdded(con::Peer *peer)
2679 DSTACK(__FUNCTION_NAME);
2680 dout_server<<"Server::peerAdded(): peer->id="
2681 <<peer->id<<std::endl;
2684 c.type = PEER_ADDED;
2685 c.peer_id = peer->id;
2687 m_peer_change_queue.push_back(c);
2690 void Server::deletingPeer(con::Peer *peer, bool timeout)
2692 DSTACK(__FUNCTION_NAME);
2693 dout_server<<"Server::deletingPeer(): peer->id="
2694 <<peer->id<<", timeout="<<timeout<<std::endl;
2697 c.type = PEER_REMOVED;
2698 c.peer_id = peer->id;
2699 c.timeout = timeout;
2700 m_peer_change_queue.push_back(c);
2703 void Server::SendObjectData(float dtime)
2705 DSTACK(__FUNCTION_NAME);
2707 core::map<v3s16, bool> stepped_blocks;
2709 for(core::map<u16, RemoteClient*>::Iterator
2710 i = m_clients.getIterator();
2711 i.atEnd() == false; i++)
2713 u16 peer_id = i.getNode()->getKey();
2714 RemoteClient *client = i.getNode()->getValue();
2715 assert(client->peer_id == peer_id);
2717 if(client->serialization_version == SER_FMT_VER_INVALID)
2720 client->SendObjectData(this, dtime, stepped_blocks);
2724 void Server::SendPlayerInfos()
2726 DSTACK(__FUNCTION_NAME);
2728 //JMutexAutoLock envlock(m_env_mutex);
2730 // Get connected players
2731 core::list<Player*> players = m_env.getPlayers(true);
2733 u32 player_count = players.getSize();
2734 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2736 SharedBuffer<u8> data(datasize);
2737 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2740 core::list<Player*>::Iterator i;
2741 for(i = players.begin();
2742 i != players.end(); i++)
2744 Player *player = *i;
2746 /*dstream<<"Server sending player info for player with "
2747 "peer_id="<<player->peer_id<<std::endl;*/
2749 writeU16(&data[start], player->peer_id);
2750 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2751 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2752 start += 2+PLAYERNAME_SIZE;
2755 //JMutexAutoLock conlock(m_con_mutex);
2758 m_con.SendToAll(0, data, true);
2762 Craft checking system
2780 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2786 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2792 enum ItemSpecType type;
2793 // Only other one of these is used
2799 items: a pointer to an array of 9 pointers to items
2800 specs: a pointer to an array of 9 ItemSpecs
2802 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2804 u16 items_min_x = 100;
2805 u16 items_max_x = 100;
2806 u16 items_min_y = 100;
2807 u16 items_max_y = 100;
2808 for(u16 y=0; y<3; y++)
2809 for(u16 x=0; x<3; x++)
2811 if(items[y*3 + x] == NULL)
2813 if(items_min_x == 100 || x < items_min_x)
2815 if(items_min_y == 100 || y < items_min_y)
2817 if(items_max_x == 100 || x > items_max_x)
2819 if(items_max_y == 100 || y > items_max_y)
2822 // No items at all, just return false
2823 if(items_min_x == 100)
2826 u16 items_w = items_max_x - items_min_x + 1;
2827 u16 items_h = items_max_y - items_min_y + 1;
2829 u16 specs_min_x = 100;
2830 u16 specs_max_x = 100;
2831 u16 specs_min_y = 100;
2832 u16 specs_max_y = 100;
2833 for(u16 y=0; y<3; y++)
2834 for(u16 x=0; x<3; x++)
2836 if(specs[y*3 + x].type == ITEM_NONE)
2838 if(specs_min_x == 100 || x < specs_min_x)
2840 if(specs_min_y == 100 || y < specs_min_y)
2842 if(specs_max_x == 100 || x > specs_max_x)
2844 if(specs_max_y == 100 || y > specs_max_y)
2847 // No specs at all, just return false
2848 if(specs_min_x == 100)
2851 u16 specs_w = specs_max_x - specs_min_x + 1;
2852 u16 specs_h = specs_max_y - specs_min_y + 1;
2855 if(items_w != specs_w || items_h != specs_h)
2858 for(u16 y=0; y<specs_h; y++)
2859 for(u16 x=0; x<specs_w; x++)
2861 u16 items_x = items_min_x + x;
2862 u16 items_y = items_min_y + y;
2863 u16 specs_x = specs_min_x + x;
2864 u16 specs_y = specs_min_y + y;
2865 InventoryItem *item = items[items_y * 3 + items_x];
2866 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2868 if(spec.type == ITEM_NONE)
2870 // Has to be no item
2876 // There should be an item
2880 std::string itemname = item->getName();
2882 if(spec.type == ITEM_MATERIAL)
2884 if(itemname != "MaterialItem")
2886 MaterialItem *mitem = (MaterialItem*)item;
2887 if(mitem->getMaterial() != spec.num)
2890 else if(spec.type == ITEM_CRAFT)
2892 if(itemname != "CraftItem")
2894 CraftItem *mitem = (CraftItem*)item;
2895 if(mitem->getSubName() != spec.name)
2898 else if(spec.type == ITEM_TOOL)
2900 // Not supported yet
2903 else if(spec.type == ITEM_MBO)
2905 // Not supported yet
2910 // Not supported yet
2918 void Server::SendInventory(u16 peer_id)
2920 DSTACK(__FUNCTION_NAME);
2922 Player* player = m_env.getPlayer(peer_id);
2925 Calculate crafting stuff
2927 if(g_settings.getBool("creative_mode") == false)
2929 InventoryList *clist = player->inventory.getList("craft");
2930 InventoryList *rlist = player->inventory.getList("craftresult");
2933 rlist->clearItems();
2937 InventoryItem *items[9];
2938 for(u16 i=0; i<9; i++)
2940 items[i] = clist->getItem(i);
2949 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2950 if(checkItemCombination(items, specs))
2952 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2961 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2962 if(checkItemCombination(items, specs))
2964 rlist->addItem(new CraftItem("Stick", 4));
2973 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2974 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2975 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2976 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2977 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2978 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2979 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2980 if(checkItemCombination(items, specs))
2982 rlist->addItem(new MapBlockObjectItem("Sign"));
2991 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2992 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2993 if(checkItemCombination(items, specs))
2995 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3004 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3005 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3006 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3007 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3008 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3009 if(checkItemCombination(items, specs))
3011 rlist->addItem(new ToolItem("WPick", 0));
3020 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3021 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3022 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3023 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3024 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3025 if(checkItemCombination(items, specs))
3027 rlist->addItem(new ToolItem("STPick", 0));
3036 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3037 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3038 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3039 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3040 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3041 if(checkItemCombination(items, specs))
3043 rlist->addItem(new ToolItem("MesePick", 0));
3048 } // if creative_mode == false
3054 std::ostringstream os;
3055 //os.imbue(std::locale("C"));
3057 player->inventory.serialize(os);
3059 std::string s = os.str();
3061 SharedBuffer<u8> data(s.size()+2);
3062 writeU16(&data[0], TOCLIENT_INVENTORY);
3063 memcpy(&data[2], s.c_str(), s.size());
3066 m_con.Send(peer_id, 0, data, true);
3069 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3071 DSTACK(__FUNCTION_NAME);
3073 std::ostringstream os(std::ios_base::binary);
3077 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3078 os.write((char*)buf, 2);
3081 writeU16(buf, message.size());
3082 os.write((char*)buf, 2);
3085 for(u32 i=0; i<message.size(); i++)
3089 os.write((char*)buf, 2);
3093 std::string s = os.str();
3094 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3096 m_con.Send(peer_id, 0, data, true);
3099 void Server::BroadcastChatMessage(const std::wstring &message)
3101 for(core::map<u16, RemoteClient*>::Iterator
3102 i = m_clients.getIterator();
3103 i.atEnd() == false; i++)
3105 // Get client and check that it is valid
3106 RemoteClient *client = i.getNode()->getValue();
3107 assert(client->peer_id == i.getNode()->getKey());
3108 if(client->serialization_version == SER_FMT_VER_INVALID)
3111 SendChatMessage(client->peer_id, message);
3115 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3119 SharedBuffer<u8> reply(replysize);
3120 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3121 writeS16(&reply[2], p.X);
3122 writeS16(&reply[4], p.Y);
3123 writeS16(&reply[6], p.Z);
3125 for(core::map<u16, RemoteClient*>::Iterator
3126 i = m_clients.getIterator();
3127 i.atEnd() == false; i++)
3129 // Get client and check that it is valid
3130 RemoteClient *client = i.getNode()->getValue();
3131 assert(client->peer_id == i.getNode()->getKey());
3132 if(client->serialization_version == SER_FMT_VER_INVALID)
3135 // Don't send if it's the same one
3136 if(client->peer_id == ignore_id)
3140 m_con.Send(client->peer_id, 0, reply, true);
3144 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3146 for(core::map<u16, RemoteClient*>::Iterator
3147 i = m_clients.getIterator();
3148 i.atEnd() == false; i++)
3150 // Get client and check that it is valid
3151 RemoteClient *client = i.getNode()->getValue();
3152 assert(client->peer_id == i.getNode()->getKey());
3153 if(client->serialization_version == SER_FMT_VER_INVALID)
3156 // Don't send if it's the same one
3157 if(client->peer_id == ignore_id)
3161 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3162 SharedBuffer<u8> reply(replysize);
3163 writeU16(&reply[0], TOCLIENT_ADDNODE);
3164 writeS16(&reply[2], p.X);
3165 writeS16(&reply[4], p.Y);
3166 writeS16(&reply[6], p.Z);
3167 n.serialize(&reply[8], client->serialization_version);
3170 m_con.Send(client->peer_id, 0, reply, true);
3174 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3176 DSTACK(__FUNCTION_NAME);
3178 Create a packet with the block in the right format
3181 std::ostringstream os(std::ios_base::binary);
3182 block->serialize(os, ver);
3183 std::string s = os.str();
3184 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3186 u32 replysize = 8 + blockdata.getSize();
3187 SharedBuffer<u8> reply(replysize);
3188 v3s16 p = block->getPos();
3189 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3190 writeS16(&reply[2], p.X);
3191 writeS16(&reply[4], p.Y);
3192 writeS16(&reply[6], p.Z);
3193 memcpy(&reply[8], *blockdata, blockdata.getSize());
3195 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3196 <<": \tpacket size: "<<replysize<<std::endl;*/
3201 m_con.Send(peer_id, 1, reply, true);
3204 void Server::SendBlocks(float dtime)
3206 DSTACK(__FUNCTION_NAME);
3208 JMutexAutoLock envlock(m_env_mutex);
3209 JMutexAutoLock conlock(m_con_mutex);
3211 //TimeTaker timer("Server::SendBlocks");
3213 core::array<PrioritySortedBlockTransfer> queue;
3215 s32 total_sending = 0;
3217 for(core::map<u16, RemoteClient*>::Iterator
3218 i = m_clients.getIterator();
3219 i.atEnd() == false; i++)
3221 RemoteClient *client = i.getNode()->getValue();
3222 assert(client->peer_id == i.getNode()->getKey());
3224 total_sending += client->SendingCount();
3226 if(client->serialization_version == SER_FMT_VER_INVALID)
3229 client->GetNextBlocks(this, dtime, queue);
3233 // Lowest priority number comes first.
3234 // Lowest is most important.
3237 for(u32 i=0; i<queue.size(); i++)
3239 //TODO: Calculate limit dynamically
3240 if(total_sending >= g_settings.getS32
3241 ("max_simultaneous_block_sends_server_total"))
3244 PrioritySortedBlockTransfer q = queue[i];
3246 MapBlock *block = NULL;
3249 block = m_env.getMap().getBlockNoCreate(q.pos);
3251 catch(InvalidPositionException &e)
3256 RemoteClient *client = getClient(q.peer_id);
3258 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3260 client->SentBlock(q.pos);
3267 RemoteClient* Server::getClient(u16 peer_id)
3269 DSTACK(__FUNCTION_NAME);
3270 //JMutexAutoLock lock(m_con_mutex);
3271 core::map<u16, RemoteClient*>::Node *n;
3272 n = m_clients.find(peer_id);
3273 // A client should exist for all peers
3275 return n->getValue();
3278 std::wstring Server::getStatusString()
3280 std::wostringstream os(std::ios_base::binary);
3283 os<<L"uptime="<<m_uptime.get();
3284 // Information about clients
3286 for(core::map<u16, RemoteClient*>::Iterator
3287 i = m_clients.getIterator();
3288 i.atEnd() == false; i++)
3290 // Get client and check that it is valid
3291 RemoteClient *client = i.getNode()->getValue();
3292 assert(client->peer_id == i.getNode()->getKey());
3293 if(client->serialization_version == SER_FMT_VER_INVALID)
3296 Player *player = m_env.getPlayer(client->peer_id);
3297 // Get name of player
3298 std::wstring name = L"unknown";
3300 name = narrow_to_wide(player->getName());
3301 // Add name to information string
3305 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3306 os<<" WARNING: Map saving is disabled."<<std::endl;
3311 void setCreativeInventory(Player *player)
3313 player->resetInventory();
3315 // Give some good picks
3317 InventoryItem *item = new ToolItem("STPick", 0);
3318 void* r = player->inventory.addItem("main", item);
3322 InventoryItem *item = new ToolItem("MesePick", 0);
3323 void* r = player->inventory.addItem("main", item);
3331 // CONTENT_IGNORE-terminated list
3332 u8 material_items[] = {
3340 CONTENT_WATERSOURCE,
3347 u8 *mip = material_items;
3348 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3350 if(*mip == CONTENT_IGNORE)
3353 InventoryItem *item = new MaterialItem(*mip, 1);
3354 player->inventory.addItem("main", item);
3360 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3363 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3364 player->inventory.addItem("main", item);
3367 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3369 // Skip some materials
3370 if(i == CONTENT_WATER || i == CONTENT_TORCH
3371 || i == CONTENT_COALSTONE)
3374 InventoryItem *item = new MaterialItem(i, 1);
3375 player->inventory.addItem("main", item);
3381 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3382 void* r = player->inventory.addItem("main", item);
3387 Player *Server::emergePlayer(const char *name, const char *password,
3391 Try to get an existing player
3393 Player *player = m_env.getPlayer(name);
3396 // If player is already connected, cancel
3397 if(player->peer_id != 0)
3399 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3404 player->peer_id = peer_id;
3406 // Reset inventory to creative if in creative mode
3407 if(g_settings.getBool("creative_mode"))
3409 setCreativeInventory(player);
3416 If player with the wanted peer_id already exists, cancel.
3418 if(m_env.getPlayer(peer_id) != NULL)
3420 dstream<<"emergePlayer(): Player with wrong name but same"
3421 " peer_id already exists"<<std::endl;
3429 player = new ServerRemotePlayer();
3430 //player->peer_id = c.peer_id;
3431 //player->peer_id = PEER_ID_INEXISTENT;
3432 player->peer_id = peer_id;
3433 player->updateName(name);
3439 dstream<<"Server: Finding spawn place for player \""
3440 <<player->getName()<<"\""<<std::endl;
3444 player->setPosition(intToFloat(v3s16(
3451 s16 groundheight = 0;
3453 // Try to find a good place a few times
3454 for(s32 i=0; i<1000; i++)
3457 // We're going to try to throw the player to this position
3458 nodepos = v2s16(-range + (myrand()%(range*2)),
3459 -range + (myrand()%(range*2)));
3460 v2s16 sectorpos = getNodeSectorPos(nodepos);
3461 // Get sector (NOTE: Don't get because it's slow)
3462 //m_env.getMap().emergeSector(sectorpos);
3463 // Get ground height at point (fallbacks to heightmap function)
3464 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3465 // Don't go underwater
3466 if(groundheight < WATER_LEVEL)
3468 //dstream<<"-> Underwater"<<std::endl;
3471 // Don't go to high places
3472 if(groundheight > WATER_LEVEL + 4)
3474 //dstream<<"-> Underwater"<<std::endl;
3479 // Doesn't work, generating blocks is a bit too complicated for doing here
3480 // Get block at point
3482 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3483 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3484 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3485 // Don't go inside ground
3487 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3488 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3489 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3490 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3491 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3492 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3494 dstream<<"-> Inside ground"<<std::endl;
3498 }catch(InvalidPositionException &e)
3500 dstream<<"-> Invalid position"<<std::endl;
3501 // Ignore invalid position
3506 // Found a good place
3507 dstream<<"Searched through "<<i<<" places."<<std::endl;
3512 // If no suitable place was not found, go above water at least.
3513 if(groundheight < WATER_LEVEL)
3514 groundheight = WATER_LEVEL;
3516 player->setPosition(intToFloat(v3s16(
3518 groundheight + 5, // Accomodate mud
3524 Add player to environment
3527 m_env.addPlayer(player);
3530 Add stuff to inventory
3533 if(g_settings.getBool("creative_mode"))
3535 setCreativeInventory(player);
3540 InventoryItem *item = new ToolItem("WPick", 32000);
3541 void* r = player->inventory.addItem("main", item);
3545 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3546 void* r = player->inventory.addItem("main", item);
3550 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3551 void* r = player->inventory.addItem("main", item);
3555 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3556 void* r = player->inventory.addItem("main", item);
3560 InventoryItem *item = new CraftItem("Stick", 4);
3561 void* r = player->inventory.addItem("main", item);
3565 InventoryItem *item = new ToolItem("WPick", 32000);
3566 void* r = player->inventory.addItem("main", item);
3570 InventoryItem *item = new ToolItem("STPick", 32000);
3571 void* r = player->inventory.addItem("main", item);
3574 /*// Give some lights
3576 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3577 bool r = player->inventory.addItem("main", item);
3581 for(u16 i=0; i<4; i++)
3583 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3584 bool r = player->inventory.addItem("main", item);
3587 /*// Give some other stuff
3589 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3590 bool r = player->inventory.addItem("main", item);
3597 } // create new player
3601 void Server::UpdateBlockWaterPressure(MapBlock *block,
3602 core::map<v3s16, MapBlock*> &modified_blocks)
3604 MapVoxelManipulator v(&m_env.getMap());
3605 v.m_disable_water_climb =
3606 g_settings.getBool("disable_water_climb");
3608 VoxelArea area(block->getPosRelative(),
3609 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3613 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3615 catch(ProcessingLimitException &e)
3617 dstream<<"Processing limit reached (1)"<<std::endl;
3620 v.blitBack(modified_blocks);
3624 void Server::handlePeerChange(PeerChange &c)
3626 JMutexAutoLock envlock(m_env_mutex);
3627 JMutexAutoLock conlock(m_con_mutex);
3629 if(c.type == PEER_ADDED)
3636 core::map<u16, RemoteClient*>::Node *n;
3637 n = m_clients.find(c.peer_id);
3638 // The client shouldn't already exist
3642 RemoteClient *client = new RemoteClient();
3643 client->peer_id = c.peer_id;
3644 m_clients.insert(client->peer_id, client);
3647 else if(c.type == PEER_REMOVED)
3654 core::map<u16, RemoteClient*>::Node *n;
3655 n = m_clients.find(c.peer_id);
3656 // The client should exist
3659 // Collect information about leaving in chat
3660 std::wstring message;
3662 std::wstring name = L"unknown";
3663 Player *player = m_env.getPlayer(c.peer_id);
3665 name = narrow_to_wide(player->getName());
3669 message += L" left game";
3671 message += L" (timed out)";
3676 m_env.removePlayer(c.peer_id);
3679 // Set player client disconnected
3681 Player *player = m_env.getPlayer(c.peer_id);
3683 player->peer_id = 0;
3687 delete m_clients[c.peer_id];
3688 m_clients.remove(c.peer_id);
3690 // Send player info to all remaining clients
3693 // Send leave chat message to all remaining clients
3694 BroadcastChatMessage(message);
3703 void Server::handlePeerChanges()
3705 while(m_peer_change_queue.size() > 0)
3707 PeerChange c = m_peer_change_queue.pop_front();
3709 dout_server<<"Server: Handling peer change: "
3710 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3713 handlePeerChange(c);
3717 void dedicated_server_loop(Server &server, bool &kill)
3719 DSTACK(__FUNCTION_NAME);
3721 std::cout<<DTIME<<std::endl;
3722 std::cout<<"========================"<<std::endl;
3723 std::cout<<"Running dedicated server"<<std::endl;
3724 std::cout<<"========================"<<std::endl;
3725 std::cout<<std::endl;
3729 // This is kind of a hack but can be done like this
3730 // because server.step() is very light
3734 if(server.getShutdownRequested() || kill)
3736 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3740 static int counter = 0;
3746 core::list<PlayerInfo> list = server.getPlayerInfo();
3747 core::list<PlayerInfo>::Iterator i;
3748 static u32 sum_old = 0;
3749 u32 sum = PIChecksum(list);
3752 std::cout<<DTIME<<"Player info:"<<std::endl;
3753 for(i=list.begin(); i!=list.end(); i++)
3755 i->PrintLine(&std::cout);