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);
1507 Trigger emergethread (it somehow gets to a non-triggered but
1508 bysy state sometimes)
1511 float &counter = m_emergethread_trigger_timer;
1517 m_emergethread.trigger();
1523 float &counter = m_savemap_timer;
1525 if(counter >= g_settings.getFloat("server_map_save_interval"))
1529 JMutexAutoLock lock(m_env_mutex);
1531 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1533 // Save only changed parts
1534 m_env.getMap().save(true);
1536 // Delete unused sectors
1537 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1538 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1539 if(deleted_count > 0)
1541 dout_server<<"Server: Unloaded "<<deleted_count
1542 <<" sectors from memory"<<std::endl;
1546 m_env.serializePlayers(m_mapsavedir);
1552 void Server::Receive()
1554 DSTACK(__FUNCTION_NAME);
1555 u32 data_maxsize = 10000;
1556 Buffer<u8> data(data_maxsize);
1561 JMutexAutoLock conlock(m_con_mutex);
1562 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1565 // This has to be called so that the client list gets synced
1566 // with the peer list of the connection
1567 handlePeerChanges();
1569 ProcessData(*data, datasize, peer_id);
1571 catch(con::InvalidIncomingDataException &e)
1573 derr_server<<"Server::Receive(): "
1574 "InvalidIncomingDataException: what()="
1575 <<e.what()<<std::endl;
1577 catch(con::PeerNotFoundException &e)
1579 //NOTE: This is not needed anymore
1581 // The peer has been disconnected.
1582 // Find the associated player and remove it.
1584 /*JMutexAutoLock envlock(m_env_mutex);
1586 dout_server<<"ServerThread: peer_id="<<peer_id
1587 <<" has apparently closed connection. "
1588 <<"Removing player."<<std::endl;
1590 m_env.removePlayer(peer_id);*/
1594 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1596 DSTACK(__FUNCTION_NAME);
1597 // Environment is locked first.
1598 JMutexAutoLock envlock(m_env_mutex);
1599 JMutexAutoLock conlock(m_con_mutex);
1603 peer = m_con.GetPeer(peer_id);
1605 catch(con::PeerNotFoundException &e)
1607 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1608 <<peer_id<<" not found"<<std::endl;
1612 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1620 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1622 if(command == TOSERVER_INIT)
1624 // [0] u16 TOSERVER_INIT
1625 // [2] u8 SER_FMT_VER_HIGHEST
1626 // [3] u8[20] player_name
1631 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1632 <<peer->id<<std::endl;
1634 // First byte after command is maximum supported
1635 // serialization version
1636 u8 client_max = data[2];
1637 u8 our_max = SER_FMT_VER_HIGHEST;
1638 // Use the highest version supported by both
1639 u8 deployed = core::min_(client_max, our_max);
1640 // If it's lower than the lowest supported, give up.
1641 if(deployed < SER_FMT_VER_LOWEST)
1642 deployed = SER_FMT_VER_INVALID;
1644 //peer->serialization_version = deployed;
1645 getClient(peer->id)->pending_serialization_version = deployed;
1647 if(deployed == SER_FMT_VER_INVALID)
1649 derr_server<<DTIME<<"Server: Cannot negotiate "
1650 "serialization version with peer "
1651 <<peer_id<<std::endl;
1660 const u32 playername_size = 20;
1661 char playername[playername_size];
1662 for(u32 i=0; i<playername_size-1; i++)
1664 playername[i] = data[3+i];
1666 playername[playername_size-1] = 0;
1669 Player *player = emergePlayer(playername, "", peer_id);
1670 //Player *player = m_env.getPlayer(peer_id);
1673 // DEBUG: Test serialization
1674 std::ostringstream test_os;
1675 player->serialize(test_os);
1676 dstream<<"Player serialization test: \""<<test_os.str()
1678 std::istringstream test_is(test_os.str());
1679 player->deSerialize(test_is);
1682 // If failed, cancel
1685 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1686 <<": failed to emerge player"<<std::endl;
1691 // If a client is already connected to the player, cancel
1692 if(player->peer_id != 0)
1694 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1695 <<" tried to connect to "
1696 "an already connected player (peer_id="
1697 <<player->peer_id<<")"<<std::endl;
1700 // Set client of player
1701 player->peer_id = peer_id;
1704 // Check if player doesn't exist
1706 throw con::InvalidIncomingDataException
1707 ("Server::ProcessData(): INIT: Player doesn't exist");
1709 /*// update name if it was supplied
1710 if(datasize >= 20+3)
1713 player->updateName((const char*)&data[3]);
1716 // Now answer with a TOCLIENT_INIT
1718 SharedBuffer<u8> reply(2+1+6+8);
1719 writeU16(&reply[0], TOCLIENT_INIT);
1720 writeU8(&reply[2], deployed);
1721 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1722 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1725 m_con.Send(peer_id, 0, reply, true);
1729 if(command == TOSERVER_INIT2)
1731 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1732 <<peer->id<<std::endl;
1735 getClient(peer->id)->serialization_version
1736 = getClient(peer->id)->pending_serialization_version;
1739 Send some initialization data
1742 // Send player info to all players
1745 // Send inventory to player
1746 SendInventory(peer->id);
1750 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1751 m_time_of_day.get());
1752 m_con.Send(peer->id, 0, data, true);
1755 // Send information about server to player in chat
1756 SendChatMessage(peer_id, getStatusString());
1758 // Send information about joining in chat
1760 std::wstring name = L"unknown";
1761 Player *player = m_env.getPlayer(peer_id);
1763 name = narrow_to_wide(player->getName());
1765 std::wstring message;
1768 message += L" joined game";
1769 BroadcastChatMessage(message);
1775 if(peer_ser_ver == SER_FMT_VER_INVALID)
1777 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1778 " serialization format invalid or not initialized."
1779 " Skipping incoming command="<<command<<std::endl;
1783 Player *player = m_env.getPlayer(peer_id);
1786 derr_server<<"Server::ProcessData(): Cancelling: "
1787 "No player for peer_id="<<peer_id
1791 if(command == TOSERVER_PLAYERPOS)
1793 if(datasize < 2+12+12+4+4)
1797 v3s32 ps = readV3S32(&data[start+2]);
1798 v3s32 ss = readV3S32(&data[start+2+12]);
1799 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1800 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1801 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1802 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1803 pitch = wrapDegrees(pitch);
1804 yaw = wrapDegrees(yaw);
1805 player->setPosition(position);
1806 player->setSpeed(speed);
1807 player->setPitch(pitch);
1808 player->setYaw(yaw);
1810 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1811 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1812 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1814 else if(command == TOSERVER_GOTBLOCKS)
1827 u16 count = data[2];
1828 for(u16 i=0; i<count; i++)
1830 if((s16)datasize < 2+1+(i+1)*6)
1831 throw con::InvalidIncomingDataException
1832 ("GOTBLOCKS length is too short");
1833 v3s16 p = readV3S16(&data[2+1+i*6]);
1834 /*dstream<<"Server: GOTBLOCKS ("
1835 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1836 RemoteClient *client = getClient(peer_id);
1837 client->GotBlock(p);
1840 else if(command == TOSERVER_DELETEDBLOCKS)
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 ("DELETEDBLOCKS length is too short");
1859 v3s16 p = readV3S16(&data[2+1+i*6]);
1860 /*dstream<<"Server: DELETEDBLOCKS ("
1861 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1862 RemoteClient *client = getClient(peer_id);
1863 client->SetBlockNotSent(p);
1866 else if(command == TOSERVER_CLICK_OBJECT)
1873 [2] u8 button (0=left, 1=right)
1878 u8 button = readU8(&data[2]);
1880 p.X = readS16(&data[3]);
1881 p.Y = readS16(&data[5]);
1882 p.Z = readS16(&data[7]);
1883 s16 id = readS16(&data[9]);
1884 //u16 item_i = readU16(&data[11]);
1886 MapBlock *block = NULL;
1889 block = m_env.getMap().getBlockNoCreate(p);
1891 catch(InvalidPositionException &e)
1893 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1897 MapBlockObject *obj = block->getObject(id);
1901 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1905 //TODO: Check that object is reasonably close
1910 InventoryList *ilist = player->inventory.getList("main");
1911 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1914 // Skip if inventory has no free space
1915 if(ilist->getUsedSlots() == ilist->getSize())
1917 dout_server<<"Player inventory has no free space"<<std::endl;
1922 Create the inventory item
1924 InventoryItem *item = NULL;
1925 // If it is an item-object, take the item from it
1926 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1928 item = ((ItemObject*)obj)->createInventoryItem();
1930 // Else create an item of the object
1933 item = new MapBlockObjectItem
1934 (obj->getInventoryString());
1937 // Add to inventory and send inventory
1938 ilist->addItem(item);
1939 SendInventory(player->peer_id);
1942 // Remove from block
1943 block->removeObject(id);
1946 else if(command == TOSERVER_GROUND_ACTION)
1954 [3] v3s16 nodepos_undersurface
1955 [9] v3s16 nodepos_abovesurface
1960 2: stop digging (all parameters ignored)
1961 3: digging completed
1963 u8 action = readU8(&data[2]);
1965 p_under.X = readS16(&data[3]);
1966 p_under.Y = readS16(&data[5]);
1967 p_under.Z = readS16(&data[7]);
1969 p_over.X = readS16(&data[9]);
1970 p_over.Y = readS16(&data[11]);
1971 p_over.Z = readS16(&data[13]);
1972 u16 item_i = readU16(&data[15]);
1974 //TODO: Check that target is reasonably close
1982 NOTE: This can be used in the future to check if
1983 somebody is cheating, by checking the timing.
1990 else if(action == 2)
1993 RemoteClient *client = getClient(peer->id);
1994 JMutexAutoLock digmutex(client->m_dig_mutex);
1995 client->m_dig_tool_item = -1;
2000 3: Digging completed
2002 else if(action == 3)
2004 // Mandatory parameter; actually used for nothing
2005 core::map<v3s16, MapBlock*> modified_blocks;
2008 u8 mineral = MINERAL_NONE;
2012 MapNode n = m_env.getMap().getNode(p_under);
2013 // Get material at position
2015 // If it's not diggable, do nothing
2016 if(content_diggable(material) == false)
2018 derr_server<<"Server: Not finishing digging: Node not diggable"
2021 // Client probably has wrong data.
2022 // Set block not sent, so that client will get
2024 dstream<<"Client "<<peer_id<<" tried to dig "
2025 <<"node from invalid position; setting"
2026 <<" MapBlock not sent."<<std::endl;
2027 RemoteClient *client = getClient(peer_id);
2028 v3s16 blockpos = getNodeBlockPos(p_under);
2029 client->SetBlockNotSent(blockpos);
2034 mineral = n.getMineral();
2036 catch(InvalidPositionException &e)
2038 derr_server<<"Server: Not finishing digging: Node not found."
2039 <<" Adding block to emerge queue."
2041 m_emerge_queue.addBlock(peer_id,
2042 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2047 Send the removal to all other clients
2049 sendRemoveNode(p_under, peer_id);
2052 Update and send inventory
2055 if(g_settings.getBool("creative_mode") == false)
2060 InventoryList *mlist = player->inventory.getList("main");
2063 InventoryItem *item = mlist->getItem(item_i);
2064 if(item && (std::string)item->getName() == "ToolItem")
2066 ToolItem *titem = (ToolItem*)item;
2067 std::string toolname = titem->getToolName();
2069 // Get digging properties for material and tool
2070 DiggingProperties prop =
2071 getDiggingProperties(material, toolname);
2073 if(prop.diggable == false)
2075 derr_server<<"Server: WARNING: Player digged"
2076 <<" with impossible material + tool"
2077 <<" combination"<<std::endl;
2080 bool weared_out = titem->addWear(prop.wear);
2084 mlist->deleteItem(item_i);
2090 Add dug item to inventory
2093 InventoryItem *item = NULL;
2095 if(mineral != MINERAL_NONE)
2096 item = getDiggedMineralItem(mineral);
2101 std::string &dug_s = content_features(material).dug_item;
2104 std::istringstream is(dug_s, std::ios::binary);
2105 item = InventoryItem::deSerialize(is);
2111 // Add a item to inventory
2112 player->inventory.addItem("main", item);
2115 SendInventory(player->peer_id);
2121 (this takes some time so it is done after the quick stuff)
2123 m_ignore_map_edit_events = true;
2124 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2125 m_ignore_map_edit_events = false;
2131 else if(action == 1)
2134 InventoryList *ilist = player->inventory.getList("main");
2139 InventoryItem *item = ilist->getItem(item_i);
2141 // If there is no item, it is not possible to add it anywhere
2146 Handle material items
2148 if(std::string("MaterialItem") == item->getName())
2151 // Don't add a node if this is not a free space
2152 MapNode n2 = m_env.getMap().getNode(p_over);
2153 if(content_buildable_to(n2.d) == false)
2155 // Client probably has wrong data.
2156 // Set block not sent, so that client will get
2158 dstream<<"Client "<<peer_id<<" tried to place"
2159 <<" node in invalid position; setting"
2160 <<" MapBlock not sent."<<std::endl;
2161 RemoteClient *client = getClient(peer_id);
2162 v3s16 blockpos = getNodeBlockPos(p_over);
2163 client->SetBlockNotSent(blockpos);
2167 catch(InvalidPositionException &e)
2169 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2170 <<" Adding block to emerge queue."
2172 m_emerge_queue.addBlock(peer_id,
2173 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2177 // Reset build time counter
2178 getClient(peer->id)->m_time_from_building = 0.0;
2181 MaterialItem *mitem = (MaterialItem*)item;
2183 n.d = mitem->getMaterial();
2184 if(content_features(n.d).wall_mounted)
2185 n.dir = packDir(p_under - p_over);
2190 sendAddNode(p_over, n, 0);
2195 InventoryList *ilist = player->inventory.getList("main");
2196 if(g_settings.getBool("creative_mode") == false && ilist)
2198 // Remove from inventory and send inventory
2199 if(mitem->getCount() == 1)
2200 ilist->deleteItem(item_i);
2204 SendInventory(peer_id);
2210 This takes some time so it is done after the quick stuff
2212 core::map<v3s16, MapBlock*> modified_blocks;
2213 m_ignore_map_edit_events = true;
2214 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2215 m_ignore_map_edit_events = false;
2218 Calculate special events
2221 /*if(n.d == CONTENT_MESE)
2224 for(s16 z=-1; z<=1; z++)
2225 for(s16 y=-1; y<=1; y++)
2226 for(s16 x=-1; x<=1; x++)
2237 v3s16 blockpos = getNodeBlockPos(p_over);
2239 MapBlock *block = NULL;
2242 block = m_env.getMap().getBlockNoCreate(blockpos);
2244 catch(InvalidPositionException &e)
2246 derr_server<<"Error while placing object: "
2247 "block not found"<<std::endl;
2251 v3s16 block_pos_i_on_map = block->getPosRelative();
2252 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2254 v3f pos = intToFloat(p_over, BS);
2255 pos -= block_pos_f_on_map;
2257 /*dout_server<<"pos="
2258 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2261 MapBlockObject *obj = NULL;
2264 Handle block object items
2266 if(std::string("MBOItem") == item->getName())
2268 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2270 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2271 "inventorystring=\""
2272 <<oitem->getInventoryString()
2273 <<"\""<<std::endl;*/
2275 obj = oitem->createObject
2276 (pos, player->getYaw(), player->getPitch());
2283 dout_server<<"Placing a miscellaneous item on map"
2286 Create an ItemObject that contains the item.
2288 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2289 std::ostringstream os(std::ios_base::binary);
2290 item->serialize(os);
2291 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2292 iobj->setItemString(os.str());
2298 derr_server<<"WARNING: item resulted in NULL object, "
2299 <<"not placing onto map"
2304 block->addObject(obj);
2306 dout_server<<"Placed object"<<std::endl;
2308 InventoryList *ilist = player->inventory.getList("main");
2309 if(g_settings.getBool("creative_mode") == false && ilist)
2311 // Remove from inventory and send inventory
2312 ilist->deleteItem(item_i);
2314 SendInventory(peer_id);
2322 Catch invalid actions
2326 derr_server<<"WARNING: Server: Invalid action "
2327 <<action<<std::endl;
2331 else if(command == TOSERVER_RELEASE)
2340 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2343 else if(command == TOSERVER_SIGNTEXT)
2352 std::string datastring((char*)&data[2], datasize-2);
2353 std::istringstream is(datastring, std::ios_base::binary);
2356 is.read((char*)buf, 6);
2357 v3s16 blockpos = readV3S16(buf);
2358 is.read((char*)buf, 2);
2359 s16 id = readS16(buf);
2360 is.read((char*)buf, 2);
2361 u16 textlen = readU16(buf);
2363 for(u16 i=0; i<textlen; i++)
2365 is.read((char*)buf, 1);
2366 text += (char)buf[0];
2369 MapBlock *block = NULL;
2372 block = m_env.getMap().getBlockNoCreate(blockpos);
2374 catch(InvalidPositionException &e)
2376 derr_server<<"Error while setting sign text: "
2377 "block not found"<<std::endl;
2381 MapBlockObject *obj = block->getObject(id);
2384 derr_server<<"Error while setting sign text: "
2385 "object not found"<<std::endl;
2389 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2391 derr_server<<"Error while setting sign text: "
2392 "object is not a sign"<<std::endl;
2396 ((SignObject*)obj)->setText(text);
2398 obj->getBlock()->setChangedFlag();
2400 else if(command == TOSERVER_SIGNNODETEXT)
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 p = readV3S16(buf);
2414 is.read((char*)buf, 2);
2415 u16 textlen = readU16(buf);
2417 for(u16 i=0; i<textlen; i++)
2419 is.read((char*)buf, 1);
2420 text += (char)buf[0];
2423 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2426 if(meta->typeId() != CONTENT_SIGN_WALL)
2428 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2429 signmeta->setText(text);
2431 v3s16 blockpos = getNodeBlockPos(p);
2432 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2435 block->setChangedFlag();
2438 for(core::map<u16, RemoteClient*>::Iterator
2439 i = m_clients.getIterator();
2440 i.atEnd()==false; i++)
2442 RemoteClient *client = i.getNode()->getValue();
2443 client->SetBlockNotSent(blockpos);
2446 else if(command == TOSERVER_INVENTORY_ACTION)
2448 /*// Ignore inventory changes if in creative mode
2449 if(g_settings.getBool("creative_mode") == true)
2451 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2455 // Strip command and create a stream
2456 std::string datastring((char*)&data[2], datasize-2);
2457 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2458 std::istringstream is(datastring, std::ios_base::binary);
2460 InventoryAction *a = InventoryAction::deSerialize(is);
2465 c.current_player = player;
2468 Handle craftresult specially if not in creative mode
2470 bool disable_action = false;
2471 if(a->getType() == IACTION_MOVE
2472 && g_settings.getBool("creative_mode") == false)
2474 IMoveAction *ma = (IMoveAction*)a;
2475 if(ma->to_inv == "current_player" &&
2476 ma->from_inv == "current_player")
2478 // Don't allow moving anything to craftresult
2479 if(ma->to_list == "craftresult")
2482 disable_action = true;
2484 // When something is removed from craftresult
2485 if(ma->from_list == "craftresult")
2487 disable_action = true;
2488 // Remove stuff from craft
2489 InventoryList *clist = player->inventory.getList("craft");
2492 u16 count = ma->count;
2495 clist->decrementMaterials(count);
2498 // Feed action to player inventory
2499 //a->apply(&player->inventory);
2503 // If something appeared in craftresult, throw it
2505 InventoryList *rlist = player->inventory.getList("craftresult");
2506 InventoryList *mlist = player->inventory.getList("main");
2507 if(rlist && mlist && rlist->getUsedSlots() == 1)
2509 InventoryItem *item1 = rlist->changeItem(0, NULL);
2510 mlist->addItem(item1);
2516 if(disable_action == false)
2518 // Feed action to player inventory
2519 //a->apply(&player->inventory);
2527 SendInventory(player->peer_id);
2532 dstream<<"TOSERVER_INVENTORY_ACTION: "
2533 <<"InventoryAction::deSerialize() returned NULL"
2537 else if(command == TOSERVER_CHAT_MESSAGE)
2545 std::string datastring((char*)&data[2], datasize-2);
2546 std::istringstream is(datastring, std::ios_base::binary);
2549 is.read((char*)buf, 2);
2550 u16 len = readU16(buf);
2552 std::wstring message;
2553 for(u16 i=0; i<len; i++)
2555 is.read((char*)buf, 2);
2556 message += (wchar_t)readU16(buf);
2559 // Get player name of this client
2560 std::wstring name = narrow_to_wide(player->getName());
2562 // Line to send to players
2564 // Whether to send to the player that sent the line
2565 bool send_to_sender = false;
2566 // Whether to send to other players
2567 bool send_to_others = false;
2570 std::wstring commandprefix = L"/#";
2571 if(message.substr(0, commandprefix.size()) == commandprefix)
2573 line += L"Server: ";
2575 message = message.substr(commandprefix.size());
2576 // Get player name as narrow string
2577 std::string name_s = player->getName();
2578 // Convert message to narrow string
2579 std::string message_s = wide_to_narrow(message);
2580 // Operator is the single name defined in config.
2581 std::string operator_name = g_settings.get("name");
2582 bool is_operator = (operator_name != "" &&
2583 wide_to_narrow(name) == operator_name);
2584 bool valid_command = false;
2585 if(message_s == "help")
2587 line += L"-!- Available commands: ";
2591 line += L"shutdown setting ";
2596 send_to_sender = true;
2597 valid_command = true;
2599 else if(message_s == "status")
2601 line = getStatusString();
2602 send_to_sender = true;
2603 valid_command = true;
2605 else if(is_operator)
2607 if(message_s == "shutdown")
2609 dstream<<DTIME<<" Server: Operator requested shutdown."
2611 m_shutdown_requested.set(true);
2613 line += L"*** Server shutting down (operator request)";
2614 send_to_sender = true;
2615 valid_command = true;
2617 else if(message_s.substr(0,8) == "setting ")
2619 std::string confline = message_s.substr(8);
2620 g_settings.parseConfigLine(confline);
2621 line += L"-!- Setting changed.";
2622 send_to_sender = true;
2623 valid_command = true;
2627 if(valid_command == false)
2629 line += L"-!- Invalid command: " + message;
2630 send_to_sender = true;
2641 send_to_others = true;
2646 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2649 Send the message to clients
2651 for(core::map<u16, RemoteClient*>::Iterator
2652 i = m_clients.getIterator();
2653 i.atEnd() == false; i++)
2655 // Get client and check that it is valid
2656 RemoteClient *client = i.getNode()->getValue();
2657 assert(client->peer_id == i.getNode()->getKey());
2658 if(client->serialization_version == SER_FMT_VER_INVALID)
2662 bool sender_selected = (peer_id == client->peer_id);
2663 if(sender_selected == true && send_to_sender == false)
2665 if(sender_selected == false && send_to_others == false)
2668 SendChatMessage(client->peer_id, line);
2674 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2675 "unknown command "<<command<<std::endl;
2679 catch(SendFailedException &e)
2681 derr_server<<"Server::ProcessData(): SendFailedException: "
2687 void Server::onMapEditEvent(MapEditEvent *event)
2689 dstream<<"Server::onMapEditEvent()"<<std::endl;
2690 if(m_ignore_map_edit_events)
2692 MapEditEvent *e = event->clone();
2693 m_unsent_map_edit_queue.push_back(e);
2696 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2698 if(id == "current_player")
2700 assert(c->current_player);
2701 return &(c->current_player->inventory);
2705 std::string id0 = fn.next(":");
2707 if(id0 == "nodemeta")
2710 p.X = stoi(fn.next(","));
2711 p.Y = stoi(fn.next(","));
2712 p.Z = stoi(fn.next(","));
2713 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2715 return meta->getInventory();
2716 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2717 <<"no metadata found"<<std::endl;
2721 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2724 void Server::inventoryModified(InventoryContext *c, std::string id)
2726 if(id == "current_player")
2728 assert(c->current_player);
2730 SendInventory(c->current_player->peer_id);
2735 std::string id0 = fn.next(":");
2737 if(id0 == "nodemeta")
2740 p.X = stoi(fn.next(","));
2741 p.Y = stoi(fn.next(","));
2742 p.Z = stoi(fn.next(","));
2743 assert(c->current_player);
2744 v3s16 blockpos = getNodeBlockPos(p);
2746 /*RemoteClient *client = getClient(c->current_player->peer_id);
2747 client->SetBlockNotSent(blockpos);*/
2749 for(core::map<u16, RemoteClient*>::Iterator
2750 i = m_clients.getIterator();
2751 i.atEnd()==false; i++)
2753 RemoteClient *client = i.getNode()->getValue();
2754 client->SetBlockNotSent(blockpos);
2760 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2763 core::list<PlayerInfo> Server::getPlayerInfo()
2765 DSTACK(__FUNCTION_NAME);
2766 JMutexAutoLock envlock(m_env_mutex);
2767 JMutexAutoLock conlock(m_con_mutex);
2769 core::list<PlayerInfo> list;
2771 core::list<Player*> players = m_env.getPlayers();
2773 core::list<Player*>::Iterator i;
2774 for(i = players.begin();
2775 i != players.end(); i++)
2779 Player *player = *i;
2782 con::Peer *peer = m_con.GetPeer(player->peer_id);
2783 // Copy info from peer to info struct
2785 info.address = peer->address;
2786 info.avg_rtt = peer->avg_rtt;
2788 catch(con::PeerNotFoundException &e)
2790 // Set dummy peer info
2792 info.address = Address(0,0,0,0,0);
2796 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2797 info.position = player->getPosition();
2799 list.push_back(info);
2806 void Server::peerAdded(con::Peer *peer)
2808 DSTACK(__FUNCTION_NAME);
2809 dout_server<<"Server::peerAdded(): peer->id="
2810 <<peer->id<<std::endl;
2813 c.type = PEER_ADDED;
2814 c.peer_id = peer->id;
2816 m_peer_change_queue.push_back(c);
2819 void Server::deletingPeer(con::Peer *peer, bool timeout)
2821 DSTACK(__FUNCTION_NAME);
2822 dout_server<<"Server::deletingPeer(): peer->id="
2823 <<peer->id<<", timeout="<<timeout<<std::endl;
2826 c.type = PEER_REMOVED;
2827 c.peer_id = peer->id;
2828 c.timeout = timeout;
2829 m_peer_change_queue.push_back(c);
2832 void Server::SendObjectData(float dtime)
2834 DSTACK(__FUNCTION_NAME);
2836 core::map<v3s16, bool> stepped_blocks;
2838 for(core::map<u16, RemoteClient*>::Iterator
2839 i = m_clients.getIterator();
2840 i.atEnd() == false; i++)
2842 u16 peer_id = i.getNode()->getKey();
2843 RemoteClient *client = i.getNode()->getValue();
2844 assert(client->peer_id == peer_id);
2846 if(client->serialization_version == SER_FMT_VER_INVALID)
2849 client->SendObjectData(this, dtime, stepped_blocks);
2853 void Server::SendPlayerInfos()
2855 DSTACK(__FUNCTION_NAME);
2857 //JMutexAutoLock envlock(m_env_mutex);
2859 // Get connected players
2860 core::list<Player*> players = m_env.getPlayers(true);
2862 u32 player_count = players.getSize();
2863 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2865 SharedBuffer<u8> data(datasize);
2866 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2869 core::list<Player*>::Iterator i;
2870 for(i = players.begin();
2871 i != players.end(); i++)
2873 Player *player = *i;
2875 /*dstream<<"Server sending player info for player with "
2876 "peer_id="<<player->peer_id<<std::endl;*/
2878 writeU16(&data[start], player->peer_id);
2879 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2880 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2881 start += 2+PLAYERNAME_SIZE;
2884 //JMutexAutoLock conlock(m_con_mutex);
2887 m_con.SendToAll(0, data, true);
2891 Craft checking system
2909 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2915 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2921 enum ItemSpecType type;
2922 // Only other one of these is used
2928 items: a pointer to an array of 9 pointers to items
2929 specs: a pointer to an array of 9 ItemSpecs
2931 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2933 u16 items_min_x = 100;
2934 u16 items_max_x = 100;
2935 u16 items_min_y = 100;
2936 u16 items_max_y = 100;
2937 for(u16 y=0; y<3; y++)
2938 for(u16 x=0; x<3; x++)
2940 if(items[y*3 + x] == NULL)
2942 if(items_min_x == 100 || x < items_min_x)
2944 if(items_min_y == 100 || y < items_min_y)
2946 if(items_max_x == 100 || x > items_max_x)
2948 if(items_max_y == 100 || y > items_max_y)
2951 // No items at all, just return false
2952 if(items_min_x == 100)
2955 u16 items_w = items_max_x - items_min_x + 1;
2956 u16 items_h = items_max_y - items_min_y + 1;
2958 u16 specs_min_x = 100;
2959 u16 specs_max_x = 100;
2960 u16 specs_min_y = 100;
2961 u16 specs_max_y = 100;
2962 for(u16 y=0; y<3; y++)
2963 for(u16 x=0; x<3; x++)
2965 if(specs[y*3 + x].type == ITEM_NONE)
2967 if(specs_min_x == 100 || x < specs_min_x)
2969 if(specs_min_y == 100 || y < specs_min_y)
2971 if(specs_max_x == 100 || x > specs_max_x)
2973 if(specs_max_y == 100 || y > specs_max_y)
2976 // No specs at all, just return false
2977 if(specs_min_x == 100)
2980 u16 specs_w = specs_max_x - specs_min_x + 1;
2981 u16 specs_h = specs_max_y - specs_min_y + 1;
2984 if(items_w != specs_w || items_h != specs_h)
2987 for(u16 y=0; y<specs_h; y++)
2988 for(u16 x=0; x<specs_w; x++)
2990 u16 items_x = items_min_x + x;
2991 u16 items_y = items_min_y + y;
2992 u16 specs_x = specs_min_x + x;
2993 u16 specs_y = specs_min_y + y;
2994 InventoryItem *item = items[items_y * 3 + items_x];
2995 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2997 if(spec.type == ITEM_NONE)
2999 // Has to be no item
3005 // There should be an item
3009 std::string itemname = item->getName();
3011 if(spec.type == ITEM_MATERIAL)
3013 if(itemname != "MaterialItem")
3015 MaterialItem *mitem = (MaterialItem*)item;
3016 if(mitem->getMaterial() != spec.num)
3019 else if(spec.type == ITEM_CRAFT)
3021 if(itemname != "CraftItem")
3023 CraftItem *mitem = (CraftItem*)item;
3024 if(mitem->getSubName() != spec.name)
3027 else if(spec.type == ITEM_TOOL)
3029 // Not supported yet
3032 else if(spec.type == ITEM_MBO)
3034 // Not supported yet
3039 // Not supported yet
3047 void Server::SendInventory(u16 peer_id)
3049 DSTACK(__FUNCTION_NAME);
3051 Player* player = m_env.getPlayer(peer_id);
3054 Calculate crafting stuff
3056 if(g_settings.getBool("creative_mode") == false)
3058 InventoryList *clist = player->inventory.getList("craft");
3059 InventoryList *rlist = player->inventory.getList("craftresult");
3062 rlist->clearItems();
3066 InventoryItem *items[9];
3067 for(u16 i=0; i<9; i++)
3069 items[i] = clist->getItem(i);
3078 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3079 if(checkItemCombination(items, specs))
3081 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3090 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3091 if(checkItemCombination(items, specs))
3093 rlist->addItem(new CraftItem("Stick", 4));
3102 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3103 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3104 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3105 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3106 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3107 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3108 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3109 if(checkItemCombination(items, specs))
3111 //rlist->addItem(new MapBlockObjectItem("Sign"));
3112 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3121 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3122 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3123 if(checkItemCombination(items, specs))
3125 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3134 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3135 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3136 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3137 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3138 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3139 if(checkItemCombination(items, specs))
3141 rlist->addItem(new ToolItem("WPick", 0));
3150 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3151 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3152 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3153 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3154 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3155 if(checkItemCombination(items, specs))
3157 rlist->addItem(new ToolItem("STPick", 0));
3166 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3167 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3168 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3169 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3170 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3171 if(checkItemCombination(items, specs))
3173 rlist->addItem(new ToolItem("MesePick", 0));
3182 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3183 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3184 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3185 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3186 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3187 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3188 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3189 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3190 if(checkItemCombination(items, specs))
3192 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3198 } // if creative_mode == false
3204 std::ostringstream os;
3205 //os.imbue(std::locale("C"));
3207 player->inventory.serialize(os);
3209 std::string s = os.str();
3211 SharedBuffer<u8> data(s.size()+2);
3212 writeU16(&data[0], TOCLIENT_INVENTORY);
3213 memcpy(&data[2], s.c_str(), s.size());
3216 m_con.Send(peer_id, 0, data, true);
3219 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3221 DSTACK(__FUNCTION_NAME);
3223 std::ostringstream os(std::ios_base::binary);
3227 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3228 os.write((char*)buf, 2);
3231 writeU16(buf, message.size());
3232 os.write((char*)buf, 2);
3235 for(u32 i=0; i<message.size(); i++)
3239 os.write((char*)buf, 2);
3243 std::string s = os.str();
3244 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3246 m_con.Send(peer_id, 0, data, true);
3249 void Server::BroadcastChatMessage(const std::wstring &message)
3251 for(core::map<u16, RemoteClient*>::Iterator
3252 i = m_clients.getIterator();
3253 i.atEnd() == false; i++)
3255 // Get client and check that it is valid
3256 RemoteClient *client = i.getNode()->getValue();
3257 assert(client->peer_id == i.getNode()->getKey());
3258 if(client->serialization_version == SER_FMT_VER_INVALID)
3261 SendChatMessage(client->peer_id, message);
3265 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3269 SharedBuffer<u8> reply(replysize);
3270 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3271 writeS16(&reply[2], p.X);
3272 writeS16(&reply[4], p.Y);
3273 writeS16(&reply[6], p.Z);
3275 for(core::map<u16, RemoteClient*>::Iterator
3276 i = m_clients.getIterator();
3277 i.atEnd() == false; i++)
3279 // Get client and check that it is valid
3280 RemoteClient *client = i.getNode()->getValue();
3281 assert(client->peer_id == i.getNode()->getKey());
3282 if(client->serialization_version == SER_FMT_VER_INVALID)
3285 // Don't send if it's the same one
3286 if(client->peer_id == ignore_id)
3290 m_con.Send(client->peer_id, 0, reply, true);
3294 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3296 for(core::map<u16, RemoteClient*>::Iterator
3297 i = m_clients.getIterator();
3298 i.atEnd() == false; i++)
3300 // Get client and check that it is valid
3301 RemoteClient *client = i.getNode()->getValue();
3302 assert(client->peer_id == i.getNode()->getKey());
3303 if(client->serialization_version == SER_FMT_VER_INVALID)
3306 // Don't send if it's the same one
3307 if(client->peer_id == ignore_id)
3311 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3312 SharedBuffer<u8> reply(replysize);
3313 writeU16(&reply[0], TOCLIENT_ADDNODE);
3314 writeS16(&reply[2], p.X);
3315 writeS16(&reply[4], p.Y);
3316 writeS16(&reply[6], p.Z);
3317 n.serialize(&reply[8], client->serialization_version);
3320 m_con.Send(client->peer_id, 0, reply, true);
3324 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3326 DSTACK(__FUNCTION_NAME);
3328 Create a packet with the block in the right format
3331 std::ostringstream os(std::ios_base::binary);
3332 block->serialize(os, ver);
3333 std::string s = os.str();
3334 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3336 u32 replysize = 8 + blockdata.getSize();
3337 SharedBuffer<u8> reply(replysize);
3338 v3s16 p = block->getPos();
3339 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3340 writeS16(&reply[2], p.X);
3341 writeS16(&reply[4], p.Y);
3342 writeS16(&reply[6], p.Z);
3343 memcpy(&reply[8], *blockdata, blockdata.getSize());
3345 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3346 <<": \tpacket size: "<<replysize<<std::endl;*/
3351 m_con.Send(peer_id, 1, reply, true);
3354 void Server::SendBlocks(float dtime)
3356 DSTACK(__FUNCTION_NAME);
3358 JMutexAutoLock envlock(m_env_mutex);
3359 JMutexAutoLock conlock(m_con_mutex);
3361 //TimeTaker timer("Server::SendBlocks");
3363 core::array<PrioritySortedBlockTransfer> queue;
3365 s32 total_sending = 0;
3367 for(core::map<u16, RemoteClient*>::Iterator
3368 i = m_clients.getIterator();
3369 i.atEnd() == false; i++)
3371 RemoteClient *client = i.getNode()->getValue();
3372 assert(client->peer_id == i.getNode()->getKey());
3374 total_sending += client->SendingCount();
3376 if(client->serialization_version == SER_FMT_VER_INVALID)
3379 client->GetNextBlocks(this, dtime, queue);
3383 // Lowest priority number comes first.
3384 // Lowest is most important.
3387 for(u32 i=0; i<queue.size(); i++)
3389 //TODO: Calculate limit dynamically
3390 if(total_sending >= g_settings.getS32
3391 ("max_simultaneous_block_sends_server_total"))
3394 PrioritySortedBlockTransfer q = queue[i];
3396 MapBlock *block = NULL;
3399 block = m_env.getMap().getBlockNoCreate(q.pos);
3401 catch(InvalidPositionException &e)
3406 RemoteClient *client = getClient(q.peer_id);
3408 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3410 client->SentBlock(q.pos);
3417 RemoteClient* Server::getClient(u16 peer_id)
3419 DSTACK(__FUNCTION_NAME);
3420 //JMutexAutoLock lock(m_con_mutex);
3421 core::map<u16, RemoteClient*>::Node *n;
3422 n = m_clients.find(peer_id);
3423 // A client should exist for all peers
3425 return n->getValue();
3428 std::wstring Server::getStatusString()
3430 std::wostringstream os(std::ios_base::binary);
3433 os<<L"uptime="<<m_uptime.get();
3434 // Information about clients
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 Player *player = m_env.getPlayer(client->peer_id);
3447 // Get name of player
3448 std::wstring name = L"unknown";
3450 name = narrow_to_wide(player->getName());
3451 // Add name to information string
3455 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3456 os<<" WARNING: Map saving is disabled."<<std::endl;
3461 void setCreativeInventory(Player *player)
3463 player->resetInventory();
3465 // Give some good picks
3467 InventoryItem *item = new ToolItem("STPick", 0);
3468 void* r = player->inventory.addItem("main", item);
3472 InventoryItem *item = new ToolItem("MesePick", 0);
3473 void* r = player->inventory.addItem("main", item);
3481 // CONTENT_IGNORE-terminated list
3482 u8 material_items[] = {
3490 CONTENT_WATERSOURCE,
3498 u8 *mip = material_items;
3499 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3501 if(*mip == CONTENT_IGNORE)
3504 InventoryItem *item = new MaterialItem(*mip, 1);
3505 player->inventory.addItem("main", item);
3511 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3514 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3515 player->inventory.addItem("main", item);
3518 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3520 // Skip some materials
3521 if(i == CONTENT_WATER || i == CONTENT_TORCH
3522 || i == CONTENT_COALSTONE)
3525 InventoryItem *item = new MaterialItem(i, 1);
3526 player->inventory.addItem("main", item);
3532 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3533 void* r = player->inventory.addItem("main", item);
3538 Player *Server::emergePlayer(const char *name, const char *password,
3542 Try to get an existing player
3544 Player *player = m_env.getPlayer(name);
3547 // If player is already connected, cancel
3548 if(player->peer_id != 0)
3550 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3555 player->peer_id = peer_id;
3557 // Reset inventory to creative if in creative mode
3558 if(g_settings.getBool("creative_mode"))
3560 setCreativeInventory(player);
3567 If player with the wanted peer_id already exists, cancel.
3569 if(m_env.getPlayer(peer_id) != NULL)
3571 dstream<<"emergePlayer(): Player with wrong name but same"
3572 " peer_id already exists"<<std::endl;
3580 player = new ServerRemotePlayer();
3581 //player->peer_id = c.peer_id;
3582 //player->peer_id = PEER_ID_INEXISTENT;
3583 player->peer_id = peer_id;
3584 player->updateName(name);
3590 dstream<<"Server: Finding spawn place for player \""
3591 <<player->getName()<<"\""<<std::endl;
3595 player->setPosition(intToFloat(v3s16(
3602 s16 groundheight = 0;
3604 // Try to find a good place a few times
3605 for(s32 i=0; i<1000; i++)
3608 // We're going to try to throw the player to this position
3609 nodepos = v2s16(-range + (myrand()%(range*2)),
3610 -range + (myrand()%(range*2)));
3611 v2s16 sectorpos = getNodeSectorPos(nodepos);
3612 // Get sector (NOTE: Don't get because it's slow)
3613 //m_env.getMap().emergeSector(sectorpos);
3614 // Get ground height at point (fallbacks to heightmap function)
3615 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3616 // Don't go underwater
3617 if(groundheight < WATER_LEVEL)
3619 //dstream<<"-> Underwater"<<std::endl;
3622 // Don't go to high places
3623 if(groundheight > WATER_LEVEL + 4)
3625 //dstream<<"-> Underwater"<<std::endl;
3630 // Doesn't work, generating blocks is a bit too complicated for doing here
3631 // Get block at point
3633 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3634 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3635 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3636 // Don't go inside ground
3638 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3639 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3640 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3641 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3642 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3643 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3645 dstream<<"-> Inside ground"<<std::endl;
3649 }catch(InvalidPositionException &e)
3651 dstream<<"-> Invalid position"<<std::endl;
3652 // Ignore invalid position
3657 // Found a good place
3658 dstream<<"Searched through "<<i<<" places."<<std::endl;
3663 // If no suitable place was not found, go above water at least.
3664 if(groundheight < WATER_LEVEL)
3665 groundheight = WATER_LEVEL;
3667 player->setPosition(intToFloat(v3s16(
3669 groundheight + 5, // Accomodate mud
3675 Add player to environment
3678 m_env.addPlayer(player);
3681 Add stuff to inventory
3684 if(g_settings.getBool("creative_mode"))
3686 setCreativeInventory(player);
3691 InventoryItem *item = new ToolItem("WPick", 32000);
3692 void* r = player->inventory.addItem("main", item);
3696 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3697 void* r = player->inventory.addItem("main", item);
3701 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3702 void* r = player->inventory.addItem("main", item);
3706 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3707 void* r = player->inventory.addItem("main", item);
3711 InventoryItem *item = new CraftItem("Stick", 4);
3712 void* r = player->inventory.addItem("main", item);
3716 InventoryItem *item = new ToolItem("WPick", 32000);
3717 void* r = player->inventory.addItem("main", item);
3721 InventoryItem *item = new ToolItem("STPick", 32000);
3722 void* r = player->inventory.addItem("main", item);
3725 /*// Give some lights
3727 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3728 bool r = player->inventory.addItem("main", item);
3732 for(u16 i=0; i<4; i++)
3734 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3735 bool r = player->inventory.addItem("main", item);
3738 /*// Give some other stuff
3740 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3741 bool r = player->inventory.addItem("main", item);
3748 } // create new player
3752 void Server::UpdateBlockWaterPressure(MapBlock *block,
3753 core::map<v3s16, MapBlock*> &modified_blocks)
3755 MapVoxelManipulator v(&m_env.getMap());
3756 v.m_disable_water_climb =
3757 g_settings.getBool("disable_water_climb");
3759 VoxelArea area(block->getPosRelative(),
3760 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3764 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3766 catch(ProcessingLimitException &e)
3768 dstream<<"Processing limit reached (1)"<<std::endl;
3771 v.blitBack(modified_blocks);
3775 void Server::handlePeerChange(PeerChange &c)
3777 JMutexAutoLock envlock(m_env_mutex);
3778 JMutexAutoLock conlock(m_con_mutex);
3780 if(c.type == PEER_ADDED)
3787 core::map<u16, RemoteClient*>::Node *n;
3788 n = m_clients.find(c.peer_id);
3789 // The client shouldn't already exist
3793 RemoteClient *client = new RemoteClient();
3794 client->peer_id = c.peer_id;
3795 m_clients.insert(client->peer_id, client);
3798 else if(c.type == PEER_REMOVED)
3805 core::map<u16, RemoteClient*>::Node *n;
3806 n = m_clients.find(c.peer_id);
3807 // The client should exist
3810 // Collect information about leaving in chat
3811 std::wstring message;
3813 std::wstring name = L"unknown";
3814 Player *player = m_env.getPlayer(c.peer_id);
3816 name = narrow_to_wide(player->getName());
3820 message += L" left game";
3822 message += L" (timed out)";
3827 m_env.removePlayer(c.peer_id);
3830 // Set player client disconnected
3832 Player *player = m_env.getPlayer(c.peer_id);
3834 player->peer_id = 0;
3838 delete m_clients[c.peer_id];
3839 m_clients.remove(c.peer_id);
3841 // Send player info to all remaining clients
3844 // Send leave chat message to all remaining clients
3845 BroadcastChatMessage(message);
3854 void Server::handlePeerChanges()
3856 while(m_peer_change_queue.size() > 0)
3858 PeerChange c = m_peer_change_queue.pop_front();
3860 dout_server<<"Server: Handling peer change: "
3861 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3864 handlePeerChange(c);
3868 void dedicated_server_loop(Server &server, bool &kill)
3870 DSTACK(__FUNCTION_NAME);
3872 std::cout<<DTIME<<std::endl;
3873 std::cout<<"========================"<<std::endl;
3874 std::cout<<"Running dedicated server"<<std::endl;
3875 std::cout<<"========================"<<std::endl;
3876 std::cout<<std::endl;
3880 // This is kind of a hack but can be done like this
3881 // because server.step() is very light
3885 if(server.getShutdownRequested() || kill)
3887 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3891 static int counter = 0;
3897 core::list<PlayerInfo> list = server.getPlayerInfo();
3898 core::list<PlayerInfo>::Iterator i;
3899 static u32 sum_old = 0;
3900 u32 sum = PIChecksum(list);
3903 std::cout<<DTIME<<"Player info:"<<std::endl;
3904 for(i=list.begin(); i!=list.end(); i++)
3906 i->PrintLine(&std::cout);