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 Player *player = m_env.getPlayer(client->peer_id);
1225 std::cout<<player->getName()<<"\t";
1226 client->PrintInfo(std::cout);
1231 //if(g_settings.getBool("enable_experimental"))
1235 Check added and deleted active objects
1238 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1240 JMutexAutoLock envlock(m_env_mutex);
1241 JMutexAutoLock conlock(m_con_mutex);
1243 // Radius inside which objects are active
1246 for(core::map<u16, RemoteClient*>::Iterator
1247 i = m_clients.getIterator();
1248 i.atEnd() == false; i++)
1250 RemoteClient *client = i.getNode()->getValue();
1251 Player *player = m_env.getPlayer(client->peer_id);
1254 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1255 <<" has no associated player"<<std::endl;
1258 v3s16 pos = floatToInt(player->getPosition(), BS);
1260 core::map<u16, bool> removed_objects;
1261 core::map<u16, bool> added_objects;
1262 m_env.getRemovedActiveObjects(pos, radius,
1263 client->m_known_objects, removed_objects);
1264 m_env.getAddedActiveObjects(pos, radius,
1265 client->m_known_objects, added_objects);
1267 // Ignore if nothing happened
1268 if(removed_objects.size() == 0 && added_objects.size() == 0)
1270 //dstream<<"INFO: active objects: none changed"<<std::endl;
1274 std::string data_buffer;
1278 // Handle removed objects
1279 writeU16((u8*)buf, removed_objects.size());
1280 data_buffer.append(buf, 2);
1281 for(core::map<u16, bool>::Iterator
1282 i = removed_objects.getIterator();
1283 i.atEnd()==false; i++)
1286 u16 id = i.getNode()->getKey();
1287 ServerActiveObject* obj = m_env.getActiveObject(id);
1289 // Add to data buffer for sending
1290 writeU16((u8*)buf, i.getNode()->getKey());
1291 data_buffer.append(buf, 2);
1293 // Remove from known objects
1294 client->m_known_objects.remove(i.getNode()->getKey());
1296 if(obj && obj->m_known_by_count > 0)
1297 obj->m_known_by_count--;
1300 // Handle added objects
1301 writeU16((u8*)buf, added_objects.size());
1302 data_buffer.append(buf, 2);
1303 for(core::map<u16, bool>::Iterator
1304 i = added_objects.getIterator();
1305 i.atEnd()==false; i++)
1308 u16 id = i.getNode()->getKey();
1309 ServerActiveObject* obj = m_env.getActiveObject(id);
1312 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1314 dstream<<"WARNING: "<<__FUNCTION_NAME
1315 <<": NULL object"<<std::endl;
1317 type = obj->getType();
1319 // Add to data buffer for sending
1320 writeU16((u8*)buf, id);
1321 data_buffer.append(buf, 2);
1322 writeU8((u8*)buf, type);
1323 data_buffer.append(buf, 1);
1325 data_buffer.append(serializeLongString(
1326 obj->getClientInitializationData()));
1328 // Add to known objects
1329 client->m_known_objects.insert(i.getNode()->getKey(), false);
1332 obj->m_known_by_count++;
1336 SharedBuffer<u8> reply(2 + data_buffer.size());
1337 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1338 memcpy((char*)&reply[2], data_buffer.c_str(),
1339 data_buffer.size());
1341 m_con.Send(client->peer_id, 0, reply, true);
1343 dstream<<"INFO: Server: Sent object remove/add: "
1344 <<removed_objects.size()<<" removed, "
1345 <<added_objects.size()<<" added, "
1346 <<"packet size is "<<reply.getSize()<<std::endl;
1351 Collect a list of all the objects known by the clients
1352 and report it back to the environment.
1355 core::map<u16, bool> all_known_objects;
1357 for(core::map<u16, RemoteClient*>::Iterator
1358 i = m_clients.getIterator();
1359 i.atEnd() == false; i++)
1361 RemoteClient *client = i.getNode()->getValue();
1362 // Go through all known objects of client
1363 for(core::map<u16, bool>::Iterator
1364 i = client->m_known_objects.getIterator();
1365 i.atEnd()==false; i++)
1367 u16 id = i.getNode()->getKey();
1368 all_known_objects[id] = true;
1372 m_env.setKnownActiveObjects(whatever);
1378 Send object messages
1381 JMutexAutoLock envlock(m_env_mutex);
1382 JMutexAutoLock conlock(m_con_mutex);
1385 // Value = data sent by object
1386 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1388 // Get active object messages from environment
1391 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1395 core::list<ActiveObjectMessage>* message_list = NULL;
1396 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1397 n = buffered_messages.find(aom.id);
1400 message_list = new core::list<ActiveObjectMessage>;
1401 buffered_messages.insert(aom.id, message_list);
1405 message_list = n->getValue();
1407 message_list->push_back(aom);
1410 // Route data to every client
1411 for(core::map<u16, RemoteClient*>::Iterator
1412 i = m_clients.getIterator();
1413 i.atEnd()==false; i++)
1415 RemoteClient *client = i.getNode()->getValue();
1416 std::string reliable_data;
1417 std::string unreliable_data;
1418 // Go through all objects in message buffer
1419 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1420 j = buffered_messages.getIterator();
1421 j.atEnd()==false; j++)
1423 // If object is not known by client, skip it
1424 u16 id = j.getNode()->getKey();
1425 if(client->m_known_objects.find(id) == NULL)
1427 // Get message list of object
1428 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1429 // Go through every message
1430 for(core::list<ActiveObjectMessage>::Iterator
1431 k = list->begin(); k != list->end(); k++)
1433 // Compose the full new data with header
1434 ActiveObjectMessage aom = *k;
1435 std::string new_data;
1438 writeU16((u8*)&buf[0], aom.id);
1439 new_data.append(buf, 2);
1441 new_data += serializeString(aom.datastring);
1442 // Add data to buffer
1444 reliable_data += new_data;
1446 unreliable_data += new_data;
1450 reliable_data and unreliable_data are now ready.
1453 if(reliable_data.size() > 0)
1455 SharedBuffer<u8> reply(2 + reliable_data.size());
1456 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1457 memcpy((char*)&reply[2], reliable_data.c_str(),
1458 reliable_data.size());
1460 m_con.Send(client->peer_id, 0, reply, true);
1462 if(unreliable_data.size() > 0)
1464 SharedBuffer<u8> reply(2 + unreliable_data.size());
1465 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1466 memcpy((char*)&reply[2], unreliable_data.c_str(),
1467 unreliable_data.size());
1468 // Send as unreliable
1469 m_con.Send(client->peer_id, 0, reply, false);
1472 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1474 dstream<<"INFO: Server: Size of object message data: "
1475 <<"reliable: "<<reliable_data.size()
1476 <<", unreliable: "<<unreliable_data.size()
1481 // Clear buffered_messages
1482 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1483 i = buffered_messages.getIterator();
1484 i.atEnd()==false; i++)
1486 delete i.getNode()->getValue();
1490 } // enable_experimental
1493 Send queued-for-sending map edit events.
1496 while(m_unsent_map_edit_queue.size() != 0)
1498 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1500 if(event->type == MEET_ADDNODE)
1502 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1503 sendAddNode(event->p, event->n, event->already_known_by_peer);
1505 else if(event->type == MEET_REMOVENODE)
1507 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1508 sendRemoveNode(event->p, event->already_known_by_peer);
1510 else if(event->type == MEET_OTHER)
1512 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1517 dstream<<"WARNING: Server: Unknown MapEditEvent "
1518 <<((u32)event->type)<<std::endl;
1526 Send object positions
1527 TODO: Get rid of MapBlockObjects
1530 float &counter = m_objectdata_timer;
1532 if(counter >= g_settings.getFloat("objectdata_interval"))
1534 JMutexAutoLock lock1(m_env_mutex);
1535 JMutexAutoLock lock2(m_con_mutex);
1536 SendObjectData(counter);
1546 JMutexAutoLock envlock(m_env_mutex);
1547 JMutexAutoLock conlock(m_con_mutex);
1549 core::map<v3s16, MapBlock*> changed_blocks;
1550 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1552 for(core::map<v3s16, MapBlock*>::Iterator
1553 i = changed_blocks.getIterator();
1554 i.atEnd() == false; i++)
1556 MapBlock *block = i.getNode()->getValue();
1558 for(core::map<u16, RemoteClient*>::Iterator
1559 i = m_clients.getIterator();
1560 i.atEnd()==false; i++)
1562 RemoteClient *client = i.getNode()->getValue();
1563 client->SetBlockNotSent(block->getPos());
1569 Trigger emergethread (it somehow gets to a non-triggered but
1570 bysy state sometimes)
1573 float &counter = m_emergethread_trigger_timer;
1579 m_emergethread.trigger();
1585 float &counter = m_savemap_timer;
1587 if(counter >= g_settings.getFloat("server_map_save_interval"))
1591 JMutexAutoLock lock(m_env_mutex);
1593 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1595 // Save only changed parts
1596 m_env.getMap().save(true);
1598 // Delete unused sectors
1599 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1600 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1601 if(deleted_count > 0)
1603 dout_server<<"Server: Unloaded "<<deleted_count
1604 <<" sectors from memory"<<std::endl;
1608 m_env.serializePlayers(m_mapsavedir);
1614 void Server::Receive()
1616 DSTACK(__FUNCTION_NAME);
1617 u32 data_maxsize = 10000;
1618 Buffer<u8> data(data_maxsize);
1623 JMutexAutoLock conlock(m_con_mutex);
1624 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1627 // This has to be called so that the client list gets synced
1628 // with the peer list of the connection
1629 handlePeerChanges();
1631 ProcessData(*data, datasize, peer_id);
1633 catch(con::InvalidIncomingDataException &e)
1635 derr_server<<"Server::Receive(): "
1636 "InvalidIncomingDataException: what()="
1637 <<e.what()<<std::endl;
1639 catch(con::PeerNotFoundException &e)
1641 //NOTE: This is not needed anymore
1643 // The peer has been disconnected.
1644 // Find the associated player and remove it.
1646 /*JMutexAutoLock envlock(m_env_mutex);
1648 dout_server<<"ServerThread: peer_id="<<peer_id
1649 <<" has apparently closed connection. "
1650 <<"Removing player."<<std::endl;
1652 m_env.removePlayer(peer_id);*/
1656 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1658 DSTACK(__FUNCTION_NAME);
1659 // Environment is locked first.
1660 JMutexAutoLock envlock(m_env_mutex);
1661 JMutexAutoLock conlock(m_con_mutex);
1665 peer = m_con.GetPeer(peer_id);
1667 catch(con::PeerNotFoundException &e)
1669 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1670 <<peer_id<<" not found"<<std::endl;
1674 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1682 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1684 if(command == TOSERVER_INIT)
1686 // [0] u16 TOSERVER_INIT
1687 // [2] u8 SER_FMT_VER_HIGHEST
1688 // [3] u8[20] player_name
1693 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1694 <<peer->id<<std::endl;
1696 // First byte after command is maximum supported
1697 // serialization version
1698 u8 client_max = data[2];
1699 u8 our_max = SER_FMT_VER_HIGHEST;
1700 // Use the highest version supported by both
1701 u8 deployed = core::min_(client_max, our_max);
1702 // If it's lower than the lowest supported, give up.
1703 if(deployed < SER_FMT_VER_LOWEST)
1704 deployed = SER_FMT_VER_INVALID;
1706 //peer->serialization_version = deployed;
1707 getClient(peer->id)->pending_serialization_version = deployed;
1709 if(deployed == SER_FMT_VER_INVALID)
1711 derr_server<<DTIME<<"Server: Cannot negotiate "
1712 "serialization version with peer "
1713 <<peer_id<<std::endl;
1722 const u32 playername_size = 20;
1723 char playername[playername_size];
1724 for(u32 i=0; i<playername_size-1; i++)
1726 playername[i] = data[3+i];
1728 playername[playername_size-1] = 0;
1731 Player *player = emergePlayer(playername, "", peer_id);
1732 //Player *player = m_env.getPlayer(peer_id);
1735 // DEBUG: Test serialization
1736 std::ostringstream test_os;
1737 player->serialize(test_os);
1738 dstream<<"Player serialization test: \""<<test_os.str()
1740 std::istringstream test_is(test_os.str());
1741 player->deSerialize(test_is);
1744 // If failed, cancel
1747 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1748 <<": failed to emerge player"<<std::endl;
1753 // If a client is already connected to the player, cancel
1754 if(player->peer_id != 0)
1756 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1757 <<" tried to connect to "
1758 "an already connected player (peer_id="
1759 <<player->peer_id<<")"<<std::endl;
1762 // Set client of player
1763 player->peer_id = peer_id;
1766 // Check if player doesn't exist
1768 throw con::InvalidIncomingDataException
1769 ("Server::ProcessData(): INIT: Player doesn't exist");
1771 /*// update name if it was supplied
1772 if(datasize >= 20+3)
1775 player->updateName((const char*)&data[3]);
1778 // Now answer with a TOCLIENT_INIT
1780 SharedBuffer<u8> reply(2+1+6+8);
1781 writeU16(&reply[0], TOCLIENT_INIT);
1782 writeU8(&reply[2], deployed);
1783 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1784 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1787 m_con.Send(peer_id, 0, reply, true);
1791 if(command == TOSERVER_INIT2)
1793 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1794 <<peer->id<<std::endl;
1797 getClient(peer->id)->serialization_version
1798 = getClient(peer->id)->pending_serialization_version;
1801 Send some initialization data
1804 // Send player info to all players
1807 // Send inventory to player
1808 SendInventory(peer->id);
1812 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1813 m_time_of_day.get());
1814 m_con.Send(peer->id, 0, data, true);
1817 // Send information about server to player in chat
1818 SendChatMessage(peer_id, getStatusString());
1820 // Send information about joining in chat
1822 std::wstring name = L"unknown";
1823 Player *player = m_env.getPlayer(peer_id);
1825 name = narrow_to_wide(player->getName());
1827 std::wstring message;
1830 message += L" joined game";
1831 BroadcastChatMessage(message);
1837 if(peer_ser_ver == SER_FMT_VER_INVALID)
1839 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1840 " serialization format invalid or not initialized."
1841 " Skipping incoming command="<<command<<std::endl;
1845 Player *player = m_env.getPlayer(peer_id);
1848 derr_server<<"Server::ProcessData(): Cancelling: "
1849 "No player for peer_id="<<peer_id
1853 if(command == TOSERVER_PLAYERPOS)
1855 if(datasize < 2+12+12+4+4)
1859 v3s32 ps = readV3S32(&data[start+2]);
1860 v3s32 ss = readV3S32(&data[start+2+12]);
1861 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1862 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1863 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1864 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1865 pitch = wrapDegrees(pitch);
1866 yaw = wrapDegrees(yaw);
1867 player->setPosition(position);
1868 player->setSpeed(speed);
1869 player->setPitch(pitch);
1870 player->setYaw(yaw);
1872 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1873 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1874 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1876 else if(command == TOSERVER_GOTBLOCKS)
1889 u16 count = data[2];
1890 for(u16 i=0; i<count; i++)
1892 if((s16)datasize < 2+1+(i+1)*6)
1893 throw con::InvalidIncomingDataException
1894 ("GOTBLOCKS length is too short");
1895 v3s16 p = readV3S16(&data[2+1+i*6]);
1896 /*dstream<<"Server: GOTBLOCKS ("
1897 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1898 RemoteClient *client = getClient(peer_id);
1899 client->GotBlock(p);
1902 else if(command == TOSERVER_DELETEDBLOCKS)
1915 u16 count = data[2];
1916 for(u16 i=0; i<count; i++)
1918 if((s16)datasize < 2+1+(i+1)*6)
1919 throw con::InvalidIncomingDataException
1920 ("DELETEDBLOCKS length is too short");
1921 v3s16 p = readV3S16(&data[2+1+i*6]);
1922 /*dstream<<"Server: DELETEDBLOCKS ("
1923 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1924 RemoteClient *client = getClient(peer_id);
1925 client->SetBlockNotSent(p);
1928 else if(command == TOSERVER_CLICK_OBJECT)
1935 [2] u8 button (0=left, 1=right)
1940 u8 button = readU8(&data[2]);
1942 p.X = readS16(&data[3]);
1943 p.Y = readS16(&data[5]);
1944 p.Z = readS16(&data[7]);
1945 s16 id = readS16(&data[9]);
1946 //u16 item_i = readU16(&data[11]);
1948 MapBlock *block = NULL;
1951 block = m_env.getMap().getBlockNoCreate(p);
1953 catch(InvalidPositionException &e)
1955 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1959 MapBlockObject *obj = block->getObject(id);
1963 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1967 //TODO: Check that object is reasonably close
1972 InventoryList *ilist = player->inventory.getList("main");
1973 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1976 // Skip if inventory has no free space
1977 if(ilist->getUsedSlots() == ilist->getSize())
1979 dout_server<<"Player inventory has no free space"<<std::endl;
1984 Create the inventory item
1986 InventoryItem *item = NULL;
1987 // If it is an item-object, take the item from it
1988 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1990 item = ((ItemObject*)obj)->createInventoryItem();
1992 // Else create an item of the object
1995 item = new MapBlockObjectItem
1996 (obj->getInventoryString());
1999 // Add to inventory and send inventory
2000 ilist->addItem(item);
2001 SendInventory(player->peer_id);
2004 // Remove from block
2005 block->removeObject(id);
2008 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2016 [2] u8 button (0=left, 1=right)
2020 u8 button = readU8(&data[2]);
2021 u16 id = readS16(&data[3]);
2022 //u16 item_i = readU16(&data[11]);
2024 ServerActiveObject *obj = m_env.getActiveObject(id);
2028 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2033 //TODO: Check that object is reasonably close
2035 // Left click, pick object up (usually)
2038 InventoryList *ilist = player->inventory.getList("main");
2039 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2042 // Skip if inventory has no free space
2043 if(ilist->getUsedSlots() == ilist->getSize())
2045 dout_server<<"Player inventory has no free space"<<std::endl;
2049 // Skip if object has been removed
2054 Create the inventory item
2056 InventoryItem *item = obj->createPickedUpItem();
2060 // Add to inventory and send inventory
2061 ilist->addItem(item);
2062 SendInventory(player->peer_id);
2064 // Remove object from environment
2065 obj->m_removed = true;
2070 else if(command == TOSERVER_GROUND_ACTION)
2078 [3] v3s16 nodepos_undersurface
2079 [9] v3s16 nodepos_abovesurface
2084 2: stop digging (all parameters ignored)
2085 3: digging completed
2087 u8 action = readU8(&data[2]);
2089 p_under.X = readS16(&data[3]);
2090 p_under.Y = readS16(&data[5]);
2091 p_under.Z = readS16(&data[7]);
2093 p_over.X = readS16(&data[9]);
2094 p_over.Y = readS16(&data[11]);
2095 p_over.Z = readS16(&data[13]);
2096 u16 item_i = readU16(&data[15]);
2098 //TODO: Check that target is reasonably close
2106 NOTE: This can be used in the future to check if
2107 somebody is cheating, by checking the timing.
2114 else if(action == 2)
2117 RemoteClient *client = getClient(peer->id);
2118 JMutexAutoLock digmutex(client->m_dig_mutex);
2119 client->m_dig_tool_item = -1;
2124 3: Digging completed
2126 else if(action == 3)
2128 // Mandatory parameter; actually used for nothing
2129 core::map<v3s16, MapBlock*> modified_blocks;
2132 u8 mineral = MINERAL_NONE;
2134 bool cannot_remove_node = false;
2138 MapNode n = m_env.getMap().getNode(p_under);
2140 mineral = n.getMineral();
2141 // Get material at position
2143 // If not yet cancelled
2144 if(cannot_remove_node == false)
2146 // If it's not diggable, do nothing
2147 if(content_diggable(material) == false)
2149 derr_server<<"Server: Not finishing digging: "
2150 <<"Node not diggable"
2152 cannot_remove_node = true;
2155 // If not yet cancelled
2156 if(cannot_remove_node == false)
2158 // Get node metadata
2159 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2160 if(meta && meta->nodeRemovalDisabled() == true)
2162 derr_server<<"Server: Not finishing digging: "
2163 <<"Node metadata disables removal"
2165 cannot_remove_node = true;
2169 catch(InvalidPositionException &e)
2171 derr_server<<"Server: Not finishing digging: Node not found."
2172 <<" Adding block to emerge queue."
2174 m_emerge_queue.addBlock(peer_id,
2175 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2176 cannot_remove_node = true;
2180 If node can't be removed, set block to be re-sent to
2183 if(cannot_remove_node)
2185 derr_server<<"Server: Not finishing digging."<<std::endl;
2187 // Client probably has wrong data.
2188 // Set block not sent, so that client will get
2190 dstream<<"Client "<<peer_id<<" tried to dig "
2191 <<"node; but node cannot be removed."
2192 <<" setting MapBlock not sent."<<std::endl;
2193 RemoteClient *client = getClient(peer_id);
2194 v3s16 blockpos = getNodeBlockPos(p_under);
2195 client->SetBlockNotSent(blockpos);
2201 Send the removal to all other clients.
2202 - If other player is close, send REMOVENODE
2203 - Otherwise set blocks not sent
2205 core::list<u16> far_players;
2206 sendRemoveNode(p_under, peer_id, &far_players, 100);
2209 Update and send inventory
2212 if(g_settings.getBool("creative_mode") == false)
2217 InventoryList *mlist = player->inventory.getList("main");
2220 InventoryItem *item = mlist->getItem(item_i);
2221 if(item && (std::string)item->getName() == "ToolItem")
2223 ToolItem *titem = (ToolItem*)item;
2224 std::string toolname = titem->getToolName();
2226 // Get digging properties for material and tool
2227 DiggingProperties prop =
2228 getDiggingProperties(material, toolname);
2230 if(prop.diggable == false)
2232 derr_server<<"Server: WARNING: Player digged"
2233 <<" with impossible material + tool"
2234 <<" combination"<<std::endl;
2237 bool weared_out = titem->addWear(prop.wear);
2241 mlist->deleteItem(item_i);
2247 Add dug item to inventory
2250 InventoryItem *item = NULL;
2252 if(mineral != MINERAL_NONE)
2253 item = getDiggedMineralItem(mineral);
2258 std::string &dug_s = content_features(material).dug_item;
2261 std::istringstream is(dug_s, std::ios::binary);
2262 item = InventoryItem::deSerialize(is);
2268 // Add a item to inventory
2269 player->inventory.addItem("main", item);
2272 SendInventory(player->peer_id);
2278 (this takes some time so it is done after the quick stuff)
2280 m_ignore_map_edit_events = true;
2281 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2282 m_ignore_map_edit_events = false;
2285 Set blocks not sent to far players
2287 for(core::list<u16>::Iterator
2288 i = far_players.begin();
2289 i != far_players.end(); i++)
2292 RemoteClient *client = getClient(peer_id);
2295 client->SetBlocksNotSent(modified_blocks);
2302 else if(action == 1)
2305 InventoryList *ilist = player->inventory.getList("main");
2310 InventoryItem *item = ilist->getItem(item_i);
2312 // If there is no item, it is not possible to add it anywhere
2317 Handle material items
2319 if(std::string("MaterialItem") == item->getName())
2322 // Don't add a node if this is not a free space
2323 MapNode n2 = m_env.getMap().getNode(p_over);
2324 if(content_buildable_to(n2.d) == false)
2326 // Client probably has wrong data.
2327 // Set block not sent, so that client will get
2329 dstream<<"Client "<<peer_id<<" tried to place"
2330 <<" node in invalid position; setting"
2331 <<" MapBlock not sent."<<std::endl;
2332 RemoteClient *client = getClient(peer_id);
2333 v3s16 blockpos = getNodeBlockPos(p_over);
2334 client->SetBlockNotSent(blockpos);
2338 catch(InvalidPositionException &e)
2340 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2341 <<" Adding block to emerge queue."
2343 m_emerge_queue.addBlock(peer_id,
2344 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2348 // Reset build time counter
2349 getClient(peer->id)->m_time_from_building = 0.0;
2352 MaterialItem *mitem = (MaterialItem*)item;
2354 n.d = mitem->getMaterial();
2355 if(content_features(n.d).wall_mounted)
2356 n.dir = packDir(p_under - p_over);
2361 core::list<u16> far_players;
2362 sendAddNode(p_over, n, 0, &far_players, 100);
2367 InventoryList *ilist = player->inventory.getList("main");
2368 if(g_settings.getBool("creative_mode") == false && ilist)
2370 // Remove from inventory and send inventory
2371 if(mitem->getCount() == 1)
2372 ilist->deleteItem(item_i);
2376 SendInventory(peer_id);
2382 This takes some time so it is done after the quick stuff
2384 core::map<v3s16, MapBlock*> modified_blocks;
2385 m_ignore_map_edit_events = true;
2386 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2387 m_ignore_map_edit_events = false;
2390 Set blocks not sent to far players
2392 for(core::list<u16>::Iterator
2393 i = far_players.begin();
2394 i != far_players.end(); i++)
2397 RemoteClient *client = getClient(peer_id);
2400 client->SetBlocksNotSent(modified_blocks);
2404 Calculate special events
2407 /*if(n.d == CONTENT_MESE)
2410 for(s16 z=-1; z<=1; z++)
2411 for(s16 y=-1; y<=1; y++)
2412 for(s16 x=-1; x<=1; x++)
2419 Place other item (not a block)
2423 v3s16 blockpos = getNodeBlockPos(p_over);
2426 Check that the block is loaded so that the item
2427 can properly be added to the static list too
2429 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2432 derr_server<<"Error while placing object: "
2433 "block not found"<<std::endl;
2437 dout_server<<"Placing a miscellaneous item on map"
2440 // Calculate a position for it
2441 v3f pos = intToFloat(p_over, BS);
2443 pos.Y -= BS*0.25; // let it drop a bit
2445 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2446 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2451 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2455 derr_server<<"WARNING: item resulted in NULL object, "
2456 <<"not placing onto map"
2461 // Add the object to the environment
2462 m_env.addActiveObject(obj);
2464 dout_server<<"Placed object"<<std::endl;
2466 // If item has count<=1, delete it
2467 if(item->getCount() <= 1)
2469 InventoryList *ilist = player->inventory.getList("main");
2470 if(g_settings.getBool("creative_mode") == false && ilist)
2472 // Remove from inventory and send inventory
2473 ilist->deleteItem(item_i);
2475 SendInventory(peer_id);
2478 // Else decrement it
2483 SendInventory(peer_id);
2491 Catch invalid actions
2495 derr_server<<"WARNING: Server: Invalid action "
2496 <<action<<std::endl;
2500 else if(command == TOSERVER_RELEASE)
2509 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2512 else if(command == TOSERVER_SIGNTEXT)
2521 std::string datastring((char*)&data[2], datasize-2);
2522 std::istringstream is(datastring, std::ios_base::binary);
2525 is.read((char*)buf, 6);
2526 v3s16 blockpos = readV3S16(buf);
2527 is.read((char*)buf, 2);
2528 s16 id = readS16(buf);
2529 is.read((char*)buf, 2);
2530 u16 textlen = readU16(buf);
2532 for(u16 i=0; i<textlen; i++)
2534 is.read((char*)buf, 1);
2535 text += (char)buf[0];
2538 MapBlock *block = NULL;
2541 block = m_env.getMap().getBlockNoCreate(blockpos);
2543 catch(InvalidPositionException &e)
2545 derr_server<<"Error while setting sign text: "
2546 "block not found"<<std::endl;
2550 MapBlockObject *obj = block->getObject(id);
2553 derr_server<<"Error while setting sign text: "
2554 "object not found"<<std::endl;
2558 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2560 derr_server<<"Error while setting sign text: "
2561 "object is not a sign"<<std::endl;
2565 ((SignObject*)obj)->setText(text);
2567 obj->getBlock()->setChangedFlag();
2569 else if(command == TOSERVER_SIGNNODETEXT)
2577 std::string datastring((char*)&data[2], datasize-2);
2578 std::istringstream is(datastring, std::ios_base::binary);
2581 is.read((char*)buf, 6);
2582 v3s16 p = readV3S16(buf);
2583 is.read((char*)buf, 2);
2584 u16 textlen = readU16(buf);
2586 for(u16 i=0; i<textlen; i++)
2588 is.read((char*)buf, 1);
2589 text += (char)buf[0];
2592 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2595 if(meta->typeId() != CONTENT_SIGN_WALL)
2597 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2598 signmeta->setText(text);
2600 v3s16 blockpos = getNodeBlockPos(p);
2601 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2604 block->setChangedFlag();
2607 for(core::map<u16, RemoteClient*>::Iterator
2608 i = m_clients.getIterator();
2609 i.atEnd()==false; i++)
2611 RemoteClient *client = i.getNode()->getValue();
2612 client->SetBlockNotSent(blockpos);
2615 else if(command == TOSERVER_INVENTORY_ACTION)
2617 /*// Ignore inventory changes if in creative mode
2618 if(g_settings.getBool("creative_mode") == true)
2620 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2624 // Strip command and create a stream
2625 std::string datastring((char*)&data[2], datasize-2);
2626 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2627 std::istringstream is(datastring, std::ios_base::binary);
2629 InventoryAction *a = InventoryAction::deSerialize(is);
2634 c.current_player = player;
2637 Handle craftresult specially if not in creative mode
2639 bool disable_action = false;
2640 if(a->getType() == IACTION_MOVE
2641 && g_settings.getBool("creative_mode") == false)
2643 IMoveAction *ma = (IMoveAction*)a;
2644 if(ma->to_inv == "current_player" &&
2645 ma->from_inv == "current_player")
2647 // Don't allow moving anything to craftresult
2648 if(ma->to_list == "craftresult")
2651 disable_action = true;
2653 // When something is removed from craftresult
2654 if(ma->from_list == "craftresult")
2656 disable_action = true;
2657 // Remove stuff from craft
2658 InventoryList *clist = player->inventory.getList("craft");
2661 u16 count = ma->count;
2664 clist->decrementMaterials(count);
2667 // Feed action to player inventory
2668 //a->apply(&player->inventory);
2672 // If something appeared in craftresult, throw it
2674 InventoryList *rlist = player->inventory.getList("craftresult");
2675 InventoryList *mlist = player->inventory.getList("main");
2676 if(rlist && mlist && rlist->getUsedSlots() == 1)
2678 InventoryItem *item1 = rlist->changeItem(0, NULL);
2679 mlist->addItem(item1);
2685 if(disable_action == false)
2687 // Feed action to player inventory
2688 //a->apply(&player->inventory);
2696 SendInventory(player->peer_id);
2701 dstream<<"TOSERVER_INVENTORY_ACTION: "
2702 <<"InventoryAction::deSerialize() returned NULL"
2706 else if(command == TOSERVER_CHAT_MESSAGE)
2714 std::string datastring((char*)&data[2], datasize-2);
2715 std::istringstream is(datastring, std::ios_base::binary);
2718 is.read((char*)buf, 2);
2719 u16 len = readU16(buf);
2721 std::wstring message;
2722 for(u16 i=0; i<len; i++)
2724 is.read((char*)buf, 2);
2725 message += (wchar_t)readU16(buf);
2728 // Get player name of this client
2729 std::wstring name = narrow_to_wide(player->getName());
2731 // Line to send to players
2733 // Whether to send to the player that sent the line
2734 bool send_to_sender = false;
2735 // Whether to send to other players
2736 bool send_to_others = false;
2739 std::wstring commandprefix = L"/#";
2740 if(message.substr(0, commandprefix.size()) == commandprefix)
2742 line += L"Server: ";
2744 message = message.substr(commandprefix.size());
2745 // Get player name as narrow string
2746 std::string name_s = player->getName();
2747 // Convert message to narrow string
2748 std::string message_s = wide_to_narrow(message);
2749 // Operator is the single name defined in config.
2750 std::string operator_name = g_settings.get("name");
2751 bool is_operator = (operator_name != "" &&
2752 wide_to_narrow(name) == operator_name);
2753 bool valid_command = false;
2754 if(message_s == "help")
2756 line += L"-!- Available commands: ";
2760 line += L"shutdown setting ";
2765 send_to_sender = true;
2766 valid_command = true;
2768 else if(message_s == "status")
2770 line = getStatusString();
2771 send_to_sender = true;
2772 valid_command = true;
2774 else if(is_operator)
2776 if(message_s == "shutdown")
2778 dstream<<DTIME<<" Server: Operator requested shutdown."
2780 m_shutdown_requested.set(true);
2782 line += L"*** Server shutting down (operator request)";
2783 send_to_sender = true;
2784 valid_command = true;
2786 else if(message_s.substr(0,8) == "setting ")
2788 std::string confline = message_s.substr(8);
2789 g_settings.parseConfigLine(confline);
2790 line += L"-!- Setting changed.";
2791 send_to_sender = true;
2792 valid_command = true;
2796 if(valid_command == false)
2798 line += L"-!- Invalid command: " + message;
2799 send_to_sender = true;
2810 send_to_others = true;
2815 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2818 Send the message to clients
2820 for(core::map<u16, RemoteClient*>::Iterator
2821 i = m_clients.getIterator();
2822 i.atEnd() == false; i++)
2824 // Get client and check that it is valid
2825 RemoteClient *client = i.getNode()->getValue();
2826 assert(client->peer_id == i.getNode()->getKey());
2827 if(client->serialization_version == SER_FMT_VER_INVALID)
2831 bool sender_selected = (peer_id == client->peer_id);
2832 if(sender_selected == true && send_to_sender == false)
2834 if(sender_selected == false && send_to_others == false)
2837 SendChatMessage(client->peer_id, line);
2843 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2844 "unknown command "<<command<<std::endl;
2848 catch(SendFailedException &e)
2850 derr_server<<"Server::ProcessData(): SendFailedException: "
2856 void Server::onMapEditEvent(MapEditEvent *event)
2858 dstream<<"Server::onMapEditEvent()"<<std::endl;
2859 if(m_ignore_map_edit_events)
2861 MapEditEvent *e = event->clone();
2862 m_unsent_map_edit_queue.push_back(e);
2865 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2867 if(id == "current_player")
2869 assert(c->current_player);
2870 return &(c->current_player->inventory);
2874 std::string id0 = fn.next(":");
2876 if(id0 == "nodemeta")
2879 p.X = stoi(fn.next(","));
2880 p.Y = stoi(fn.next(","));
2881 p.Z = stoi(fn.next(","));
2882 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2884 return meta->getInventory();
2885 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2886 <<"no metadata found"<<std::endl;
2890 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2893 void Server::inventoryModified(InventoryContext *c, std::string id)
2895 if(id == "current_player")
2897 assert(c->current_player);
2899 SendInventory(c->current_player->peer_id);
2904 std::string id0 = fn.next(":");
2906 if(id0 == "nodemeta")
2909 p.X = stoi(fn.next(","));
2910 p.Y = stoi(fn.next(","));
2911 p.Z = stoi(fn.next(","));
2912 v3s16 blockpos = getNodeBlockPos(p);
2914 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2916 meta->inventoryModified();
2918 for(core::map<u16, RemoteClient*>::Iterator
2919 i = m_clients.getIterator();
2920 i.atEnd()==false; i++)
2922 RemoteClient *client = i.getNode()->getValue();
2923 client->SetBlockNotSent(blockpos);
2929 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2932 core::list<PlayerInfo> Server::getPlayerInfo()
2934 DSTACK(__FUNCTION_NAME);
2935 JMutexAutoLock envlock(m_env_mutex);
2936 JMutexAutoLock conlock(m_con_mutex);
2938 core::list<PlayerInfo> list;
2940 core::list<Player*> players = m_env.getPlayers();
2942 core::list<Player*>::Iterator i;
2943 for(i = players.begin();
2944 i != players.end(); i++)
2948 Player *player = *i;
2951 con::Peer *peer = m_con.GetPeer(player->peer_id);
2952 // Copy info from peer to info struct
2954 info.address = peer->address;
2955 info.avg_rtt = peer->avg_rtt;
2957 catch(con::PeerNotFoundException &e)
2959 // Set dummy peer info
2961 info.address = Address(0,0,0,0,0);
2965 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2966 info.position = player->getPosition();
2968 list.push_back(info);
2975 void Server::peerAdded(con::Peer *peer)
2977 DSTACK(__FUNCTION_NAME);
2978 dout_server<<"Server::peerAdded(): peer->id="
2979 <<peer->id<<std::endl;
2982 c.type = PEER_ADDED;
2983 c.peer_id = peer->id;
2985 m_peer_change_queue.push_back(c);
2988 void Server::deletingPeer(con::Peer *peer, bool timeout)
2990 DSTACK(__FUNCTION_NAME);
2991 dout_server<<"Server::deletingPeer(): peer->id="
2992 <<peer->id<<", timeout="<<timeout<<std::endl;
2995 c.type = PEER_REMOVED;
2996 c.peer_id = peer->id;
2997 c.timeout = timeout;
2998 m_peer_change_queue.push_back(c);
3001 void Server::SendObjectData(float dtime)
3003 DSTACK(__FUNCTION_NAME);
3005 core::map<v3s16, bool> stepped_blocks;
3007 for(core::map<u16, RemoteClient*>::Iterator
3008 i = m_clients.getIterator();
3009 i.atEnd() == false; i++)
3011 u16 peer_id = i.getNode()->getKey();
3012 RemoteClient *client = i.getNode()->getValue();
3013 assert(client->peer_id == peer_id);
3015 if(client->serialization_version == SER_FMT_VER_INVALID)
3018 client->SendObjectData(this, dtime, stepped_blocks);
3022 void Server::SendPlayerInfos()
3024 DSTACK(__FUNCTION_NAME);
3026 //JMutexAutoLock envlock(m_env_mutex);
3028 // Get connected players
3029 core::list<Player*> players = m_env.getPlayers(true);
3031 u32 player_count = players.getSize();
3032 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3034 SharedBuffer<u8> data(datasize);
3035 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3038 core::list<Player*>::Iterator i;
3039 for(i = players.begin();
3040 i != players.end(); i++)
3042 Player *player = *i;
3044 /*dstream<<"Server sending player info for player with "
3045 "peer_id="<<player->peer_id<<std::endl;*/
3047 writeU16(&data[start], player->peer_id);
3048 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3049 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3050 start += 2+PLAYERNAME_SIZE;
3053 //JMutexAutoLock conlock(m_con_mutex);
3056 m_con.SendToAll(0, data, true);
3059 void Server::SendInventory(u16 peer_id)
3061 DSTACK(__FUNCTION_NAME);
3063 Player* player = m_env.getPlayer(peer_id);
3066 Calculate crafting stuff
3068 if(g_settings.getBool("creative_mode") == false)
3070 InventoryList *clist = player->inventory.getList("craft");
3071 InventoryList *rlist = player->inventory.getList("craftresult");
3074 rlist->clearItems();
3078 InventoryItem *items[9];
3079 for(u16 i=0; i<9; i++)
3081 items[i] = clist->getItem(i);
3090 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3091 if(checkItemCombination(items, specs))
3093 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3102 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3103 if(checkItemCombination(items, specs))
3105 rlist->addItem(new CraftItem("Stick", 4));
3114 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3115 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3116 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3117 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3118 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3119 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3120 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3121 if(checkItemCombination(items, specs))
3123 //rlist->addItem(new MapBlockObjectItem("Sign"));
3124 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3133 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3134 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3135 if(checkItemCombination(items, specs))
3137 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3146 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3147 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3148 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3149 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3150 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3151 if(checkItemCombination(items, specs))
3153 rlist->addItem(new ToolItem("WPick", 0));
3162 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3163 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3164 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3165 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3166 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3167 if(checkItemCombination(items, specs))
3169 rlist->addItem(new ToolItem("STPick", 0));
3178 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3179 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3180 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3181 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3182 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3183 if(checkItemCombination(items, specs))
3185 rlist->addItem(new ToolItem("SteelPick", 0));
3194 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3195 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3196 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3197 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3198 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3199 if(checkItemCombination(items, specs))
3201 rlist->addItem(new ToolItem("MesePick", 0));
3210 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3211 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3212 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3213 if(checkItemCombination(items, specs))
3215 rlist->addItem(new ToolItem("WShovel", 0));
3224 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3225 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3226 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3227 if(checkItemCombination(items, specs))
3229 rlist->addItem(new ToolItem("STShovel", 0));
3238 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3239 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3240 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3241 if(checkItemCombination(items, specs))
3243 rlist->addItem(new ToolItem("SteelShovel", 0));
3252 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3253 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3254 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3255 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3256 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3257 if(checkItemCombination(items, specs))
3259 rlist->addItem(new ToolItem("WAxe", 0));
3268 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3269 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3270 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3271 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3272 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3273 if(checkItemCombination(items, specs))
3275 rlist->addItem(new ToolItem("STAxe", 0));
3284 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3285 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3286 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3287 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3288 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3289 if(checkItemCombination(items, specs))
3291 rlist->addItem(new ToolItem("SteelAxe", 0));
3300 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3301 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3302 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3303 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3304 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3305 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3306 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3307 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3308 if(checkItemCombination(items, specs))
3310 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3319 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3320 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3321 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3322 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3323 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3324 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3325 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3326 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3327 if(checkItemCombination(items, specs))
3329 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3338 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3339 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3340 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3341 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3342 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3343 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3344 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3345 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3346 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3347 if(checkItemCombination(items, specs))
3349 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3355 } // if creative_mode == false
3361 std::ostringstream os;
3362 //os.imbue(std::locale("C"));
3364 player->inventory.serialize(os);
3366 std::string s = os.str();
3368 SharedBuffer<u8> data(s.size()+2);
3369 writeU16(&data[0], TOCLIENT_INVENTORY);
3370 memcpy(&data[2], s.c_str(), s.size());
3373 m_con.Send(peer_id, 0, data, true);
3376 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3378 DSTACK(__FUNCTION_NAME);
3380 std::ostringstream os(std::ios_base::binary);
3384 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3385 os.write((char*)buf, 2);
3388 writeU16(buf, message.size());
3389 os.write((char*)buf, 2);
3392 for(u32 i=0; i<message.size(); i++)
3396 os.write((char*)buf, 2);
3400 std::string s = os.str();
3401 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3403 m_con.Send(peer_id, 0, data, true);
3406 void Server::BroadcastChatMessage(const std::wstring &message)
3408 for(core::map<u16, RemoteClient*>::Iterator
3409 i = m_clients.getIterator();
3410 i.atEnd() == false; i++)
3412 // Get client and check that it is valid
3413 RemoteClient *client = i.getNode()->getValue();
3414 assert(client->peer_id == i.getNode()->getKey());
3415 if(client->serialization_version == SER_FMT_VER_INVALID)
3418 SendChatMessage(client->peer_id, message);
3422 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3423 core::list<u16> *far_players, float far_d_nodes)
3425 float maxd = far_d_nodes*BS;
3426 v3f p_f = intToFloat(p, BS);
3430 SharedBuffer<u8> reply(replysize);
3431 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3432 writeS16(&reply[2], p.X);
3433 writeS16(&reply[4], p.Y);
3434 writeS16(&reply[6], p.Z);
3436 for(core::map<u16, RemoteClient*>::Iterator
3437 i = m_clients.getIterator();
3438 i.atEnd() == false; i++)
3440 // Get client and check that it is valid
3441 RemoteClient *client = i.getNode()->getValue();
3442 assert(client->peer_id == i.getNode()->getKey());
3443 if(client->serialization_version == SER_FMT_VER_INVALID)
3446 // Don't send if it's the same one
3447 if(client->peer_id == ignore_id)
3453 Player *player = m_env.getPlayer(client->peer_id);
3456 // If player is far away, only set modified blocks not sent
3457 v3f player_pos = player->getPosition();
3458 if(player_pos.getDistanceFrom(p_f) > maxd)
3460 far_players->push_back(client->peer_id);
3467 m_con.Send(client->peer_id, 0, reply, true);
3471 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3472 core::list<u16> *far_players, float far_d_nodes)
3474 float maxd = far_d_nodes*BS;
3475 v3f p_f = intToFloat(p, BS);
3477 for(core::map<u16, RemoteClient*>::Iterator
3478 i = m_clients.getIterator();
3479 i.atEnd() == false; i++)
3481 // Get client and check that it is valid
3482 RemoteClient *client = i.getNode()->getValue();
3483 assert(client->peer_id == i.getNode()->getKey());
3484 if(client->serialization_version == SER_FMT_VER_INVALID)
3487 // Don't send if it's the same one
3488 if(client->peer_id == ignore_id)
3494 Player *player = m_env.getPlayer(client->peer_id);
3497 // If player is far away, only set modified blocks not sent
3498 v3f player_pos = player->getPosition();
3499 if(player_pos.getDistanceFrom(p_f) > maxd)
3501 far_players->push_back(client->peer_id);
3508 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3509 SharedBuffer<u8> reply(replysize);
3510 writeU16(&reply[0], TOCLIENT_ADDNODE);
3511 writeS16(&reply[2], p.X);
3512 writeS16(&reply[4], p.Y);
3513 writeS16(&reply[6], p.Z);
3514 n.serialize(&reply[8], client->serialization_version);
3517 m_con.Send(client->peer_id, 0, reply, true);
3521 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3523 DSTACK(__FUNCTION_NAME);
3525 Create a packet with the block in the right format
3528 std::ostringstream os(std::ios_base::binary);
3529 block->serialize(os, ver);
3530 std::string s = os.str();
3531 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3533 u32 replysize = 8 + blockdata.getSize();
3534 SharedBuffer<u8> reply(replysize);
3535 v3s16 p = block->getPos();
3536 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3537 writeS16(&reply[2], p.X);
3538 writeS16(&reply[4], p.Y);
3539 writeS16(&reply[6], p.Z);
3540 memcpy(&reply[8], *blockdata, blockdata.getSize());
3542 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3543 <<": \tpacket size: "<<replysize<<std::endl;*/
3548 m_con.Send(peer_id, 1, reply, true);
3551 void Server::SendBlocks(float dtime)
3553 DSTACK(__FUNCTION_NAME);
3555 JMutexAutoLock envlock(m_env_mutex);
3556 JMutexAutoLock conlock(m_con_mutex);
3558 //TimeTaker timer("Server::SendBlocks");
3560 core::array<PrioritySortedBlockTransfer> queue;
3562 s32 total_sending = 0;
3564 for(core::map<u16, RemoteClient*>::Iterator
3565 i = m_clients.getIterator();
3566 i.atEnd() == false; i++)
3568 RemoteClient *client = i.getNode()->getValue();
3569 assert(client->peer_id == i.getNode()->getKey());
3571 total_sending += client->SendingCount();
3573 if(client->serialization_version == SER_FMT_VER_INVALID)
3576 client->GetNextBlocks(this, dtime, queue);
3580 // Lowest priority number comes first.
3581 // Lowest is most important.
3584 for(u32 i=0; i<queue.size(); i++)
3586 //TODO: Calculate limit dynamically
3587 if(total_sending >= g_settings.getS32
3588 ("max_simultaneous_block_sends_server_total"))
3591 PrioritySortedBlockTransfer q = queue[i];
3593 MapBlock *block = NULL;
3596 block = m_env.getMap().getBlockNoCreate(q.pos);
3598 catch(InvalidPositionException &e)
3603 RemoteClient *client = getClient(q.peer_id);
3605 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3607 client->SentBlock(q.pos);
3614 RemoteClient* Server::getClient(u16 peer_id)
3616 DSTACK(__FUNCTION_NAME);
3617 //JMutexAutoLock lock(m_con_mutex);
3618 core::map<u16, RemoteClient*>::Node *n;
3619 n = m_clients.find(peer_id);
3620 // A client should exist for all peers
3622 return n->getValue();
3625 std::wstring Server::getStatusString()
3627 std::wostringstream os(std::ios_base::binary);
3630 os<<L"uptime="<<m_uptime.get();
3631 // Information about clients
3633 for(core::map<u16, RemoteClient*>::Iterator
3634 i = m_clients.getIterator();
3635 i.atEnd() == false; i++)
3637 // Get client and check that it is valid
3638 RemoteClient *client = i.getNode()->getValue();
3639 assert(client->peer_id == i.getNode()->getKey());
3640 if(client->serialization_version == SER_FMT_VER_INVALID)
3643 Player *player = m_env.getPlayer(client->peer_id);
3644 // Get name of player
3645 std::wstring name = L"unknown";
3647 name = narrow_to_wide(player->getName());
3648 // Add name to information string
3652 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3653 os<<" WARNING: Map saving is disabled."<<std::endl;
3658 void setCreativeInventory(Player *player)
3660 player->resetInventory();
3662 // Give some good picks
3664 InventoryItem *item = new ToolItem("STPick", 0);
3665 void* r = player->inventory.addItem("main", item);
3669 InventoryItem *item = new ToolItem("MesePick", 0);
3670 void* r = player->inventory.addItem("main", item);
3678 // CONTENT_IGNORE-terminated list
3679 u8 material_items[] = {
3688 CONTENT_WATERSOURCE,
3696 u8 *mip = material_items;
3697 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3699 if(*mip == CONTENT_IGNORE)
3702 InventoryItem *item = new MaterialItem(*mip, 1);
3703 player->inventory.addItem("main", item);
3709 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3712 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3713 player->inventory.addItem("main", item);
3716 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3718 // Skip some materials
3719 if(i == CONTENT_WATER || i == CONTENT_TORCH
3720 || i == CONTENT_COALSTONE)
3723 InventoryItem *item = new MaterialItem(i, 1);
3724 player->inventory.addItem("main", item);
3730 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3731 void* r = player->inventory.addItem("main", item);
3736 Player *Server::emergePlayer(const char *name, const char *password,
3740 Try to get an existing player
3742 Player *player = m_env.getPlayer(name);
3745 // If player is already connected, cancel
3746 if(player->peer_id != 0)
3748 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3753 player->peer_id = peer_id;
3755 // Reset inventory to creative if in creative mode
3756 if(g_settings.getBool("creative_mode"))
3758 setCreativeInventory(player);
3765 If player with the wanted peer_id already exists, cancel.
3767 if(m_env.getPlayer(peer_id) != NULL)
3769 dstream<<"emergePlayer(): Player with wrong name but same"
3770 " peer_id already exists"<<std::endl;
3778 player = new ServerRemotePlayer();
3779 //player->peer_id = c.peer_id;
3780 //player->peer_id = PEER_ID_INEXISTENT;
3781 player->peer_id = peer_id;
3782 player->updateName(name);
3788 dstream<<"Server: Finding spawn place for player \""
3789 <<player->getName()<<"\""<<std::endl;
3793 player->setPosition(intToFloat(v3s16(
3800 s16 groundheight = 0;
3802 // Try to find a good place a few times
3803 for(s32 i=0; i<1000; i++)
3806 // We're going to try to throw the player to this position
3807 nodepos = v2s16(-range + (myrand()%(range*2)),
3808 -range + (myrand()%(range*2)));
3809 v2s16 sectorpos = getNodeSectorPos(nodepos);
3810 // Get sector (NOTE: Don't get because it's slow)
3811 //m_env.getMap().emergeSector(sectorpos);
3812 // Get ground height at point (fallbacks to heightmap function)
3813 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3814 // Don't go underwater
3815 if(groundheight < WATER_LEVEL)
3817 //dstream<<"-> Underwater"<<std::endl;
3820 // Don't go to high places
3821 if(groundheight > WATER_LEVEL + 4)
3823 //dstream<<"-> Underwater"<<std::endl;
3828 // Doesn't work, generating blocks is a bit too complicated for doing here
3829 // Get block at point
3831 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3832 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3833 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3834 // Don't go inside ground
3836 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3837 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3838 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3839 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3840 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3841 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3843 dstream<<"-> Inside ground"<<std::endl;
3847 }catch(InvalidPositionException &e)
3849 dstream<<"-> Invalid position"<<std::endl;
3850 // Ignore invalid position
3855 // Found a good place
3856 dstream<<"Searched through "<<i<<" places."<<std::endl;
3861 // If no suitable place was not found, go above water at least.
3862 if(groundheight < WATER_LEVEL)
3863 groundheight = WATER_LEVEL;
3865 player->setPosition(intToFloat(v3s16(
3867 groundheight + 5, // Accomodate mud
3873 Add player to environment
3876 m_env.addPlayer(player);
3879 Add stuff to inventory
3882 if(g_settings.getBool("creative_mode"))
3884 setCreativeInventory(player);
3889 InventoryItem *item = new ToolItem("WPick", 32000);
3890 void* r = player->inventory.addItem("main", item);
3894 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3895 void* r = player->inventory.addItem("main", item);
3899 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3900 void* r = player->inventory.addItem("main", item);
3904 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3905 void* r = player->inventory.addItem("main", item);
3909 InventoryItem *item = new CraftItem("Stick", 4);
3910 void* r = player->inventory.addItem("main", item);
3914 InventoryItem *item = new ToolItem("WPick", 32000);
3915 void* r = player->inventory.addItem("main", item);
3919 InventoryItem *item = new ToolItem("STPick", 32000);
3920 void* r = player->inventory.addItem("main", item);
3923 /*// Give some lights
3925 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3926 bool r = player->inventory.addItem("main", item);
3930 for(u16 i=0; i<4; i++)
3932 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3933 bool r = player->inventory.addItem("main", item);
3936 /*// Give some other stuff
3938 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3939 bool r = player->inventory.addItem("main", item);
3946 } // create new player
3949 void Server::handlePeerChange(PeerChange &c)
3951 JMutexAutoLock envlock(m_env_mutex);
3952 JMutexAutoLock conlock(m_con_mutex);
3954 if(c.type == PEER_ADDED)
3961 core::map<u16, RemoteClient*>::Node *n;
3962 n = m_clients.find(c.peer_id);
3963 // The client shouldn't already exist
3967 RemoteClient *client = new RemoteClient();
3968 client->peer_id = c.peer_id;
3969 m_clients.insert(client->peer_id, client);
3972 else if(c.type == PEER_REMOVED)
3979 core::map<u16, RemoteClient*>::Node *n;
3980 n = m_clients.find(c.peer_id);
3981 // The client should exist
3984 // Collect information about leaving in chat
3985 std::wstring message;
3987 std::wstring name = L"unknown";
3988 Player *player = m_env.getPlayer(c.peer_id);
3990 name = narrow_to_wide(player->getName());
3994 message += L" left game";
3996 message += L" (timed out)";
4001 m_env.removePlayer(c.peer_id);
4004 // Set player client disconnected
4006 Player *player = m_env.getPlayer(c.peer_id);
4008 player->peer_id = 0;
4012 delete m_clients[c.peer_id];
4013 m_clients.remove(c.peer_id);
4015 // Send player info to all remaining clients
4018 // Send leave chat message to all remaining clients
4019 BroadcastChatMessage(message);
4028 void Server::handlePeerChanges()
4030 while(m_peer_change_queue.size() > 0)
4032 PeerChange c = m_peer_change_queue.pop_front();
4034 dout_server<<"Server: Handling peer change: "
4035 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4038 handlePeerChange(c);
4042 void dedicated_server_loop(Server &server, bool &kill)
4044 DSTACK(__FUNCTION_NAME);
4046 std::cout<<DTIME<<std::endl;
4047 std::cout<<"========================"<<std::endl;
4048 std::cout<<"Running dedicated server"<<std::endl;
4049 std::cout<<"========================"<<std::endl;
4050 std::cout<<std::endl;
4054 // This is kind of a hack but can be done like this
4055 // because server.step() is very light
4059 if(server.getShutdownRequested() || kill)
4061 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4065 static int counter = 0;
4071 core::list<PlayerInfo> list = server.getPlayerInfo();
4072 core::list<PlayerInfo>::Iterator i;
4073 static u32 sum_old = 0;
4074 u32 sum = PIChecksum(list);
4077 std::cout<<DTIME<<"Player info:"<<std::endl;
4078 for(i=list.begin(); i!=list.end(); i++)
4080 i->PrintLine(&std::cout);