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()<<" ";
1226 client->PrintInfo(std::cout);
1231 if(g_settings.getBool("enable_experimental"))
1235 Check added and deleted active objects
1238 JMutexAutoLock envlock(m_env_mutex);
1239 JMutexAutoLock conlock(m_con_mutex);
1241 // Radius inside which objects are active
1244 for(core::map<u16, RemoteClient*>::Iterator
1245 i = m_clients.getIterator();
1246 i.atEnd() == false; i++)
1248 RemoteClient *client = i.getNode()->getValue();
1249 Player *player = m_env.getPlayer(client->peer_id);
1252 v3s16 pos = floatToInt(player->getPosition(), BS);
1254 core::map<u16, bool> removed_objects;
1255 core::map<u16, bool> added_objects;
1256 m_env.getRemovedActiveObjects(pos, radius,
1257 client->m_known_objects, removed_objects);
1258 m_env.getAddedActiveObjects(pos, radius,
1259 client->m_known_objects, added_objects);
1261 // Ignore if nothing happened
1262 if(removed_objects.size() == 0 && added_objects.size() == 0)
1265 std::string data_buffer;
1269 // Handle removed objects
1270 writeU16((u8*)buf, removed_objects.size());
1271 data_buffer.append(buf, 2);
1272 for(core::map<u16, bool>::Iterator
1273 i = removed_objects.getIterator();
1274 i.atEnd()==false; i++)
1277 u16 id = i.getNode()->getKey();
1278 ServerActiveObject* obj = m_env.getActiveObject(id);
1280 // Add to data buffer for sending
1281 writeU16((u8*)buf, i.getNode()->getKey());
1282 data_buffer.append(buf, 2);
1284 // Remove from known objects
1285 client->m_known_objects.remove(i.getNode()->getKey());
1287 if(obj && obj->m_known_by_count > 0)
1288 obj->m_known_by_count--;
1291 // Handle added objects
1292 writeU16((u8*)buf, added_objects.size());
1293 data_buffer.append(buf, 2);
1294 for(core::map<u16, bool>::Iterator
1295 i = added_objects.getIterator();
1296 i.atEnd()==false; i++)
1299 u16 id = i.getNode()->getKey();
1300 ServerActiveObject* obj = m_env.getActiveObject(id);
1303 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1305 dstream<<"WARNING: "<<__FUNCTION_NAME
1306 <<": NULL object"<<std::endl;
1308 type = obj->getType();
1310 // Add to data buffer for sending
1311 writeU16((u8*)buf, id);
1312 data_buffer.append(buf, 2);
1313 writeU8((u8*)buf, type);
1314 data_buffer.append(buf, 1);
1316 data_buffer.append(serializeLongString(
1317 obj->getClientInitializationData()));
1319 // Add to known objects
1320 client->m_known_objects.insert(i.getNode()->getKey(), false);
1323 obj->m_known_by_count++;
1327 SharedBuffer<u8> reply(2 + data_buffer.size());
1328 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1329 memcpy((char*)&reply[2], data_buffer.c_str(),
1330 data_buffer.size());
1332 m_con.Send(client->peer_id, 0, reply, true);
1334 dstream<<"INFO: Server: Sent object remove/add: "
1335 <<removed_objects.size()<<" removed, "
1336 <<added_objects.size()<<" added, "
1337 <<"packet size is "<<reply.getSize()<<std::endl;
1342 Send object messages
1345 JMutexAutoLock envlock(m_env_mutex);
1346 JMutexAutoLock conlock(m_con_mutex);
1349 // Value = data sent by object
1350 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1352 // Get active object messages from environment
1355 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1359 core::list<ActiveObjectMessage>* message_list = NULL;
1360 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1361 n = buffered_messages.find(aom.id);
1364 message_list = new core::list<ActiveObjectMessage>;
1365 buffered_messages.insert(aom.id, message_list);
1369 message_list = n->getValue();
1371 message_list->push_back(aom);
1374 // Route data to every client
1375 for(core::map<u16, RemoteClient*>::Iterator
1376 i = m_clients.getIterator();
1377 i.atEnd()==false; i++)
1379 RemoteClient *client = i.getNode()->getValue();
1380 std::string reliable_data;
1381 std::string unreliable_data;
1382 // Go through all objects in message buffer
1383 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1384 j = buffered_messages.getIterator();
1385 j.atEnd()==false; j++)
1387 // If object is not known by client, skip it
1388 u16 id = j.getNode()->getKey();
1389 if(client->m_known_objects.find(id) == NULL)
1391 // Get message list of object
1392 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1393 // Go through every message
1394 for(core::list<ActiveObjectMessage>::Iterator
1395 k = list->begin(); k != list->end(); k++)
1397 // Compose the full new data with header
1398 ActiveObjectMessage aom = *k;
1399 std::string new_data;
1402 writeU16((u8*)&buf[0], aom.id);
1403 new_data.append(buf, 2);
1405 new_data += serializeString(aom.datastring);
1406 // Add data to buffer
1408 reliable_data += new_data;
1410 unreliable_data += new_data;
1414 reliable_data and unreliable_data are now ready.
1417 if(reliable_data.size() > 0)
1419 SharedBuffer<u8> reply(2 + reliable_data.size());
1420 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1421 memcpy((char*)&reply[2], reliable_data.c_str(),
1422 reliable_data.size());
1424 m_con.Send(client->peer_id, 0, reply, true);
1426 if(unreliable_data.size() > 0)
1428 SharedBuffer<u8> reply(2 + unreliable_data.size());
1429 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1430 memcpy((char*)&reply[2], unreliable_data.c_str(),
1431 unreliable_data.size());
1432 // Send as unreliable
1433 m_con.Send(client->peer_id, 0, reply, false);
1436 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1438 dstream<<"INFO: Server: Size of object message data: "
1439 <<"reliable: "<<reliable_data.size()
1440 <<", unreliable: "<<unreliable_data.size()
1445 // Clear buffered_messages
1446 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1447 i = buffered_messages.getIterator();
1448 i.atEnd()==false; i++)
1450 delete i.getNode()->getValue();
1454 } // enable_experimental
1457 Send queued-for-sending map edit events.
1460 while(m_unsent_map_edit_queue.size() != 0)
1462 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1464 if(event->type == MEET_ADDNODE)
1466 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1467 sendAddNode(event->p, event->n, event->already_known_by_peer);
1469 else if(event->type == MEET_REMOVENODE)
1471 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1472 sendRemoveNode(event->p, event->already_known_by_peer);
1474 else if(event->type == MEET_OTHER)
1476 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1481 dstream<<"WARNING: Server: Unknown MapEditEvent "
1482 <<((u32)event->type)<<std::endl;
1490 Send object positions
1491 TODO: Get rid of MapBlockObjects
1494 float &counter = m_objectdata_timer;
1496 if(counter >= g_settings.getFloat("objectdata_interval"))
1498 JMutexAutoLock lock1(m_env_mutex);
1499 JMutexAutoLock lock2(m_con_mutex);
1500 SendObjectData(counter);
1510 JMutexAutoLock envlock(m_env_mutex);
1511 JMutexAutoLock conlock(m_con_mutex);
1513 core::map<v3s16, MapBlock*> changed_blocks;
1514 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1516 for(core::map<v3s16, MapBlock*>::Iterator
1517 i = changed_blocks.getIterator();
1518 i.atEnd() == false; i++)
1520 MapBlock *block = i.getNode()->getValue();
1522 for(core::map<u16, RemoteClient*>::Iterator
1523 i = m_clients.getIterator();
1524 i.atEnd()==false; i++)
1526 RemoteClient *client = i.getNode()->getValue();
1527 client->SetBlockNotSent(block->getPos());
1533 Trigger emergethread (it somehow gets to a non-triggered but
1534 bysy state sometimes)
1537 float &counter = m_emergethread_trigger_timer;
1543 m_emergethread.trigger();
1549 float &counter = m_savemap_timer;
1551 if(counter >= g_settings.getFloat("server_map_save_interval"))
1555 JMutexAutoLock lock(m_env_mutex);
1557 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1559 // Save only changed parts
1560 m_env.getMap().save(true);
1562 // Delete unused sectors
1563 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1564 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1565 if(deleted_count > 0)
1567 dout_server<<"Server: Unloaded "<<deleted_count
1568 <<" sectors from memory"<<std::endl;
1572 m_env.serializePlayers(m_mapsavedir);
1578 void Server::Receive()
1580 DSTACK(__FUNCTION_NAME);
1581 u32 data_maxsize = 10000;
1582 Buffer<u8> data(data_maxsize);
1587 JMutexAutoLock conlock(m_con_mutex);
1588 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1591 // This has to be called so that the client list gets synced
1592 // with the peer list of the connection
1593 handlePeerChanges();
1595 ProcessData(*data, datasize, peer_id);
1597 catch(con::InvalidIncomingDataException &e)
1599 derr_server<<"Server::Receive(): "
1600 "InvalidIncomingDataException: what()="
1601 <<e.what()<<std::endl;
1603 catch(con::PeerNotFoundException &e)
1605 //NOTE: This is not needed anymore
1607 // The peer has been disconnected.
1608 // Find the associated player and remove it.
1610 /*JMutexAutoLock envlock(m_env_mutex);
1612 dout_server<<"ServerThread: peer_id="<<peer_id
1613 <<" has apparently closed connection. "
1614 <<"Removing player."<<std::endl;
1616 m_env.removePlayer(peer_id);*/
1620 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1622 DSTACK(__FUNCTION_NAME);
1623 // Environment is locked first.
1624 JMutexAutoLock envlock(m_env_mutex);
1625 JMutexAutoLock conlock(m_con_mutex);
1629 peer = m_con.GetPeer(peer_id);
1631 catch(con::PeerNotFoundException &e)
1633 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1634 <<peer_id<<" not found"<<std::endl;
1638 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1646 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1648 if(command == TOSERVER_INIT)
1650 // [0] u16 TOSERVER_INIT
1651 // [2] u8 SER_FMT_VER_HIGHEST
1652 // [3] u8[20] player_name
1657 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1658 <<peer->id<<std::endl;
1660 // First byte after command is maximum supported
1661 // serialization version
1662 u8 client_max = data[2];
1663 u8 our_max = SER_FMT_VER_HIGHEST;
1664 // Use the highest version supported by both
1665 u8 deployed = core::min_(client_max, our_max);
1666 // If it's lower than the lowest supported, give up.
1667 if(deployed < SER_FMT_VER_LOWEST)
1668 deployed = SER_FMT_VER_INVALID;
1670 //peer->serialization_version = deployed;
1671 getClient(peer->id)->pending_serialization_version = deployed;
1673 if(deployed == SER_FMT_VER_INVALID)
1675 derr_server<<DTIME<<"Server: Cannot negotiate "
1676 "serialization version with peer "
1677 <<peer_id<<std::endl;
1686 const u32 playername_size = 20;
1687 char playername[playername_size];
1688 for(u32 i=0; i<playername_size-1; i++)
1690 playername[i] = data[3+i];
1692 playername[playername_size-1] = 0;
1695 Player *player = emergePlayer(playername, "", peer_id);
1696 //Player *player = m_env.getPlayer(peer_id);
1699 // DEBUG: Test serialization
1700 std::ostringstream test_os;
1701 player->serialize(test_os);
1702 dstream<<"Player serialization test: \""<<test_os.str()
1704 std::istringstream test_is(test_os.str());
1705 player->deSerialize(test_is);
1708 // If failed, cancel
1711 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1712 <<": failed to emerge player"<<std::endl;
1717 // If a client is already connected to the player, cancel
1718 if(player->peer_id != 0)
1720 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1721 <<" tried to connect to "
1722 "an already connected player (peer_id="
1723 <<player->peer_id<<")"<<std::endl;
1726 // Set client of player
1727 player->peer_id = peer_id;
1730 // Check if player doesn't exist
1732 throw con::InvalidIncomingDataException
1733 ("Server::ProcessData(): INIT: Player doesn't exist");
1735 /*// update name if it was supplied
1736 if(datasize >= 20+3)
1739 player->updateName((const char*)&data[3]);
1742 // Now answer with a TOCLIENT_INIT
1744 SharedBuffer<u8> reply(2+1+6+8);
1745 writeU16(&reply[0], TOCLIENT_INIT);
1746 writeU8(&reply[2], deployed);
1747 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1748 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1751 m_con.Send(peer_id, 0, reply, true);
1755 if(command == TOSERVER_INIT2)
1757 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1758 <<peer->id<<std::endl;
1761 getClient(peer->id)->serialization_version
1762 = getClient(peer->id)->pending_serialization_version;
1765 Send some initialization data
1768 // Send player info to all players
1771 // Send inventory to player
1772 SendInventory(peer->id);
1776 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1777 m_time_of_day.get());
1778 m_con.Send(peer->id, 0, data, true);
1781 // Send information about server to player in chat
1782 SendChatMessage(peer_id, getStatusString());
1784 // Send information about joining in chat
1786 std::wstring name = L"unknown";
1787 Player *player = m_env.getPlayer(peer_id);
1789 name = narrow_to_wide(player->getName());
1791 std::wstring message;
1794 message += L" joined game";
1795 BroadcastChatMessage(message);
1801 if(peer_ser_ver == SER_FMT_VER_INVALID)
1803 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1804 " serialization format invalid or not initialized."
1805 " Skipping incoming command="<<command<<std::endl;
1809 Player *player = m_env.getPlayer(peer_id);
1812 derr_server<<"Server::ProcessData(): Cancelling: "
1813 "No player for peer_id="<<peer_id
1817 if(command == TOSERVER_PLAYERPOS)
1819 if(datasize < 2+12+12+4+4)
1823 v3s32 ps = readV3S32(&data[start+2]);
1824 v3s32 ss = readV3S32(&data[start+2+12]);
1825 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1826 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1827 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1828 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1829 pitch = wrapDegrees(pitch);
1830 yaw = wrapDegrees(yaw);
1831 player->setPosition(position);
1832 player->setSpeed(speed);
1833 player->setPitch(pitch);
1834 player->setYaw(yaw);
1836 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1837 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1838 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1840 else if(command == TOSERVER_GOTBLOCKS)
1853 u16 count = data[2];
1854 for(u16 i=0; i<count; i++)
1856 if((s16)datasize < 2+1+(i+1)*6)
1857 throw con::InvalidIncomingDataException
1858 ("GOTBLOCKS length is too short");
1859 v3s16 p = readV3S16(&data[2+1+i*6]);
1860 /*dstream<<"Server: GOTBLOCKS ("
1861 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1862 RemoteClient *client = getClient(peer_id);
1863 client->GotBlock(p);
1866 else if(command == TOSERVER_DELETEDBLOCKS)
1879 u16 count = data[2];
1880 for(u16 i=0; i<count; i++)
1882 if((s16)datasize < 2+1+(i+1)*6)
1883 throw con::InvalidIncomingDataException
1884 ("DELETEDBLOCKS length is too short");
1885 v3s16 p = readV3S16(&data[2+1+i*6]);
1886 /*dstream<<"Server: DELETEDBLOCKS ("
1887 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1888 RemoteClient *client = getClient(peer_id);
1889 client->SetBlockNotSent(p);
1892 else if(command == TOSERVER_CLICK_OBJECT)
1899 [2] u8 button (0=left, 1=right)
1904 u8 button = readU8(&data[2]);
1906 p.X = readS16(&data[3]);
1907 p.Y = readS16(&data[5]);
1908 p.Z = readS16(&data[7]);
1909 s16 id = readS16(&data[9]);
1910 //u16 item_i = readU16(&data[11]);
1912 MapBlock *block = NULL;
1915 block = m_env.getMap().getBlockNoCreate(p);
1917 catch(InvalidPositionException &e)
1919 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1923 MapBlockObject *obj = block->getObject(id);
1927 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1931 //TODO: Check that object is reasonably close
1936 InventoryList *ilist = player->inventory.getList("main");
1937 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1940 // Skip if inventory has no free space
1941 if(ilist->getUsedSlots() == ilist->getSize())
1943 dout_server<<"Player inventory has no free space"<<std::endl;
1948 Create the inventory item
1950 InventoryItem *item = NULL;
1951 // If it is an item-object, take the item from it
1952 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1954 item = ((ItemObject*)obj)->createInventoryItem();
1956 // Else create an item of the object
1959 item = new MapBlockObjectItem
1960 (obj->getInventoryString());
1963 // Add to inventory and send inventory
1964 ilist->addItem(item);
1965 SendInventory(player->peer_id);
1968 // Remove from block
1969 block->removeObject(id);
1972 else if(command == TOSERVER_GROUND_ACTION)
1980 [3] v3s16 nodepos_undersurface
1981 [9] v3s16 nodepos_abovesurface
1986 2: stop digging (all parameters ignored)
1987 3: digging completed
1989 u8 action = readU8(&data[2]);
1991 p_under.X = readS16(&data[3]);
1992 p_under.Y = readS16(&data[5]);
1993 p_under.Z = readS16(&data[7]);
1995 p_over.X = readS16(&data[9]);
1996 p_over.Y = readS16(&data[11]);
1997 p_over.Z = readS16(&data[13]);
1998 u16 item_i = readU16(&data[15]);
2000 //TODO: Check that target is reasonably close
2008 NOTE: This can be used in the future to check if
2009 somebody is cheating, by checking the timing.
2016 else if(action == 2)
2019 RemoteClient *client = getClient(peer->id);
2020 JMutexAutoLock digmutex(client->m_dig_mutex);
2021 client->m_dig_tool_item = -1;
2026 3: Digging completed
2028 else if(action == 3)
2030 // Mandatory parameter; actually used for nothing
2031 core::map<v3s16, MapBlock*> modified_blocks;
2034 u8 mineral = MINERAL_NONE;
2036 bool cannot_remove_node = false;
2040 MapNode n = m_env.getMap().getNode(p_under);
2042 mineral = n.getMineral();
2043 // Get material at position
2045 // If not yet cancelled
2046 if(cannot_remove_node == false)
2048 // If it's not diggable, do nothing
2049 if(content_diggable(material) == false)
2051 derr_server<<"Server: Not finishing digging: "
2052 <<"Node not diggable"
2054 cannot_remove_node = true;
2057 // If not yet cancelled
2058 if(cannot_remove_node == false)
2060 // Get node metadata
2061 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2062 if(meta && meta->nodeRemovalDisabled() == true)
2064 derr_server<<"Server: Not finishing digging: "
2065 <<"Node metadata disables removal"
2067 cannot_remove_node = true;
2071 catch(InvalidPositionException &e)
2073 derr_server<<"Server: Not finishing digging: Node not found."
2074 <<" Adding block to emerge queue."
2076 m_emerge_queue.addBlock(peer_id,
2077 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2078 cannot_remove_node = true;
2082 If node can't be removed, set block to be re-sent to
2085 if(cannot_remove_node)
2087 derr_server<<"Server: Not finishing digging."<<std::endl;
2089 // Client probably has wrong data.
2090 // Set block not sent, so that client will get
2092 dstream<<"Client "<<peer_id<<" tried to dig "
2093 <<"node; but node cannot be removed."
2094 <<" setting MapBlock not sent."<<std::endl;
2095 RemoteClient *client = getClient(peer_id);
2096 v3s16 blockpos = getNodeBlockPos(p_under);
2097 client->SetBlockNotSent(blockpos);
2103 Send the removal to all other clients
2105 sendRemoveNode(p_under, peer_id);
2108 Update and send inventory
2111 if(g_settings.getBool("creative_mode") == false)
2116 InventoryList *mlist = player->inventory.getList("main");
2119 InventoryItem *item = mlist->getItem(item_i);
2120 if(item && (std::string)item->getName() == "ToolItem")
2122 ToolItem *titem = (ToolItem*)item;
2123 std::string toolname = titem->getToolName();
2125 // Get digging properties for material and tool
2126 DiggingProperties prop =
2127 getDiggingProperties(material, toolname);
2129 if(prop.diggable == false)
2131 derr_server<<"Server: WARNING: Player digged"
2132 <<" with impossible material + tool"
2133 <<" combination"<<std::endl;
2136 bool weared_out = titem->addWear(prop.wear);
2140 mlist->deleteItem(item_i);
2146 Add dug item to inventory
2149 InventoryItem *item = NULL;
2151 if(mineral != MINERAL_NONE)
2152 item = getDiggedMineralItem(mineral);
2157 std::string &dug_s = content_features(material).dug_item;
2160 std::istringstream is(dug_s, std::ios::binary);
2161 item = InventoryItem::deSerialize(is);
2167 // Add a item to inventory
2168 player->inventory.addItem("main", item);
2171 SendInventory(player->peer_id);
2177 (this takes some time so it is done after the quick stuff)
2179 m_ignore_map_edit_events = true;
2180 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2181 m_ignore_map_edit_events = false;
2187 else if(action == 1)
2190 InventoryList *ilist = player->inventory.getList("main");
2195 InventoryItem *item = ilist->getItem(item_i);
2197 // If there is no item, it is not possible to add it anywhere
2202 Handle material items
2204 if(std::string("MaterialItem") == item->getName())
2207 // Don't add a node if this is not a free space
2208 MapNode n2 = m_env.getMap().getNode(p_over);
2209 if(content_buildable_to(n2.d) == false)
2211 // Client probably has wrong data.
2212 // Set block not sent, so that client will get
2214 dstream<<"Client "<<peer_id<<" tried to place"
2215 <<" node in invalid position; setting"
2216 <<" MapBlock not sent."<<std::endl;
2217 RemoteClient *client = getClient(peer_id);
2218 v3s16 blockpos = getNodeBlockPos(p_over);
2219 client->SetBlockNotSent(blockpos);
2223 catch(InvalidPositionException &e)
2225 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2226 <<" Adding block to emerge queue."
2228 m_emerge_queue.addBlock(peer_id,
2229 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2233 // Reset build time counter
2234 getClient(peer->id)->m_time_from_building = 0.0;
2237 MaterialItem *mitem = (MaterialItem*)item;
2239 n.d = mitem->getMaterial();
2240 if(content_features(n.d).wall_mounted)
2241 n.dir = packDir(p_under - p_over);
2246 sendAddNode(p_over, n, 0);
2251 InventoryList *ilist = player->inventory.getList("main");
2252 if(g_settings.getBool("creative_mode") == false && ilist)
2254 // Remove from inventory and send inventory
2255 if(mitem->getCount() == 1)
2256 ilist->deleteItem(item_i);
2260 SendInventory(peer_id);
2266 This takes some time so it is done after the quick stuff
2268 core::map<v3s16, MapBlock*> modified_blocks;
2269 m_ignore_map_edit_events = true;
2270 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2271 m_ignore_map_edit_events = false;
2274 Calculate special events
2277 /*if(n.d == CONTENT_MESE)
2280 for(s16 z=-1; z<=1; z++)
2281 for(s16 y=-1; y<=1; y++)
2282 for(s16 x=-1; x<=1; x++)
2293 v3s16 blockpos = getNodeBlockPos(p_over);
2295 MapBlock *block = NULL;
2298 block = m_env.getMap().getBlockNoCreate(blockpos);
2300 catch(InvalidPositionException &e)
2302 derr_server<<"Error while placing object: "
2303 "block not found"<<std::endl;
2307 v3s16 block_pos_i_on_map = block->getPosRelative();
2308 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2310 v3f pos = intToFloat(p_over, BS);
2311 pos -= block_pos_f_on_map;
2313 /*dout_server<<"pos="
2314 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2317 MapBlockObject *obj = NULL;
2320 Handle block object items
2322 if(std::string("MBOItem") == item->getName())
2324 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2326 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2327 "inventorystring=\""
2328 <<oitem->getInventoryString()
2329 <<"\""<<std::endl;*/
2331 obj = oitem->createObject
2332 (pos, player->getYaw(), player->getPitch());
2339 dout_server<<"Placing a miscellaneous item on map"
2342 Create an ItemObject that contains the item.
2344 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2345 std::ostringstream os(std::ios_base::binary);
2346 item->serialize(os);
2347 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2348 iobj->setItemString(os.str());
2354 derr_server<<"WARNING: item resulted in NULL object, "
2355 <<"not placing onto map"
2360 block->addObject(obj);
2362 dout_server<<"Placed object"<<std::endl;
2364 InventoryList *ilist = player->inventory.getList("main");
2365 if(g_settings.getBool("creative_mode") == false && ilist)
2367 // Remove from inventory and send inventory
2368 ilist->deleteItem(item_i);
2370 SendInventory(peer_id);
2378 Catch invalid actions
2382 derr_server<<"WARNING: Server: Invalid action "
2383 <<action<<std::endl;
2387 else if(command == TOSERVER_RELEASE)
2396 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2399 else if(command == TOSERVER_SIGNTEXT)
2408 std::string datastring((char*)&data[2], datasize-2);
2409 std::istringstream is(datastring, std::ios_base::binary);
2412 is.read((char*)buf, 6);
2413 v3s16 blockpos = readV3S16(buf);
2414 is.read((char*)buf, 2);
2415 s16 id = readS16(buf);
2416 is.read((char*)buf, 2);
2417 u16 textlen = readU16(buf);
2419 for(u16 i=0; i<textlen; i++)
2421 is.read((char*)buf, 1);
2422 text += (char)buf[0];
2425 MapBlock *block = NULL;
2428 block = m_env.getMap().getBlockNoCreate(blockpos);
2430 catch(InvalidPositionException &e)
2432 derr_server<<"Error while setting sign text: "
2433 "block not found"<<std::endl;
2437 MapBlockObject *obj = block->getObject(id);
2440 derr_server<<"Error while setting sign text: "
2441 "object not found"<<std::endl;
2445 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2447 derr_server<<"Error while setting sign text: "
2448 "object is not a sign"<<std::endl;
2452 ((SignObject*)obj)->setText(text);
2454 obj->getBlock()->setChangedFlag();
2456 else if(command == TOSERVER_SIGNNODETEXT)
2464 std::string datastring((char*)&data[2], datasize-2);
2465 std::istringstream is(datastring, std::ios_base::binary);
2468 is.read((char*)buf, 6);
2469 v3s16 p = readV3S16(buf);
2470 is.read((char*)buf, 2);
2471 u16 textlen = readU16(buf);
2473 for(u16 i=0; i<textlen; i++)
2475 is.read((char*)buf, 1);
2476 text += (char)buf[0];
2479 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2482 if(meta->typeId() != CONTENT_SIGN_WALL)
2484 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2485 signmeta->setText(text);
2487 v3s16 blockpos = getNodeBlockPos(p);
2488 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2491 block->setChangedFlag();
2494 for(core::map<u16, RemoteClient*>::Iterator
2495 i = m_clients.getIterator();
2496 i.atEnd()==false; i++)
2498 RemoteClient *client = i.getNode()->getValue();
2499 client->SetBlockNotSent(blockpos);
2502 else if(command == TOSERVER_INVENTORY_ACTION)
2504 /*// Ignore inventory changes if in creative mode
2505 if(g_settings.getBool("creative_mode") == true)
2507 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2511 // Strip command and create a stream
2512 std::string datastring((char*)&data[2], datasize-2);
2513 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2514 std::istringstream is(datastring, std::ios_base::binary);
2516 InventoryAction *a = InventoryAction::deSerialize(is);
2521 c.current_player = player;
2524 Handle craftresult specially if not in creative mode
2526 bool disable_action = false;
2527 if(a->getType() == IACTION_MOVE
2528 && g_settings.getBool("creative_mode") == false)
2530 IMoveAction *ma = (IMoveAction*)a;
2531 if(ma->to_inv == "current_player" &&
2532 ma->from_inv == "current_player")
2534 // Don't allow moving anything to craftresult
2535 if(ma->to_list == "craftresult")
2538 disable_action = true;
2540 // When something is removed from craftresult
2541 if(ma->from_list == "craftresult")
2543 disable_action = true;
2544 // Remove stuff from craft
2545 InventoryList *clist = player->inventory.getList("craft");
2548 u16 count = ma->count;
2551 clist->decrementMaterials(count);
2554 // Feed action to player inventory
2555 //a->apply(&player->inventory);
2559 // If something appeared in craftresult, throw it
2561 InventoryList *rlist = player->inventory.getList("craftresult");
2562 InventoryList *mlist = player->inventory.getList("main");
2563 if(rlist && mlist && rlist->getUsedSlots() == 1)
2565 InventoryItem *item1 = rlist->changeItem(0, NULL);
2566 mlist->addItem(item1);
2572 if(disable_action == false)
2574 // Feed action to player inventory
2575 //a->apply(&player->inventory);
2583 SendInventory(player->peer_id);
2588 dstream<<"TOSERVER_INVENTORY_ACTION: "
2589 <<"InventoryAction::deSerialize() returned NULL"
2593 else if(command == TOSERVER_CHAT_MESSAGE)
2601 std::string datastring((char*)&data[2], datasize-2);
2602 std::istringstream is(datastring, std::ios_base::binary);
2605 is.read((char*)buf, 2);
2606 u16 len = readU16(buf);
2608 std::wstring message;
2609 for(u16 i=0; i<len; i++)
2611 is.read((char*)buf, 2);
2612 message += (wchar_t)readU16(buf);
2615 // Get player name of this client
2616 std::wstring name = narrow_to_wide(player->getName());
2618 // Line to send to players
2620 // Whether to send to the player that sent the line
2621 bool send_to_sender = false;
2622 // Whether to send to other players
2623 bool send_to_others = false;
2626 std::wstring commandprefix = L"/#";
2627 if(message.substr(0, commandprefix.size()) == commandprefix)
2629 line += L"Server: ";
2631 message = message.substr(commandprefix.size());
2632 // Get player name as narrow string
2633 std::string name_s = player->getName();
2634 // Convert message to narrow string
2635 std::string message_s = wide_to_narrow(message);
2636 // Operator is the single name defined in config.
2637 std::string operator_name = g_settings.get("name");
2638 bool is_operator = (operator_name != "" &&
2639 wide_to_narrow(name) == operator_name);
2640 bool valid_command = false;
2641 if(message_s == "help")
2643 line += L"-!- Available commands: ";
2647 line += L"shutdown setting ";
2652 send_to_sender = true;
2653 valid_command = true;
2655 else if(message_s == "status")
2657 line = getStatusString();
2658 send_to_sender = true;
2659 valid_command = true;
2661 else if(is_operator)
2663 if(message_s == "shutdown")
2665 dstream<<DTIME<<" Server: Operator requested shutdown."
2667 m_shutdown_requested.set(true);
2669 line += L"*** Server shutting down (operator request)";
2670 send_to_sender = true;
2671 valid_command = true;
2673 else if(message_s.substr(0,8) == "setting ")
2675 std::string confline = message_s.substr(8);
2676 g_settings.parseConfigLine(confline);
2677 line += L"-!- Setting changed.";
2678 send_to_sender = true;
2679 valid_command = true;
2683 if(valid_command == false)
2685 line += L"-!- Invalid command: " + message;
2686 send_to_sender = true;
2697 send_to_others = true;
2702 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2705 Send the message to clients
2707 for(core::map<u16, RemoteClient*>::Iterator
2708 i = m_clients.getIterator();
2709 i.atEnd() == false; i++)
2711 // Get client and check that it is valid
2712 RemoteClient *client = i.getNode()->getValue();
2713 assert(client->peer_id == i.getNode()->getKey());
2714 if(client->serialization_version == SER_FMT_VER_INVALID)
2718 bool sender_selected = (peer_id == client->peer_id);
2719 if(sender_selected == true && send_to_sender == false)
2721 if(sender_selected == false && send_to_others == false)
2724 SendChatMessage(client->peer_id, line);
2730 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2731 "unknown command "<<command<<std::endl;
2735 catch(SendFailedException &e)
2737 derr_server<<"Server::ProcessData(): SendFailedException: "
2743 void Server::onMapEditEvent(MapEditEvent *event)
2745 dstream<<"Server::onMapEditEvent()"<<std::endl;
2746 if(m_ignore_map_edit_events)
2748 MapEditEvent *e = event->clone();
2749 m_unsent_map_edit_queue.push_back(e);
2752 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2754 if(id == "current_player")
2756 assert(c->current_player);
2757 return &(c->current_player->inventory);
2761 std::string id0 = fn.next(":");
2763 if(id0 == "nodemeta")
2766 p.X = stoi(fn.next(","));
2767 p.Y = stoi(fn.next(","));
2768 p.Z = stoi(fn.next(","));
2769 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2771 return meta->getInventory();
2772 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2773 <<"no metadata found"<<std::endl;
2777 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2780 void Server::inventoryModified(InventoryContext *c, std::string id)
2782 if(id == "current_player")
2784 assert(c->current_player);
2786 SendInventory(c->current_player->peer_id);
2791 std::string id0 = fn.next(":");
2793 if(id0 == "nodemeta")
2796 p.X = stoi(fn.next(","));
2797 p.Y = stoi(fn.next(","));
2798 p.Z = stoi(fn.next(","));
2799 v3s16 blockpos = getNodeBlockPos(p);
2801 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2803 meta->inventoryModified();
2805 for(core::map<u16, RemoteClient*>::Iterator
2806 i = m_clients.getIterator();
2807 i.atEnd()==false; i++)
2809 RemoteClient *client = i.getNode()->getValue();
2810 client->SetBlockNotSent(blockpos);
2816 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2819 core::list<PlayerInfo> Server::getPlayerInfo()
2821 DSTACK(__FUNCTION_NAME);
2822 JMutexAutoLock envlock(m_env_mutex);
2823 JMutexAutoLock conlock(m_con_mutex);
2825 core::list<PlayerInfo> list;
2827 core::list<Player*> players = m_env.getPlayers();
2829 core::list<Player*>::Iterator i;
2830 for(i = players.begin();
2831 i != players.end(); i++)
2835 Player *player = *i;
2838 con::Peer *peer = m_con.GetPeer(player->peer_id);
2839 // Copy info from peer to info struct
2841 info.address = peer->address;
2842 info.avg_rtt = peer->avg_rtt;
2844 catch(con::PeerNotFoundException &e)
2846 // Set dummy peer info
2848 info.address = Address(0,0,0,0,0);
2852 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2853 info.position = player->getPosition();
2855 list.push_back(info);
2862 void Server::peerAdded(con::Peer *peer)
2864 DSTACK(__FUNCTION_NAME);
2865 dout_server<<"Server::peerAdded(): peer->id="
2866 <<peer->id<<std::endl;
2869 c.type = PEER_ADDED;
2870 c.peer_id = peer->id;
2872 m_peer_change_queue.push_back(c);
2875 void Server::deletingPeer(con::Peer *peer, bool timeout)
2877 DSTACK(__FUNCTION_NAME);
2878 dout_server<<"Server::deletingPeer(): peer->id="
2879 <<peer->id<<", timeout="<<timeout<<std::endl;
2882 c.type = PEER_REMOVED;
2883 c.peer_id = peer->id;
2884 c.timeout = timeout;
2885 m_peer_change_queue.push_back(c);
2888 void Server::SendObjectData(float dtime)
2890 DSTACK(__FUNCTION_NAME);
2892 core::map<v3s16, bool> stepped_blocks;
2894 for(core::map<u16, RemoteClient*>::Iterator
2895 i = m_clients.getIterator();
2896 i.atEnd() == false; i++)
2898 u16 peer_id = i.getNode()->getKey();
2899 RemoteClient *client = i.getNode()->getValue();
2900 assert(client->peer_id == peer_id);
2902 if(client->serialization_version == SER_FMT_VER_INVALID)
2905 client->SendObjectData(this, dtime, stepped_blocks);
2909 void Server::SendPlayerInfos()
2911 DSTACK(__FUNCTION_NAME);
2913 //JMutexAutoLock envlock(m_env_mutex);
2915 // Get connected players
2916 core::list<Player*> players = m_env.getPlayers(true);
2918 u32 player_count = players.getSize();
2919 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2921 SharedBuffer<u8> data(datasize);
2922 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2925 core::list<Player*>::Iterator i;
2926 for(i = players.begin();
2927 i != players.end(); i++)
2929 Player *player = *i;
2931 /*dstream<<"Server sending player info for player with "
2932 "peer_id="<<player->peer_id<<std::endl;*/
2934 writeU16(&data[start], player->peer_id);
2935 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2936 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2937 start += 2+PLAYERNAME_SIZE;
2940 //JMutexAutoLock conlock(m_con_mutex);
2943 m_con.SendToAll(0, data, true);
2946 void Server::SendInventory(u16 peer_id)
2948 DSTACK(__FUNCTION_NAME);
2950 Player* player = m_env.getPlayer(peer_id);
2953 Calculate crafting stuff
2955 if(g_settings.getBool("creative_mode") == false)
2957 InventoryList *clist = player->inventory.getList("craft");
2958 InventoryList *rlist = player->inventory.getList("craftresult");
2961 rlist->clearItems();
2965 InventoryItem *items[9];
2966 for(u16 i=0; i<9; i++)
2968 items[i] = clist->getItem(i);
2977 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2978 if(checkItemCombination(items, specs))
2980 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2989 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2990 if(checkItemCombination(items, specs))
2992 rlist->addItem(new CraftItem("Stick", 4));
3001 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3002 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3003 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3004 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3005 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3006 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3007 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3008 if(checkItemCombination(items, specs))
3010 //rlist->addItem(new MapBlockObjectItem("Sign"));
3011 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3020 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3021 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3022 if(checkItemCombination(items, specs))
3024 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3033 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3034 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3035 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3036 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3037 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3038 if(checkItemCombination(items, specs))
3040 rlist->addItem(new ToolItem("WPick", 0));
3049 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3050 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3051 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3052 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3053 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3054 if(checkItemCombination(items, specs))
3056 rlist->addItem(new ToolItem("STPick", 0));
3065 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3066 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3067 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3068 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3069 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3070 if(checkItemCombination(items, specs))
3072 rlist->addItem(new ToolItem("SteelPick", 0));
3081 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3082 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3083 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3084 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3085 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3086 if(checkItemCombination(items, specs))
3088 rlist->addItem(new ToolItem("MesePick", 0));
3097 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3098 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3099 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3100 if(checkItemCombination(items, specs))
3102 rlist->addItem(new ToolItem("WShovel", 0));
3111 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3112 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3113 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3114 if(checkItemCombination(items, specs))
3116 rlist->addItem(new ToolItem("STShovel", 0));
3125 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3126 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3127 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3128 if(checkItemCombination(items, specs))
3130 rlist->addItem(new ToolItem("SteelShovel", 0));
3139 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3140 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3141 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3142 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3143 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3144 if(checkItemCombination(items, specs))
3146 rlist->addItem(new ToolItem("WAxe", 0));
3155 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3156 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3157 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3158 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3159 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3160 if(checkItemCombination(items, specs))
3162 rlist->addItem(new ToolItem("STAxe", 0));
3171 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3172 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3173 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3174 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3175 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3176 if(checkItemCombination(items, specs))
3178 rlist->addItem(new ToolItem("SteelAxe", 0));
3187 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3188 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3189 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3190 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3191 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3192 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3193 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3194 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3195 if(checkItemCombination(items, specs))
3197 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3206 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3207 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3208 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3209 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3210 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3211 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3212 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3213 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3214 if(checkItemCombination(items, specs))
3216 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3225 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3226 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3227 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3228 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3229 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3230 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3231 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3232 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3233 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3234 if(checkItemCombination(items, specs))
3236 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3242 } // if creative_mode == false
3248 std::ostringstream os;
3249 //os.imbue(std::locale("C"));
3251 player->inventory.serialize(os);
3253 std::string s = os.str();
3255 SharedBuffer<u8> data(s.size()+2);
3256 writeU16(&data[0], TOCLIENT_INVENTORY);
3257 memcpy(&data[2], s.c_str(), s.size());
3260 m_con.Send(peer_id, 0, data, true);
3263 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3265 DSTACK(__FUNCTION_NAME);
3267 std::ostringstream os(std::ios_base::binary);
3271 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3272 os.write((char*)buf, 2);
3275 writeU16(buf, message.size());
3276 os.write((char*)buf, 2);
3279 for(u32 i=0; i<message.size(); i++)
3283 os.write((char*)buf, 2);
3287 std::string s = os.str();
3288 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3290 m_con.Send(peer_id, 0, data, true);
3293 void Server::BroadcastChatMessage(const std::wstring &message)
3295 for(core::map<u16, RemoteClient*>::Iterator
3296 i = m_clients.getIterator();
3297 i.atEnd() == false; i++)
3299 // Get client and check that it is valid
3300 RemoteClient *client = i.getNode()->getValue();
3301 assert(client->peer_id == i.getNode()->getKey());
3302 if(client->serialization_version == SER_FMT_VER_INVALID)
3305 SendChatMessage(client->peer_id, message);
3309 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3313 SharedBuffer<u8> reply(replysize);
3314 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3315 writeS16(&reply[2], p.X);
3316 writeS16(&reply[4], p.Y);
3317 writeS16(&reply[6], p.Z);
3319 for(core::map<u16, RemoteClient*>::Iterator
3320 i = m_clients.getIterator();
3321 i.atEnd() == false; i++)
3323 // Get client and check that it is valid
3324 RemoteClient *client = i.getNode()->getValue();
3325 assert(client->peer_id == i.getNode()->getKey());
3326 if(client->serialization_version == SER_FMT_VER_INVALID)
3329 // Don't send if it's the same one
3330 if(client->peer_id == ignore_id)
3334 m_con.Send(client->peer_id, 0, reply, true);
3338 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3340 for(core::map<u16, RemoteClient*>::Iterator
3341 i = m_clients.getIterator();
3342 i.atEnd() == false; i++)
3344 // Get client and check that it is valid
3345 RemoteClient *client = i.getNode()->getValue();
3346 assert(client->peer_id == i.getNode()->getKey());
3347 if(client->serialization_version == SER_FMT_VER_INVALID)
3350 // Don't send if it's the same one
3351 if(client->peer_id == ignore_id)
3355 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3356 SharedBuffer<u8> reply(replysize);
3357 writeU16(&reply[0], TOCLIENT_ADDNODE);
3358 writeS16(&reply[2], p.X);
3359 writeS16(&reply[4], p.Y);
3360 writeS16(&reply[6], p.Z);
3361 n.serialize(&reply[8], client->serialization_version);
3364 m_con.Send(client->peer_id, 0, reply, true);
3368 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3370 DSTACK(__FUNCTION_NAME);
3372 Create a packet with the block in the right format
3375 std::ostringstream os(std::ios_base::binary);
3376 block->serialize(os, ver);
3377 std::string s = os.str();
3378 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3380 u32 replysize = 8 + blockdata.getSize();
3381 SharedBuffer<u8> reply(replysize);
3382 v3s16 p = block->getPos();
3383 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3384 writeS16(&reply[2], p.X);
3385 writeS16(&reply[4], p.Y);
3386 writeS16(&reply[6], p.Z);
3387 memcpy(&reply[8], *blockdata, blockdata.getSize());
3389 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3390 <<": \tpacket size: "<<replysize<<std::endl;*/
3395 m_con.Send(peer_id, 1, reply, true);
3398 void Server::SendBlocks(float dtime)
3400 DSTACK(__FUNCTION_NAME);
3402 JMutexAutoLock envlock(m_env_mutex);
3403 JMutexAutoLock conlock(m_con_mutex);
3405 //TimeTaker timer("Server::SendBlocks");
3407 core::array<PrioritySortedBlockTransfer> queue;
3409 s32 total_sending = 0;
3411 for(core::map<u16, RemoteClient*>::Iterator
3412 i = m_clients.getIterator();
3413 i.atEnd() == false; i++)
3415 RemoteClient *client = i.getNode()->getValue();
3416 assert(client->peer_id == i.getNode()->getKey());
3418 total_sending += client->SendingCount();
3420 if(client->serialization_version == SER_FMT_VER_INVALID)
3423 client->GetNextBlocks(this, dtime, queue);
3427 // Lowest priority number comes first.
3428 // Lowest is most important.
3431 for(u32 i=0; i<queue.size(); i++)
3433 //TODO: Calculate limit dynamically
3434 if(total_sending >= g_settings.getS32
3435 ("max_simultaneous_block_sends_server_total"))
3438 PrioritySortedBlockTransfer q = queue[i];
3440 MapBlock *block = NULL;
3443 block = m_env.getMap().getBlockNoCreate(q.pos);
3445 catch(InvalidPositionException &e)
3450 RemoteClient *client = getClient(q.peer_id);
3452 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3454 client->SentBlock(q.pos);
3461 RemoteClient* Server::getClient(u16 peer_id)
3463 DSTACK(__FUNCTION_NAME);
3464 //JMutexAutoLock lock(m_con_mutex);
3465 core::map<u16, RemoteClient*>::Node *n;
3466 n = m_clients.find(peer_id);
3467 // A client should exist for all peers
3469 return n->getValue();
3472 std::wstring Server::getStatusString()
3474 std::wostringstream os(std::ios_base::binary);
3477 os<<L"uptime="<<m_uptime.get();
3478 // Information about clients
3480 for(core::map<u16, RemoteClient*>::Iterator
3481 i = m_clients.getIterator();
3482 i.atEnd() == false; i++)
3484 // Get client and check that it is valid
3485 RemoteClient *client = i.getNode()->getValue();
3486 assert(client->peer_id == i.getNode()->getKey());
3487 if(client->serialization_version == SER_FMT_VER_INVALID)
3490 Player *player = m_env.getPlayer(client->peer_id);
3491 // Get name of player
3492 std::wstring name = L"unknown";
3494 name = narrow_to_wide(player->getName());
3495 // Add name to information string
3499 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3500 os<<" WARNING: Map saving is disabled."<<std::endl;
3505 void setCreativeInventory(Player *player)
3507 player->resetInventory();
3509 // Give some good picks
3511 InventoryItem *item = new ToolItem("STPick", 0);
3512 void* r = player->inventory.addItem("main", item);
3516 InventoryItem *item = new ToolItem("MesePick", 0);
3517 void* r = player->inventory.addItem("main", item);
3525 // CONTENT_IGNORE-terminated list
3526 u8 material_items[] = {
3535 CONTENT_WATERSOURCE,
3543 u8 *mip = material_items;
3544 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3546 if(*mip == CONTENT_IGNORE)
3549 InventoryItem *item = new MaterialItem(*mip, 1);
3550 player->inventory.addItem("main", item);
3556 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3559 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3560 player->inventory.addItem("main", item);
3563 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3565 // Skip some materials
3566 if(i == CONTENT_WATER || i == CONTENT_TORCH
3567 || i == CONTENT_COALSTONE)
3570 InventoryItem *item = new MaterialItem(i, 1);
3571 player->inventory.addItem("main", item);
3577 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3578 void* r = player->inventory.addItem("main", item);
3583 Player *Server::emergePlayer(const char *name, const char *password,
3587 Try to get an existing player
3589 Player *player = m_env.getPlayer(name);
3592 // If player is already connected, cancel
3593 if(player->peer_id != 0)
3595 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3600 player->peer_id = peer_id;
3602 // Reset inventory to creative if in creative mode
3603 if(g_settings.getBool("creative_mode"))
3605 setCreativeInventory(player);
3612 If player with the wanted peer_id already exists, cancel.
3614 if(m_env.getPlayer(peer_id) != NULL)
3616 dstream<<"emergePlayer(): Player with wrong name but same"
3617 " peer_id already exists"<<std::endl;
3625 player = new ServerRemotePlayer();
3626 //player->peer_id = c.peer_id;
3627 //player->peer_id = PEER_ID_INEXISTENT;
3628 player->peer_id = peer_id;
3629 player->updateName(name);
3635 dstream<<"Server: Finding spawn place for player \""
3636 <<player->getName()<<"\""<<std::endl;
3640 player->setPosition(intToFloat(v3s16(
3647 s16 groundheight = 0;
3649 // Try to find a good place a few times
3650 for(s32 i=0; i<1000; i++)
3653 // We're going to try to throw the player to this position
3654 nodepos = v2s16(-range + (myrand()%(range*2)),
3655 -range + (myrand()%(range*2)));
3656 v2s16 sectorpos = getNodeSectorPos(nodepos);
3657 // Get sector (NOTE: Don't get because it's slow)
3658 //m_env.getMap().emergeSector(sectorpos);
3659 // Get ground height at point (fallbacks to heightmap function)
3660 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3661 // Don't go underwater
3662 if(groundheight < WATER_LEVEL)
3664 //dstream<<"-> Underwater"<<std::endl;
3667 // Don't go to high places
3668 if(groundheight > WATER_LEVEL + 4)
3670 //dstream<<"-> Underwater"<<std::endl;
3675 // Doesn't work, generating blocks is a bit too complicated for doing here
3676 // Get block at point
3678 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3679 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3680 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3681 // Don't go inside ground
3683 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3684 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3685 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3686 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3687 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3688 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3690 dstream<<"-> Inside ground"<<std::endl;
3694 }catch(InvalidPositionException &e)
3696 dstream<<"-> Invalid position"<<std::endl;
3697 // Ignore invalid position
3702 // Found a good place
3703 dstream<<"Searched through "<<i<<" places."<<std::endl;
3708 // If no suitable place was not found, go above water at least.
3709 if(groundheight < WATER_LEVEL)
3710 groundheight = WATER_LEVEL;
3712 player->setPosition(intToFloat(v3s16(
3714 groundheight + 5, // Accomodate mud
3720 Add player to environment
3723 m_env.addPlayer(player);
3726 Add stuff to inventory
3729 if(g_settings.getBool("creative_mode"))
3731 setCreativeInventory(player);
3736 InventoryItem *item = new ToolItem("WPick", 32000);
3737 void* r = player->inventory.addItem("main", item);
3741 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3742 void* r = player->inventory.addItem("main", item);
3746 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3747 void* r = player->inventory.addItem("main", item);
3751 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3752 void* r = player->inventory.addItem("main", item);
3756 InventoryItem *item = new CraftItem("Stick", 4);
3757 void* r = player->inventory.addItem("main", item);
3761 InventoryItem *item = new ToolItem("WPick", 32000);
3762 void* r = player->inventory.addItem("main", item);
3766 InventoryItem *item = new ToolItem("STPick", 32000);
3767 void* r = player->inventory.addItem("main", item);
3770 /*// Give some lights
3772 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3773 bool r = player->inventory.addItem("main", item);
3777 for(u16 i=0; i<4; i++)
3779 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3780 bool r = player->inventory.addItem("main", item);
3783 /*// Give some other stuff
3785 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3786 bool r = player->inventory.addItem("main", item);
3793 } // create new player
3797 void Server::UpdateBlockWaterPressure(MapBlock *block,
3798 core::map<v3s16, MapBlock*> &modified_blocks)
3800 MapVoxelManipulator v(&m_env.getMap());
3801 v.m_disable_water_climb =
3802 g_settings.getBool("disable_water_climb");
3804 VoxelArea area(block->getPosRelative(),
3805 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3809 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3811 catch(ProcessingLimitException &e)
3813 dstream<<"Processing limit reached (1)"<<std::endl;
3816 v.blitBack(modified_blocks);
3820 void Server::handlePeerChange(PeerChange &c)
3822 JMutexAutoLock envlock(m_env_mutex);
3823 JMutexAutoLock conlock(m_con_mutex);
3825 if(c.type == PEER_ADDED)
3832 core::map<u16, RemoteClient*>::Node *n;
3833 n = m_clients.find(c.peer_id);
3834 // The client shouldn't already exist
3838 RemoteClient *client = new RemoteClient();
3839 client->peer_id = c.peer_id;
3840 m_clients.insert(client->peer_id, client);
3843 else if(c.type == PEER_REMOVED)
3850 core::map<u16, RemoteClient*>::Node *n;
3851 n = m_clients.find(c.peer_id);
3852 // The client should exist
3855 // Collect information about leaving in chat
3856 std::wstring message;
3858 std::wstring name = L"unknown";
3859 Player *player = m_env.getPlayer(c.peer_id);
3861 name = narrow_to_wide(player->getName());
3865 message += L" left game";
3867 message += L" (timed out)";
3872 m_env.removePlayer(c.peer_id);
3875 // Set player client disconnected
3877 Player *player = m_env.getPlayer(c.peer_id);
3879 player->peer_id = 0;
3883 delete m_clients[c.peer_id];
3884 m_clients.remove(c.peer_id);
3886 // Send player info to all remaining clients
3889 // Send leave chat message to all remaining clients
3890 BroadcastChatMessage(message);
3899 void Server::handlePeerChanges()
3901 while(m_peer_change_queue.size() > 0)
3903 PeerChange c = m_peer_change_queue.pop_front();
3905 dout_server<<"Server: Handling peer change: "
3906 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3909 handlePeerChange(c);
3913 void dedicated_server_loop(Server &server, bool &kill)
3915 DSTACK(__FUNCTION_NAME);
3917 std::cout<<DTIME<<std::endl;
3918 std::cout<<"========================"<<std::endl;
3919 std::cout<<"Running dedicated server"<<std::endl;
3920 std::cout<<"========================"<<std::endl;
3921 std::cout<<std::endl;
3925 // This is kind of a hack but can be done like this
3926 // because server.step() is very light
3930 if(server.getShutdownRequested() || kill)
3932 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3936 static int counter = 0;
3942 core::list<PlayerInfo> list = server.getPlayerInfo();
3943 core::list<PlayerInfo>::Iterator i;
3944 static u32 sum_old = 0;
3945 u32 sum = PIChecksum(list);
3948 std::cout<<DTIME<<"Player info:"<<std::endl;
3949 for(i=list.begin(); i!=list.end(); i++)
3951 i->PrintLine(&std::cout);