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.
2104 - If other player is close, send REMOVENODE
2105 - Otherwise set blocks not sent
2107 core::list<u16> far_players;
2108 sendRemoveNode(p_under, peer_id, &far_players, 100);
2111 Update and send inventory
2114 if(g_settings.getBool("creative_mode") == false)
2119 InventoryList *mlist = player->inventory.getList("main");
2122 InventoryItem *item = mlist->getItem(item_i);
2123 if(item && (std::string)item->getName() == "ToolItem")
2125 ToolItem *titem = (ToolItem*)item;
2126 std::string toolname = titem->getToolName();
2128 // Get digging properties for material and tool
2129 DiggingProperties prop =
2130 getDiggingProperties(material, toolname);
2132 if(prop.diggable == false)
2134 derr_server<<"Server: WARNING: Player digged"
2135 <<" with impossible material + tool"
2136 <<" combination"<<std::endl;
2139 bool weared_out = titem->addWear(prop.wear);
2143 mlist->deleteItem(item_i);
2149 Add dug item to inventory
2152 InventoryItem *item = NULL;
2154 if(mineral != MINERAL_NONE)
2155 item = getDiggedMineralItem(mineral);
2160 std::string &dug_s = content_features(material).dug_item;
2163 std::istringstream is(dug_s, std::ios::binary);
2164 item = InventoryItem::deSerialize(is);
2170 // Add a item to inventory
2171 player->inventory.addItem("main", item);
2174 SendInventory(player->peer_id);
2180 (this takes some time so it is done after the quick stuff)
2182 m_ignore_map_edit_events = true;
2183 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2184 m_ignore_map_edit_events = false;
2187 Set blocks not sent to far players
2189 for(core::list<u16>::Iterator
2190 i = far_players.begin();
2191 i != far_players.end(); i++)
2194 RemoteClient *client = getClient(peer_id);
2197 client->SetBlocksNotSent(modified_blocks);
2204 else if(action == 1)
2207 InventoryList *ilist = player->inventory.getList("main");
2212 InventoryItem *item = ilist->getItem(item_i);
2214 // If there is no item, it is not possible to add it anywhere
2219 Handle material items
2221 if(std::string("MaterialItem") == item->getName())
2224 // Don't add a node if this is not a free space
2225 MapNode n2 = m_env.getMap().getNode(p_over);
2226 if(content_buildable_to(n2.d) == false)
2228 // Client probably has wrong data.
2229 // Set block not sent, so that client will get
2231 dstream<<"Client "<<peer_id<<" tried to place"
2232 <<" node in invalid position; setting"
2233 <<" MapBlock not sent."<<std::endl;
2234 RemoteClient *client = getClient(peer_id);
2235 v3s16 blockpos = getNodeBlockPos(p_over);
2236 client->SetBlockNotSent(blockpos);
2240 catch(InvalidPositionException &e)
2242 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2243 <<" Adding block to emerge queue."
2245 m_emerge_queue.addBlock(peer_id,
2246 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2250 // Reset build time counter
2251 getClient(peer->id)->m_time_from_building = 0.0;
2254 MaterialItem *mitem = (MaterialItem*)item;
2256 n.d = mitem->getMaterial();
2257 if(content_features(n.d).wall_mounted)
2258 n.dir = packDir(p_under - p_over);
2263 core::list<u16> far_players;
2264 sendAddNode(p_over, n, 0, &far_players, 100);
2269 InventoryList *ilist = player->inventory.getList("main");
2270 if(g_settings.getBool("creative_mode") == false && ilist)
2272 // Remove from inventory and send inventory
2273 if(mitem->getCount() == 1)
2274 ilist->deleteItem(item_i);
2278 SendInventory(peer_id);
2284 This takes some time so it is done after the quick stuff
2286 core::map<v3s16, MapBlock*> modified_blocks;
2287 m_ignore_map_edit_events = true;
2288 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2289 m_ignore_map_edit_events = false;
2292 Set blocks not sent to far players
2294 for(core::list<u16>::Iterator
2295 i = far_players.begin();
2296 i != far_players.end(); i++)
2299 RemoteClient *client = getClient(peer_id);
2302 client->SetBlocksNotSent(modified_blocks);
2306 Calculate special events
2309 /*if(n.d == CONTENT_MESE)
2312 for(s16 z=-1; z<=1; z++)
2313 for(s16 y=-1; y<=1; y++)
2314 for(s16 x=-1; x<=1; x++)
2325 v3s16 blockpos = getNodeBlockPos(p_over);
2327 MapBlock *block = NULL;
2330 block = m_env.getMap().getBlockNoCreate(blockpos);
2332 catch(InvalidPositionException &e)
2334 derr_server<<"Error while placing object: "
2335 "block not found"<<std::endl;
2339 v3s16 block_pos_i_on_map = block->getPosRelative();
2340 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2342 v3f pos = intToFloat(p_over, BS);
2343 pos -= block_pos_f_on_map;
2345 /*dout_server<<"pos="
2346 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2349 MapBlockObject *obj = NULL;
2352 Handle block object items
2354 if(std::string("MBOItem") == item->getName())
2356 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2358 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2359 "inventorystring=\""
2360 <<oitem->getInventoryString()
2361 <<"\""<<std::endl;*/
2363 obj = oitem->createObject
2364 (pos, player->getYaw(), player->getPitch());
2371 dout_server<<"Placing a miscellaneous item on map"
2374 Create an ItemObject that contains the item.
2376 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2377 std::ostringstream os(std::ios_base::binary);
2378 item->serialize(os);
2379 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2380 iobj->setItemString(os.str());
2386 derr_server<<"WARNING: item resulted in NULL object, "
2387 <<"not placing onto map"
2392 block->addObject(obj);
2394 dout_server<<"Placed object"<<std::endl;
2396 InventoryList *ilist = player->inventory.getList("main");
2397 if(g_settings.getBool("creative_mode") == false && ilist)
2399 // Remove from inventory and send inventory
2400 ilist->deleteItem(item_i);
2402 SendInventory(peer_id);
2410 Catch invalid actions
2414 derr_server<<"WARNING: Server: Invalid action "
2415 <<action<<std::endl;
2419 else if(command == TOSERVER_RELEASE)
2428 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2431 else if(command == TOSERVER_SIGNTEXT)
2440 std::string datastring((char*)&data[2], datasize-2);
2441 std::istringstream is(datastring, std::ios_base::binary);
2444 is.read((char*)buf, 6);
2445 v3s16 blockpos = readV3S16(buf);
2446 is.read((char*)buf, 2);
2447 s16 id = readS16(buf);
2448 is.read((char*)buf, 2);
2449 u16 textlen = readU16(buf);
2451 for(u16 i=0; i<textlen; i++)
2453 is.read((char*)buf, 1);
2454 text += (char)buf[0];
2457 MapBlock *block = NULL;
2460 block = m_env.getMap().getBlockNoCreate(blockpos);
2462 catch(InvalidPositionException &e)
2464 derr_server<<"Error while setting sign text: "
2465 "block not found"<<std::endl;
2469 MapBlockObject *obj = block->getObject(id);
2472 derr_server<<"Error while setting sign text: "
2473 "object not found"<<std::endl;
2477 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2479 derr_server<<"Error while setting sign text: "
2480 "object is not a sign"<<std::endl;
2484 ((SignObject*)obj)->setText(text);
2486 obj->getBlock()->setChangedFlag();
2488 else if(command == TOSERVER_SIGNNODETEXT)
2496 std::string datastring((char*)&data[2], datasize-2);
2497 std::istringstream is(datastring, std::ios_base::binary);
2500 is.read((char*)buf, 6);
2501 v3s16 p = readV3S16(buf);
2502 is.read((char*)buf, 2);
2503 u16 textlen = readU16(buf);
2505 for(u16 i=0; i<textlen; i++)
2507 is.read((char*)buf, 1);
2508 text += (char)buf[0];
2511 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2514 if(meta->typeId() != CONTENT_SIGN_WALL)
2516 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2517 signmeta->setText(text);
2519 v3s16 blockpos = getNodeBlockPos(p);
2520 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2523 block->setChangedFlag();
2526 for(core::map<u16, RemoteClient*>::Iterator
2527 i = m_clients.getIterator();
2528 i.atEnd()==false; i++)
2530 RemoteClient *client = i.getNode()->getValue();
2531 client->SetBlockNotSent(blockpos);
2534 else if(command == TOSERVER_INVENTORY_ACTION)
2536 /*// Ignore inventory changes if in creative mode
2537 if(g_settings.getBool("creative_mode") == true)
2539 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2543 // Strip command and create a stream
2544 std::string datastring((char*)&data[2], datasize-2);
2545 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2546 std::istringstream is(datastring, std::ios_base::binary);
2548 InventoryAction *a = InventoryAction::deSerialize(is);
2553 c.current_player = player;
2556 Handle craftresult specially if not in creative mode
2558 bool disable_action = false;
2559 if(a->getType() == IACTION_MOVE
2560 && g_settings.getBool("creative_mode") == false)
2562 IMoveAction *ma = (IMoveAction*)a;
2563 if(ma->to_inv == "current_player" &&
2564 ma->from_inv == "current_player")
2566 // Don't allow moving anything to craftresult
2567 if(ma->to_list == "craftresult")
2570 disable_action = true;
2572 // When something is removed from craftresult
2573 if(ma->from_list == "craftresult")
2575 disable_action = true;
2576 // Remove stuff from craft
2577 InventoryList *clist = player->inventory.getList("craft");
2580 u16 count = ma->count;
2583 clist->decrementMaterials(count);
2586 // Feed action to player inventory
2587 //a->apply(&player->inventory);
2591 // If something appeared in craftresult, throw it
2593 InventoryList *rlist = player->inventory.getList("craftresult");
2594 InventoryList *mlist = player->inventory.getList("main");
2595 if(rlist && mlist && rlist->getUsedSlots() == 1)
2597 InventoryItem *item1 = rlist->changeItem(0, NULL);
2598 mlist->addItem(item1);
2604 if(disable_action == false)
2606 // Feed action to player inventory
2607 //a->apply(&player->inventory);
2615 SendInventory(player->peer_id);
2620 dstream<<"TOSERVER_INVENTORY_ACTION: "
2621 <<"InventoryAction::deSerialize() returned NULL"
2625 else if(command == TOSERVER_CHAT_MESSAGE)
2633 std::string datastring((char*)&data[2], datasize-2);
2634 std::istringstream is(datastring, std::ios_base::binary);
2637 is.read((char*)buf, 2);
2638 u16 len = readU16(buf);
2640 std::wstring message;
2641 for(u16 i=0; i<len; i++)
2643 is.read((char*)buf, 2);
2644 message += (wchar_t)readU16(buf);
2647 // Get player name of this client
2648 std::wstring name = narrow_to_wide(player->getName());
2650 // Line to send to players
2652 // Whether to send to the player that sent the line
2653 bool send_to_sender = false;
2654 // Whether to send to other players
2655 bool send_to_others = false;
2658 std::wstring commandprefix = L"/#";
2659 if(message.substr(0, commandprefix.size()) == commandprefix)
2661 line += L"Server: ";
2663 message = message.substr(commandprefix.size());
2664 // Get player name as narrow string
2665 std::string name_s = player->getName();
2666 // Convert message to narrow string
2667 std::string message_s = wide_to_narrow(message);
2668 // Operator is the single name defined in config.
2669 std::string operator_name = g_settings.get("name");
2670 bool is_operator = (operator_name != "" &&
2671 wide_to_narrow(name) == operator_name);
2672 bool valid_command = false;
2673 if(message_s == "help")
2675 line += L"-!- Available commands: ";
2679 line += L"shutdown setting ";
2684 send_to_sender = true;
2685 valid_command = true;
2687 else if(message_s == "status")
2689 line = getStatusString();
2690 send_to_sender = true;
2691 valid_command = true;
2693 else if(is_operator)
2695 if(message_s == "shutdown")
2697 dstream<<DTIME<<" Server: Operator requested shutdown."
2699 m_shutdown_requested.set(true);
2701 line += L"*** Server shutting down (operator request)";
2702 send_to_sender = true;
2703 valid_command = true;
2705 else if(message_s.substr(0,8) == "setting ")
2707 std::string confline = message_s.substr(8);
2708 g_settings.parseConfigLine(confline);
2709 line += L"-!- Setting changed.";
2710 send_to_sender = true;
2711 valid_command = true;
2715 if(valid_command == false)
2717 line += L"-!- Invalid command: " + message;
2718 send_to_sender = true;
2729 send_to_others = true;
2734 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2737 Send the message to clients
2739 for(core::map<u16, RemoteClient*>::Iterator
2740 i = m_clients.getIterator();
2741 i.atEnd() == false; i++)
2743 // Get client and check that it is valid
2744 RemoteClient *client = i.getNode()->getValue();
2745 assert(client->peer_id == i.getNode()->getKey());
2746 if(client->serialization_version == SER_FMT_VER_INVALID)
2750 bool sender_selected = (peer_id == client->peer_id);
2751 if(sender_selected == true && send_to_sender == false)
2753 if(sender_selected == false && send_to_others == false)
2756 SendChatMessage(client->peer_id, line);
2762 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2763 "unknown command "<<command<<std::endl;
2767 catch(SendFailedException &e)
2769 derr_server<<"Server::ProcessData(): SendFailedException: "
2775 void Server::onMapEditEvent(MapEditEvent *event)
2777 dstream<<"Server::onMapEditEvent()"<<std::endl;
2778 if(m_ignore_map_edit_events)
2780 MapEditEvent *e = event->clone();
2781 m_unsent_map_edit_queue.push_back(e);
2784 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2786 if(id == "current_player")
2788 assert(c->current_player);
2789 return &(c->current_player->inventory);
2793 std::string id0 = fn.next(":");
2795 if(id0 == "nodemeta")
2798 p.X = stoi(fn.next(","));
2799 p.Y = stoi(fn.next(","));
2800 p.Z = stoi(fn.next(","));
2801 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2803 return meta->getInventory();
2804 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2805 <<"no metadata found"<<std::endl;
2809 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2812 void Server::inventoryModified(InventoryContext *c, std::string id)
2814 if(id == "current_player")
2816 assert(c->current_player);
2818 SendInventory(c->current_player->peer_id);
2823 std::string id0 = fn.next(":");
2825 if(id0 == "nodemeta")
2828 p.X = stoi(fn.next(","));
2829 p.Y = stoi(fn.next(","));
2830 p.Z = stoi(fn.next(","));
2831 v3s16 blockpos = getNodeBlockPos(p);
2833 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2835 meta->inventoryModified();
2837 for(core::map<u16, RemoteClient*>::Iterator
2838 i = m_clients.getIterator();
2839 i.atEnd()==false; i++)
2841 RemoteClient *client = i.getNode()->getValue();
2842 client->SetBlockNotSent(blockpos);
2848 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2851 core::list<PlayerInfo> Server::getPlayerInfo()
2853 DSTACK(__FUNCTION_NAME);
2854 JMutexAutoLock envlock(m_env_mutex);
2855 JMutexAutoLock conlock(m_con_mutex);
2857 core::list<PlayerInfo> list;
2859 core::list<Player*> players = m_env.getPlayers();
2861 core::list<Player*>::Iterator i;
2862 for(i = players.begin();
2863 i != players.end(); i++)
2867 Player *player = *i;
2870 con::Peer *peer = m_con.GetPeer(player->peer_id);
2871 // Copy info from peer to info struct
2873 info.address = peer->address;
2874 info.avg_rtt = peer->avg_rtt;
2876 catch(con::PeerNotFoundException &e)
2878 // Set dummy peer info
2880 info.address = Address(0,0,0,0,0);
2884 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2885 info.position = player->getPosition();
2887 list.push_back(info);
2894 void Server::peerAdded(con::Peer *peer)
2896 DSTACK(__FUNCTION_NAME);
2897 dout_server<<"Server::peerAdded(): peer->id="
2898 <<peer->id<<std::endl;
2901 c.type = PEER_ADDED;
2902 c.peer_id = peer->id;
2904 m_peer_change_queue.push_back(c);
2907 void Server::deletingPeer(con::Peer *peer, bool timeout)
2909 DSTACK(__FUNCTION_NAME);
2910 dout_server<<"Server::deletingPeer(): peer->id="
2911 <<peer->id<<", timeout="<<timeout<<std::endl;
2914 c.type = PEER_REMOVED;
2915 c.peer_id = peer->id;
2916 c.timeout = timeout;
2917 m_peer_change_queue.push_back(c);
2920 void Server::SendObjectData(float dtime)
2922 DSTACK(__FUNCTION_NAME);
2924 core::map<v3s16, bool> stepped_blocks;
2926 for(core::map<u16, RemoteClient*>::Iterator
2927 i = m_clients.getIterator();
2928 i.atEnd() == false; i++)
2930 u16 peer_id = i.getNode()->getKey();
2931 RemoteClient *client = i.getNode()->getValue();
2932 assert(client->peer_id == peer_id);
2934 if(client->serialization_version == SER_FMT_VER_INVALID)
2937 client->SendObjectData(this, dtime, stepped_blocks);
2941 void Server::SendPlayerInfos()
2943 DSTACK(__FUNCTION_NAME);
2945 //JMutexAutoLock envlock(m_env_mutex);
2947 // Get connected players
2948 core::list<Player*> players = m_env.getPlayers(true);
2950 u32 player_count = players.getSize();
2951 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2953 SharedBuffer<u8> data(datasize);
2954 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2957 core::list<Player*>::Iterator i;
2958 for(i = players.begin();
2959 i != players.end(); i++)
2961 Player *player = *i;
2963 /*dstream<<"Server sending player info for player with "
2964 "peer_id="<<player->peer_id<<std::endl;*/
2966 writeU16(&data[start], player->peer_id);
2967 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2968 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2969 start += 2+PLAYERNAME_SIZE;
2972 //JMutexAutoLock conlock(m_con_mutex);
2975 m_con.SendToAll(0, data, true);
2978 void Server::SendInventory(u16 peer_id)
2980 DSTACK(__FUNCTION_NAME);
2982 Player* player = m_env.getPlayer(peer_id);
2985 Calculate crafting stuff
2987 if(g_settings.getBool("creative_mode") == false)
2989 InventoryList *clist = player->inventory.getList("craft");
2990 InventoryList *rlist = player->inventory.getList("craftresult");
2993 rlist->clearItems();
2997 InventoryItem *items[9];
2998 for(u16 i=0; i<9; i++)
3000 items[i] = clist->getItem(i);
3009 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3010 if(checkItemCombination(items, specs))
3012 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3021 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3022 if(checkItemCombination(items, specs))
3024 rlist->addItem(new CraftItem("Stick", 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[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3037 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3038 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3039 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3040 if(checkItemCombination(items, specs))
3042 //rlist->addItem(new MapBlockObjectItem("Sign"));
3043 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3052 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3053 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3054 if(checkItemCombination(items, specs))
3056 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3065 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3066 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3067 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3068 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3069 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3070 if(checkItemCombination(items, specs))
3072 rlist->addItem(new ToolItem("WPick", 0));
3081 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3082 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3083 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3084 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3085 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3086 if(checkItemCombination(items, specs))
3088 rlist->addItem(new ToolItem("STPick", 0));
3097 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3098 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3099 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3100 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3101 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3102 if(checkItemCombination(items, specs))
3104 rlist->addItem(new ToolItem("SteelPick", 0));
3113 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3114 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3115 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3116 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3117 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3118 if(checkItemCombination(items, specs))
3120 rlist->addItem(new ToolItem("MesePick", 0));
3129 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3130 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3131 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3132 if(checkItemCombination(items, specs))
3134 rlist->addItem(new ToolItem("WShovel", 0));
3143 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3144 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3145 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3146 if(checkItemCombination(items, specs))
3148 rlist->addItem(new ToolItem("STShovel", 0));
3157 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3158 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3159 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3160 if(checkItemCombination(items, specs))
3162 rlist->addItem(new ToolItem("SteelShovel", 0));
3171 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3172 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3173 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3174 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3175 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3176 if(checkItemCombination(items, specs))
3178 rlist->addItem(new ToolItem("WAxe", 0));
3187 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3188 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3189 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3190 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3191 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3192 if(checkItemCombination(items, specs))
3194 rlist->addItem(new ToolItem("STAxe", 0));
3203 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3204 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3205 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3206 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3207 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3208 if(checkItemCombination(items, specs))
3210 rlist->addItem(new ToolItem("SteelAxe", 0));
3219 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3220 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3221 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3222 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3223 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3224 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3225 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3226 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3227 if(checkItemCombination(items, specs))
3229 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3238 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3239 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3240 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3241 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3242 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3243 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3244 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3245 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3246 if(checkItemCombination(items, specs))
3248 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3257 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3258 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3259 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3260 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3261 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3262 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3263 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3264 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3265 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3266 if(checkItemCombination(items, specs))
3268 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3274 } // if creative_mode == false
3280 std::ostringstream os;
3281 //os.imbue(std::locale("C"));
3283 player->inventory.serialize(os);
3285 std::string s = os.str();
3287 SharedBuffer<u8> data(s.size()+2);
3288 writeU16(&data[0], TOCLIENT_INVENTORY);
3289 memcpy(&data[2], s.c_str(), s.size());
3292 m_con.Send(peer_id, 0, data, true);
3295 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3297 DSTACK(__FUNCTION_NAME);
3299 std::ostringstream os(std::ios_base::binary);
3303 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3304 os.write((char*)buf, 2);
3307 writeU16(buf, message.size());
3308 os.write((char*)buf, 2);
3311 for(u32 i=0; i<message.size(); i++)
3315 os.write((char*)buf, 2);
3319 std::string s = os.str();
3320 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3322 m_con.Send(peer_id, 0, data, true);
3325 void Server::BroadcastChatMessage(const std::wstring &message)
3327 for(core::map<u16, RemoteClient*>::Iterator
3328 i = m_clients.getIterator();
3329 i.atEnd() == false; i++)
3331 // Get client and check that it is valid
3332 RemoteClient *client = i.getNode()->getValue();
3333 assert(client->peer_id == i.getNode()->getKey());
3334 if(client->serialization_version == SER_FMT_VER_INVALID)
3337 SendChatMessage(client->peer_id, message);
3341 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3342 core::list<u16> *far_players, float far_d_nodes)
3344 float maxd = far_d_nodes*BS;
3345 v3f p_f = intToFloat(p, BS);
3349 SharedBuffer<u8> reply(replysize);
3350 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3351 writeS16(&reply[2], p.X);
3352 writeS16(&reply[4], p.Y);
3353 writeS16(&reply[6], p.Z);
3355 for(core::map<u16, RemoteClient*>::Iterator
3356 i = m_clients.getIterator();
3357 i.atEnd() == false; i++)
3359 // Get client and check that it is valid
3360 RemoteClient *client = i.getNode()->getValue();
3361 assert(client->peer_id == i.getNode()->getKey());
3362 if(client->serialization_version == SER_FMT_VER_INVALID)
3365 // Don't send if it's the same one
3366 if(client->peer_id == ignore_id)
3372 Player *player = m_env.getPlayer(client->peer_id);
3375 // If player is far away, only set modified blocks not sent
3376 v3f player_pos = player->getPosition();
3377 if(player_pos.getDistanceFrom(p_f) > maxd)
3379 far_players->push_back(client->peer_id);
3386 m_con.Send(client->peer_id, 0, reply, true);
3390 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3391 core::list<u16> *far_players, float far_d_nodes)
3393 float maxd = far_d_nodes*BS;
3394 v3f p_f = intToFloat(p, BS);
3396 for(core::map<u16, RemoteClient*>::Iterator
3397 i = m_clients.getIterator();
3398 i.atEnd() == false; i++)
3400 // Get client and check that it is valid
3401 RemoteClient *client = i.getNode()->getValue();
3402 assert(client->peer_id == i.getNode()->getKey());
3403 if(client->serialization_version == SER_FMT_VER_INVALID)
3406 // Don't send if it's the same one
3407 if(client->peer_id == ignore_id)
3413 Player *player = m_env.getPlayer(client->peer_id);
3416 // If player is far away, only set modified blocks not sent
3417 v3f player_pos = player->getPosition();
3418 if(player_pos.getDistanceFrom(p_f) > maxd)
3420 far_players->push_back(client->peer_id);
3427 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3428 SharedBuffer<u8> reply(replysize);
3429 writeU16(&reply[0], TOCLIENT_ADDNODE);
3430 writeS16(&reply[2], p.X);
3431 writeS16(&reply[4], p.Y);
3432 writeS16(&reply[6], p.Z);
3433 n.serialize(&reply[8], client->serialization_version);
3436 m_con.Send(client->peer_id, 0, reply, true);
3440 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3442 DSTACK(__FUNCTION_NAME);
3444 Create a packet with the block in the right format
3447 std::ostringstream os(std::ios_base::binary);
3448 block->serialize(os, ver);
3449 std::string s = os.str();
3450 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3452 u32 replysize = 8 + blockdata.getSize();
3453 SharedBuffer<u8> reply(replysize);
3454 v3s16 p = block->getPos();
3455 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3456 writeS16(&reply[2], p.X);
3457 writeS16(&reply[4], p.Y);
3458 writeS16(&reply[6], p.Z);
3459 memcpy(&reply[8], *blockdata, blockdata.getSize());
3461 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3462 <<": \tpacket size: "<<replysize<<std::endl;*/
3467 m_con.Send(peer_id, 1, reply, true);
3470 void Server::SendBlocks(float dtime)
3472 DSTACK(__FUNCTION_NAME);
3474 JMutexAutoLock envlock(m_env_mutex);
3475 JMutexAutoLock conlock(m_con_mutex);
3477 //TimeTaker timer("Server::SendBlocks");
3479 core::array<PrioritySortedBlockTransfer> queue;
3481 s32 total_sending = 0;
3483 for(core::map<u16, RemoteClient*>::Iterator
3484 i = m_clients.getIterator();
3485 i.atEnd() == false; i++)
3487 RemoteClient *client = i.getNode()->getValue();
3488 assert(client->peer_id == i.getNode()->getKey());
3490 total_sending += client->SendingCount();
3492 if(client->serialization_version == SER_FMT_VER_INVALID)
3495 client->GetNextBlocks(this, dtime, queue);
3499 // Lowest priority number comes first.
3500 // Lowest is most important.
3503 for(u32 i=0; i<queue.size(); i++)
3505 //TODO: Calculate limit dynamically
3506 if(total_sending >= g_settings.getS32
3507 ("max_simultaneous_block_sends_server_total"))
3510 PrioritySortedBlockTransfer q = queue[i];
3512 MapBlock *block = NULL;
3515 block = m_env.getMap().getBlockNoCreate(q.pos);
3517 catch(InvalidPositionException &e)
3522 RemoteClient *client = getClient(q.peer_id);
3524 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3526 client->SentBlock(q.pos);
3533 RemoteClient* Server::getClient(u16 peer_id)
3535 DSTACK(__FUNCTION_NAME);
3536 //JMutexAutoLock lock(m_con_mutex);
3537 core::map<u16, RemoteClient*>::Node *n;
3538 n = m_clients.find(peer_id);
3539 // A client should exist for all peers
3541 return n->getValue();
3544 std::wstring Server::getStatusString()
3546 std::wostringstream os(std::ios_base::binary);
3549 os<<L"uptime="<<m_uptime.get();
3550 // Information about clients
3552 for(core::map<u16, RemoteClient*>::Iterator
3553 i = m_clients.getIterator();
3554 i.atEnd() == false; i++)
3556 // Get client and check that it is valid
3557 RemoteClient *client = i.getNode()->getValue();
3558 assert(client->peer_id == i.getNode()->getKey());
3559 if(client->serialization_version == SER_FMT_VER_INVALID)
3562 Player *player = m_env.getPlayer(client->peer_id);
3563 // Get name of player
3564 std::wstring name = L"unknown";
3566 name = narrow_to_wide(player->getName());
3567 // Add name to information string
3571 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3572 os<<" WARNING: Map saving is disabled."<<std::endl;
3577 void setCreativeInventory(Player *player)
3579 player->resetInventory();
3581 // Give some good picks
3583 InventoryItem *item = new ToolItem("STPick", 0);
3584 void* r = player->inventory.addItem("main", item);
3588 InventoryItem *item = new ToolItem("MesePick", 0);
3589 void* r = player->inventory.addItem("main", item);
3597 // CONTENT_IGNORE-terminated list
3598 u8 material_items[] = {
3607 CONTENT_WATERSOURCE,
3615 u8 *mip = material_items;
3616 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3618 if(*mip == CONTENT_IGNORE)
3621 InventoryItem *item = new MaterialItem(*mip, 1);
3622 player->inventory.addItem("main", item);
3628 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3631 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3632 player->inventory.addItem("main", item);
3635 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3637 // Skip some materials
3638 if(i == CONTENT_WATER || i == CONTENT_TORCH
3639 || i == CONTENT_COALSTONE)
3642 InventoryItem *item = new MaterialItem(i, 1);
3643 player->inventory.addItem("main", item);
3649 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3650 void* r = player->inventory.addItem("main", item);
3655 Player *Server::emergePlayer(const char *name, const char *password,
3659 Try to get an existing player
3661 Player *player = m_env.getPlayer(name);
3664 // If player is already connected, cancel
3665 if(player->peer_id != 0)
3667 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3672 player->peer_id = peer_id;
3674 // Reset inventory to creative if in creative mode
3675 if(g_settings.getBool("creative_mode"))
3677 setCreativeInventory(player);
3684 If player with the wanted peer_id already exists, cancel.
3686 if(m_env.getPlayer(peer_id) != NULL)
3688 dstream<<"emergePlayer(): Player with wrong name but same"
3689 " peer_id already exists"<<std::endl;
3697 player = new ServerRemotePlayer();
3698 //player->peer_id = c.peer_id;
3699 //player->peer_id = PEER_ID_INEXISTENT;
3700 player->peer_id = peer_id;
3701 player->updateName(name);
3707 dstream<<"Server: Finding spawn place for player \""
3708 <<player->getName()<<"\""<<std::endl;
3712 player->setPosition(intToFloat(v3s16(
3719 s16 groundheight = 0;
3721 // Try to find a good place a few times
3722 for(s32 i=0; i<1000; i++)
3725 // We're going to try to throw the player to this position
3726 nodepos = v2s16(-range + (myrand()%(range*2)),
3727 -range + (myrand()%(range*2)));
3728 v2s16 sectorpos = getNodeSectorPos(nodepos);
3729 // Get sector (NOTE: Don't get because it's slow)
3730 //m_env.getMap().emergeSector(sectorpos);
3731 // Get ground height at point (fallbacks to heightmap function)
3732 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3733 // Don't go underwater
3734 if(groundheight < WATER_LEVEL)
3736 //dstream<<"-> Underwater"<<std::endl;
3739 // Don't go to high places
3740 if(groundheight > WATER_LEVEL + 4)
3742 //dstream<<"-> Underwater"<<std::endl;
3747 // Doesn't work, generating blocks is a bit too complicated for doing here
3748 // Get block at point
3750 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3751 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3752 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3753 // Don't go inside ground
3755 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3756 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3757 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3758 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3759 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3760 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3762 dstream<<"-> Inside ground"<<std::endl;
3766 }catch(InvalidPositionException &e)
3768 dstream<<"-> Invalid position"<<std::endl;
3769 // Ignore invalid position
3774 // Found a good place
3775 dstream<<"Searched through "<<i<<" places."<<std::endl;
3780 // If no suitable place was not found, go above water at least.
3781 if(groundheight < WATER_LEVEL)
3782 groundheight = WATER_LEVEL;
3784 player->setPosition(intToFloat(v3s16(
3786 groundheight + 5, // Accomodate mud
3792 Add player to environment
3795 m_env.addPlayer(player);
3798 Add stuff to inventory
3801 if(g_settings.getBool("creative_mode"))
3803 setCreativeInventory(player);
3808 InventoryItem *item = new ToolItem("WPick", 32000);
3809 void* r = player->inventory.addItem("main", item);
3813 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3814 void* r = player->inventory.addItem("main", item);
3818 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3819 void* r = player->inventory.addItem("main", item);
3823 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3824 void* r = player->inventory.addItem("main", item);
3828 InventoryItem *item = new CraftItem("Stick", 4);
3829 void* r = player->inventory.addItem("main", item);
3833 InventoryItem *item = new ToolItem("WPick", 32000);
3834 void* r = player->inventory.addItem("main", item);
3838 InventoryItem *item = new ToolItem("STPick", 32000);
3839 void* r = player->inventory.addItem("main", item);
3842 /*// Give some lights
3844 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3845 bool r = player->inventory.addItem("main", item);
3849 for(u16 i=0; i<4; i++)
3851 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3852 bool r = player->inventory.addItem("main", item);
3855 /*// Give some other stuff
3857 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3858 bool r = player->inventory.addItem("main", item);
3865 } // create new player
3869 void Server::UpdateBlockWaterPressure(MapBlock *block,
3870 core::map<v3s16, MapBlock*> &modified_blocks)
3872 MapVoxelManipulator v(&m_env.getMap());
3873 v.m_disable_water_climb =
3874 g_settings.getBool("disable_water_climb");
3876 VoxelArea area(block->getPosRelative(),
3877 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3881 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3883 catch(ProcessingLimitException &e)
3885 dstream<<"Processing limit reached (1)"<<std::endl;
3888 v.blitBack(modified_blocks);
3892 void Server::handlePeerChange(PeerChange &c)
3894 JMutexAutoLock envlock(m_env_mutex);
3895 JMutexAutoLock conlock(m_con_mutex);
3897 if(c.type == PEER_ADDED)
3904 core::map<u16, RemoteClient*>::Node *n;
3905 n = m_clients.find(c.peer_id);
3906 // The client shouldn't already exist
3910 RemoteClient *client = new RemoteClient();
3911 client->peer_id = c.peer_id;
3912 m_clients.insert(client->peer_id, client);
3915 else if(c.type == PEER_REMOVED)
3922 core::map<u16, RemoteClient*>::Node *n;
3923 n = m_clients.find(c.peer_id);
3924 // The client should exist
3927 // Collect information about leaving in chat
3928 std::wstring message;
3930 std::wstring name = L"unknown";
3931 Player *player = m_env.getPlayer(c.peer_id);
3933 name = narrow_to_wide(player->getName());
3937 message += L" left game";
3939 message += L" (timed out)";
3944 m_env.removePlayer(c.peer_id);
3947 // Set player client disconnected
3949 Player *player = m_env.getPlayer(c.peer_id);
3951 player->peer_id = 0;
3955 delete m_clients[c.peer_id];
3956 m_clients.remove(c.peer_id);
3958 // Send player info to all remaining clients
3961 // Send leave chat message to all remaining clients
3962 BroadcastChatMessage(message);
3971 void Server::handlePeerChanges()
3973 while(m_peer_change_queue.size() > 0)
3975 PeerChange c = m_peer_change_queue.pop_front();
3977 dout_server<<"Server: Handling peer change: "
3978 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3981 handlePeerChange(c);
3985 void dedicated_server_loop(Server &server, bool &kill)
3987 DSTACK(__FUNCTION_NAME);
3989 std::cout<<DTIME<<std::endl;
3990 std::cout<<"========================"<<std::endl;
3991 std::cout<<"Running dedicated server"<<std::endl;
3992 std::cout<<"========================"<<std::endl;
3993 std::cout<<std::endl;
3997 // This is kind of a hack but can be done like this
3998 // because server.step() is very light
4002 if(server.getShutdownRequested() || kill)
4004 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4008 static int counter = 0;
4014 core::list<PlayerInfo> list = server.getPlayerInfo();
4015 core::list<PlayerInfo>::Iterator i;
4016 static u32 sum_old = 0;
4017 u32 sum = PIChecksum(list);
4020 std::cout<<DTIME<<"Player info:"<<std::endl;
4021 for(i=list.begin(); i!=list.end(); i++)
4023 i->PrintLine(&std::cout);