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);
172 // If it is a dummy, block was not found on disk
175 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
178 if(only_from_disk == false)
180 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
185 catch(InvalidPositionException &e)
188 // This happens when position is over limit.
194 if(debug && changed_blocks.size() > 0)
196 dout_server<<DTIME<<"Got changed_blocks: ";
197 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
198 i.atEnd() == false; i++)
200 MapBlock *block = i.getNode()->getValue();
201 v3s16 p = block->getPos();
202 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
204 dout_server<<std::endl;
208 Collect a list of blocks that have been modified in
209 addition to the fetched one.
212 // Add all the "changed blocks" to modified_blocks
213 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
214 i.atEnd() == false; i++)
216 MapBlock *block = i.getNode()->getValue();
217 modified_blocks.insert(block->getPos(), block);
220 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
221 <<" blocks"<<std::endl;*/
223 //TimeTaker timer("** updateLighting");
225 // Update lighting without locking the environment mutex,
226 // add modified blocks to changed blocks
227 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
229 // If we got no block, there should be no invalidated blocks
232 assert(lighting_invalidated_blocks.size() == 0);
238 Set sent status of modified blocks on clients
241 // NOTE: Server's clients are also behind the connection mutex
242 JMutexAutoLock lock(m_server->m_con_mutex);
245 Add the originally fetched block to the modified list
249 modified_blocks.insert(p, block);
253 Set the modified blocks unsent for all the clients
256 for(core::map<u16, RemoteClient*>::Iterator
257 i = m_server->m_clients.getIterator();
258 i.atEnd() == false; i++)
260 RemoteClient *client = i.getNode()->getValue();
262 if(modified_blocks.size() > 0)
264 // Remove block from sent history
265 client->SetBlocksNotSent(modified_blocks);
271 END_DEBUG_EXCEPTION_HANDLER
276 void RemoteClient::GetNextBlocks(Server *server, float dtime,
277 core::array<PrioritySortedBlockTransfer> &dest)
279 DSTACK(__FUNCTION_NAME);
282 m_nearest_unsent_reset_timer += dtime;
284 // Won't send anything if already sending
285 if(m_blocks_sending.size() >= g_settings.getU16
286 ("max_simultaneous_block_sends_per_client"))
288 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
292 Player *player = server->m_env.getPlayer(peer_id);
294 assert(player != NULL);
296 v3f playerpos = player->getPosition();
297 v3f playerspeed = player->getSpeed();
299 v3s16 center_nodepos = floatToInt(playerpos, BS);
301 v3s16 center = getNodeBlockPos(center_nodepos);
303 // Camera position and direction
305 playerpos + v3f(0, BS+BS/2, 0);
306 v3f camera_dir = v3f(0,0,1);
307 camera_dir.rotateYZBy(player->getPitch());
308 camera_dir.rotateXZBy(player->getYaw());
311 Get the starting value of the block finder radius.
313 s16 last_nearest_unsent_d;
316 if(m_last_center != center)
318 m_nearest_unsent_d = 0;
319 m_last_center = center;
322 /*dstream<<"m_nearest_unsent_reset_timer="
323 <<m_nearest_unsent_reset_timer<<std::endl;*/
324 if(m_nearest_unsent_reset_timer > 5.0)
326 m_nearest_unsent_reset_timer = 0;
327 m_nearest_unsent_d = 0;
328 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
331 last_nearest_unsent_d = m_nearest_unsent_d;
333 d_start = m_nearest_unsent_d;
335 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
336 ("max_simultaneous_block_sends_per_client");
337 u16 maximum_simultaneous_block_sends =
338 maximum_simultaneous_block_sends_setting;
341 Check the time from last addNode/removeNode.
343 Decrease send rate if player is building stuff.
345 m_time_from_building += dtime;
346 if(m_time_from_building < g_settings.getFloat(
347 "full_block_send_enable_min_time_from_building"))
349 maximum_simultaneous_block_sends
350 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
353 u32 num_blocks_selected = m_blocks_sending.size();
356 next time d will be continued from the d from which the nearest
357 unsent block was found this time.
359 This is because not necessarily any of the blocks found this
360 time are actually sent.
362 s32 new_nearest_unsent_d = -1;
364 s16 d_max = g_settings.getS16("max_block_send_distance");
365 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
367 //dstream<<"Starting from "<<d_start<<std::endl;
369 for(s16 d = d_start; d <= d_max; d++)
371 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
374 If m_nearest_unsent_d was changed by the EmergeThread
375 (it can change it to 0 through SetBlockNotSent),
377 Else update m_nearest_unsent_d
379 if(m_nearest_unsent_d != last_nearest_unsent_d)
381 d = m_nearest_unsent_d;
382 last_nearest_unsent_d = m_nearest_unsent_d;
386 Get the border/face dot coordinates of a "d-radiused"
389 core::list<v3s16> list;
390 getFacePositions(list, d);
392 core::list<v3s16>::Iterator li;
393 for(li=list.begin(); li!=list.end(); li++)
395 v3s16 p = *li + center;
399 - Don't allow too many simultaneous transfers
400 - EXCEPT when the blocks are very close
402 Also, don't send blocks that are already flying.
405 u16 maximum_simultaneous_block_sends_now =
406 maximum_simultaneous_block_sends;
408 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
410 maximum_simultaneous_block_sends_now =
411 maximum_simultaneous_block_sends_setting;
414 // Limit is dynamically lowered when building
415 if(num_blocks_selected
416 >= maximum_simultaneous_block_sends_now)
418 /*dstream<<"Not sending more blocks. Queue full. "
419 <<m_blocks_sending.size()
424 if(m_blocks_sending.find(p) != NULL)
430 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
431 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
432 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
433 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
434 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
435 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
438 // If this is true, inexistent block will be made from scratch
439 bool generate = d <= d_max_gen;
442 /*// Limit the generating area vertically to 2/3
443 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
446 // Limit the send area vertically to 2/3
447 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
453 If block is far away, don't generate it unless it is
456 NOTE: We can't know the ground level this way with the
462 MapSector *sector = NULL;
465 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
467 catch(InvalidPositionException &e)
473 // Get center ground height in nodes
474 f32 gh = sector->getGroundHeight(
475 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
476 // Block center y in nodes
477 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
478 // If differs a lot, don't generate
479 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
486 Don't generate or send if not in sight
489 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
495 Don't send already sent blocks
498 if(m_blocks_sent.find(p) != NULL)
503 Check if map has this block
505 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
507 bool surely_not_found_on_disk = false;
508 bool block_is_invalid = false;
513 surely_not_found_on_disk = true;
516 if(block->isValid() == false)
518 block_is_invalid = true;
522 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
523 v2s16 chunkpos = map->sector_to_chunk(p2d);
524 if(map->chunkNonVolatile(chunkpos) == false)
525 block_is_invalid = true;
529 If block has been marked to not exist on disk (dummy)
530 and generating new ones is not wanted, skip block.
532 if(generate == false && surely_not_found_on_disk == true)
539 Record the lowest d from which a a block has been
540 found being not sent and possibly to exist
542 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
544 new_nearest_unsent_d = d;
548 Add inexistent block to emerge queue.
550 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
552 //TODO: Get value from somewhere
553 // Allow only one block in emerge queue
554 if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
556 //dstream<<"Adding block to emerge queue"<<std::endl;
558 // Add it to the emerge queue and trigger the thread
561 if(generate == false)
562 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
564 server->m_emerge_queue.addBlock(peer_id, p, flags);
565 server->m_emergethread.trigger();
573 Add block to send queue
576 PrioritySortedBlockTransfer q((float)d, p, peer_id);
580 num_blocks_selected += 1;
585 if(new_nearest_unsent_d != -1)
587 m_nearest_unsent_d = new_nearest_unsent_d;
591 void RemoteClient::SendObjectData(
594 core::map<v3s16, bool> &stepped_blocks
597 DSTACK(__FUNCTION_NAME);
599 // Can't send anything without knowing version
600 if(serialization_version == SER_FMT_VER_INVALID)
602 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
608 Send a TOCLIENT_OBJECTDATA packet.
612 u16 number of player positions
623 std::ostringstream os(std::ios_base::binary);
627 writeU16(buf, TOCLIENT_OBJECTDATA);
628 os.write((char*)buf, 2);
631 Get and write player data
634 // Get connected players
635 core::list<Player*> players = server->m_env.getPlayers(true);
637 // Write player count
638 u16 playercount = players.size();
639 writeU16(buf, playercount);
640 os.write((char*)buf, 2);
642 core::list<Player*>::Iterator i;
643 for(i = players.begin();
644 i != players.end(); i++)
648 v3f pf = player->getPosition();
649 v3f sf = player->getSpeed();
651 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
652 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
653 s32 pitch_i (player->getPitch() * 100);
654 s32 yaw_i (player->getYaw() * 100);
656 writeU16(buf, player->peer_id);
657 os.write((char*)buf, 2);
658 writeV3S32(buf, position_i);
659 os.write((char*)buf, 12);
660 writeV3S32(buf, speed_i);
661 os.write((char*)buf, 12);
662 writeS32(buf, pitch_i);
663 os.write((char*)buf, 4);
664 writeS32(buf, yaw_i);
665 os.write((char*)buf, 4);
669 Get and write object data
675 For making players to be able to build to their nearby
676 environment (building is not possible on blocks that are not
679 - Add blocks to emerge queue if they are not found
681 SUGGESTION: These could be ignored from the backside of the player
684 Player *player = server->m_env.getPlayer(peer_id);
688 v3f playerpos = player->getPosition();
689 v3f playerspeed = player->getSpeed();
691 v3s16 center_nodepos = floatToInt(playerpos, BS);
692 v3s16 center = getNodeBlockPos(center_nodepos);
694 s16 d_max = g_settings.getS16("active_object_range");
696 // Number of blocks whose objects were written to bos
699 std::ostringstream bos(std::ios_base::binary);
701 for(s16 d = 0; d <= d_max; d++)
703 core::list<v3s16> list;
704 getFacePositions(list, d);
706 core::list<v3s16>::Iterator li;
707 for(li=list.begin(); li!=list.end(); li++)
709 v3s16 p = *li + center;
712 Ignore blocks that haven't been sent to the client
715 if(m_blocks_sent.find(p) == NULL)
719 // Try stepping block and add it to a send queue
724 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
727 Step block if not in stepped_blocks and add to stepped_blocks.
729 if(stepped_blocks.find(p) == NULL)
731 block->stepObjects(dtime, true, server->getDayNightRatio());
732 stepped_blocks.insert(p, true);
733 block->setChangedFlag();
736 // Skip block if there are no objects
737 if(block->getObjectCount() == 0)
746 bos.write((char*)buf, 6);
749 block->serializeObjects(bos, serialization_version);
754 Stop collecting objects if data is already too big
756 // Sum of player and object data sizes
757 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
758 // break out if data too big
759 if(sum > MAX_OBJECTDATA_SIZE)
761 goto skip_subsequent;
765 catch(InvalidPositionException &e)
768 // Add it to the emerge queue and trigger the thread.
769 // Fetch the block only if it is on disk.
771 // Grab and increment counter
772 /*SharedPtr<JMutexAutoLock> lock
773 (m_num_blocks_in_emerge_queue.getLock());
774 m_num_blocks_in_emerge_queue.m_value++;*/
776 // Add to queue as an anonymous fetch from disk
777 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
778 server->m_emerge_queue.addBlock(0, p, flags);
779 server->m_emergethread.trigger();
787 writeU16(buf, blockcount);
788 os.write((char*)buf, 2);
790 // Write block objects
797 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
800 std::string s = os.str();
801 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
802 // Send as unreliable
803 server->m_con.Send(peer_id, 0, data, false);
806 void RemoteClient::GotBlock(v3s16 p)
808 if(m_blocks_sending.find(p) != NULL)
809 m_blocks_sending.remove(p);
812 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
813 " m_blocks_sending"<<std::endl;*/
814 m_excess_gotblocks++;
816 m_blocks_sent.insert(p, true);
819 void RemoteClient::SentBlock(v3s16 p)
821 if(m_blocks_sending.find(p) == NULL)
822 m_blocks_sending.insert(p, 0.0);
824 dstream<<"RemoteClient::SentBlock(): Sent block"
825 " already in m_blocks_sending"<<std::endl;
828 void RemoteClient::SetBlockNotSent(v3s16 p)
830 m_nearest_unsent_d = 0;
832 if(m_blocks_sending.find(p) != NULL)
833 m_blocks_sending.remove(p);
834 if(m_blocks_sent.find(p) != NULL)
835 m_blocks_sent.remove(p);
838 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
840 m_nearest_unsent_d = 0;
842 for(core::map<v3s16, MapBlock*>::Iterator
843 i = blocks.getIterator();
844 i.atEnd()==false; i++)
846 v3s16 p = i.getNode()->getKey();
848 if(m_blocks_sending.find(p) != NULL)
849 m_blocks_sending.remove(p);
850 if(m_blocks_sent.find(p) != NULL)
851 m_blocks_sent.remove(p);
859 PlayerInfo::PlayerInfo()
864 void PlayerInfo::PrintLine(std::ostream *s)
867 (*s)<<"\""<<name<<"\" ("
868 <<(position.X/10)<<","<<(position.Y/10)
869 <<","<<(position.Z/10)<<") ";
871 (*s)<<" avg_rtt="<<avg_rtt;
875 u32 PIChecksum(core::list<PlayerInfo> &l)
877 core::list<PlayerInfo>::Iterator i;
880 for(i=l.begin(); i!=l.end(); i++)
882 checksum += a * (i->id+1);
883 checksum ^= 0x435aafcd;
894 std::string mapsavedir
896 m_env(new ServerMap(mapsavedir), this),
897 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
899 m_emergethread(this),
902 m_time_of_day_send_timer(0),
904 m_mapsavedir(mapsavedir),
905 m_shutdown_requested(false),
906 m_ignore_map_edit_events(false),
907 m_ignore_map_edit_events_peer_id(0)
909 m_liquid_transform_timer = 0.0;
910 m_print_info_timer = 0.0;
911 m_objectdata_timer = 0.0;
912 m_emergethread_trigger_timer = 0.0;
913 m_savemap_timer = 0.0;
917 m_step_dtime_mutex.Init();
920 m_env.getMap().addEventReceiver(this);
923 m_env.deSerializePlayers(m_mapsavedir);
929 Send shutdown message
932 JMutexAutoLock conlock(m_con_mutex);
934 std::wstring line = L"*** Server shutting down";
937 Send the message to clients
939 for(core::map<u16, RemoteClient*>::Iterator
940 i = m_clients.getIterator();
941 i.atEnd() == false; i++)
943 // Get client and check that it is valid
944 RemoteClient *client = i.getNode()->getValue();
945 assert(client->peer_id == i.getNode()->getKey());
946 if(client->serialization_version == SER_FMT_VER_INVALID)
949 SendChatMessage(client->peer_id, line);
956 m_env.serializePlayers(m_mapsavedir);
967 JMutexAutoLock clientslock(m_con_mutex);
969 for(core::map<u16, RemoteClient*>::Iterator
970 i = m_clients.getIterator();
971 i.atEnd() == false; i++)
974 // NOTE: These are removed by env destructor
976 u16 peer_id = i.getNode()->getKey();
977 JMutexAutoLock envlock(m_env_mutex);
978 m_env.removePlayer(peer_id);
982 delete i.getNode()->getValue();
987 void Server::start(unsigned short port)
989 DSTACK(__FUNCTION_NAME);
990 // Stop thread if already running
993 // Initialize connection
994 m_con.setTimeoutMs(30);
998 m_thread.setRun(true);
1001 dout_server<<"Server: Started on port "<<port<<std::endl;
1006 DSTACK(__FUNCTION_NAME);
1008 // Stop threads (set run=false first so both start stopping)
1009 m_thread.setRun(false);
1010 m_emergethread.setRun(false);
1012 m_emergethread.stop();
1014 dout_server<<"Server: Threads stopped"<<std::endl;
1016 dout_server<<"Server: Saving players"<<std::endl;
1018 // FIXME: Apparently this does not do anything here
1019 //m_env.serializePlayers(m_mapsavedir);
1022 void Server::step(float dtime)
1024 DSTACK(__FUNCTION_NAME);
1029 JMutexAutoLock lock(m_step_dtime_mutex);
1030 m_step_dtime += dtime;
1034 void Server::AsyncRunStep()
1036 DSTACK(__FUNCTION_NAME);
1040 JMutexAutoLock lock1(m_step_dtime_mutex);
1041 dtime = m_step_dtime;
1044 // Send blocks to clients
1050 //dstream<<"Server steps "<<dtime<<std::endl;
1051 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1054 JMutexAutoLock lock1(m_step_dtime_mutex);
1055 m_step_dtime -= dtime;
1062 m_uptime.set(m_uptime.get() + dtime);
1066 Update m_time_of_day
1069 m_time_counter += dtime;
1070 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1071 u32 units = (u32)(m_time_counter*speed);
1072 m_time_counter -= (f32)units / speed;
1073 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1075 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1078 Send to clients at constant intervals
1081 m_time_of_day_send_timer -= dtime;
1082 if(m_time_of_day_send_timer < 0.0)
1084 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1086 //JMutexAutoLock envlock(m_env_mutex);
1087 JMutexAutoLock conlock(m_con_mutex);
1089 for(core::map<u16, RemoteClient*>::Iterator
1090 i = m_clients.getIterator();
1091 i.atEnd() == false; i++)
1093 RemoteClient *client = i.getNode()->getValue();
1094 //Player *player = m_env.getPlayer(client->peer_id);
1096 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1097 m_time_of_day.get());
1099 m_con.Send(client->peer_id, 0, data, true);
1105 // Process connection's timeouts
1106 JMutexAutoLock lock2(m_con_mutex);
1107 m_con.RunTimeouts(dtime);
1111 // This has to be called so that the client list gets synced
1112 // with the peer list of the connection
1113 handlePeerChanges();
1118 // This also runs Map's timers
1119 JMutexAutoLock lock(m_env_mutex);
1130 m_liquid_transform_timer += dtime;
1131 if(m_liquid_transform_timer >= 1.00)
1133 m_liquid_transform_timer -= 1.00;
1135 JMutexAutoLock lock(m_env_mutex);
1137 core::map<v3s16, MapBlock*> modified_blocks;
1138 m_env.getMap().transformLiquids(modified_blocks);
1143 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1144 ServerMap &map = ((ServerMap&)m_env.getMap());
1145 map.updateLighting(modified_blocks, lighting_modified_blocks);
1147 // Add blocks modified by lighting to modified_blocks
1148 for(core::map<v3s16, MapBlock*>::Iterator
1149 i = lighting_modified_blocks.getIterator();
1150 i.atEnd() == false; i++)
1152 MapBlock *block = i.getNode()->getValue();
1153 modified_blocks.insert(block->getPos(), block);
1157 Set the modified blocks unsent for all the clients
1160 JMutexAutoLock lock2(m_con_mutex);
1162 for(core::map<u16, RemoteClient*>::Iterator
1163 i = m_clients.getIterator();
1164 i.atEnd() == false; i++)
1166 RemoteClient *client = i.getNode()->getValue();
1168 if(modified_blocks.size() > 0)
1170 // Remove block from sent history
1171 client->SetBlocksNotSent(modified_blocks);
1176 // Periodically print some info
1178 float &counter = m_print_info_timer;
1184 JMutexAutoLock lock2(m_con_mutex);
1186 for(core::map<u16, RemoteClient*>::Iterator
1187 i = m_clients.getIterator();
1188 i.atEnd() == false; i++)
1190 //u16 peer_id = i.getNode()->getKey();
1191 RemoteClient *client = i.getNode()->getValue();
1192 client->PrintInfo(std::cout);
1197 if(g_settings.getBool("enable_experimental"))
1201 Check added and deleted active objects
1204 JMutexAutoLock envlock(m_env_mutex);
1205 JMutexAutoLock conlock(m_con_mutex);
1207 // Radius inside which objects are active
1210 for(core::map<u16, RemoteClient*>::Iterator
1211 i = m_clients.getIterator();
1212 i.atEnd() == false; i++)
1214 RemoteClient *client = i.getNode()->getValue();
1215 Player *player = m_env.getPlayer(client->peer_id);
1218 v3s16 pos = floatToInt(player->getPosition(), BS);
1220 core::map<u16, bool> removed_objects;
1221 core::map<u16, bool> added_objects;
1222 m_env.getRemovedActiveObjects(pos, radius,
1223 client->m_known_objects, removed_objects);
1224 m_env.getAddedActiveObjects(pos, radius,
1225 client->m_known_objects, added_objects);
1227 // Ignore if nothing happened
1228 if(removed_objects.size() == 0 && added_objects.size() == 0)
1231 std::string data_buffer;
1235 // Handle removed objects
1236 writeU16((u8*)buf, removed_objects.size());
1237 data_buffer.append(buf, 2);
1238 for(core::map<u16, bool>::Iterator
1239 i = removed_objects.getIterator();
1240 i.atEnd()==false; i++)
1243 u16 id = i.getNode()->getKey();
1244 ServerActiveObject* obj = m_env.getActiveObject(id);
1246 // Add to data buffer for sending
1247 writeU16((u8*)buf, i.getNode()->getKey());
1248 data_buffer.append(buf, 2);
1250 // Remove from known objects
1251 client->m_known_objects.remove(i.getNode()->getKey());
1253 if(obj && obj->m_known_by_count > 0)
1254 obj->m_known_by_count--;
1257 // Handle added objects
1258 writeU16((u8*)buf, added_objects.size());
1259 data_buffer.append(buf, 2);
1260 for(core::map<u16, bool>::Iterator
1261 i = added_objects.getIterator();
1262 i.atEnd()==false; i++)
1265 u16 id = i.getNode()->getKey();
1266 ServerActiveObject* obj = m_env.getActiveObject(id);
1269 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1271 dstream<<"WARNING: "<<__FUNCTION_NAME
1272 <<": NULL object"<<std::endl;
1274 type = obj->getType();
1276 // Add to data buffer for sending
1277 writeU16((u8*)buf, id);
1278 data_buffer.append(buf, 2);
1279 writeU8((u8*)buf, type);
1280 data_buffer.append(buf, 1);
1282 data_buffer.append(serializeLongString(
1283 obj->getClientInitializationData()));
1285 // Add to known objects
1286 client->m_known_objects.insert(i.getNode()->getKey(), false);
1289 obj->m_known_by_count++;
1293 SharedBuffer<u8> reply(2 + data_buffer.size());
1294 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1295 memcpy((char*)&reply[2], data_buffer.c_str(),
1296 data_buffer.size());
1298 m_con.Send(client->peer_id, 0, reply, true);
1300 dstream<<"INFO: Server: Sent object remove/add: "
1301 <<removed_objects.size()<<" removed, "
1302 <<added_objects.size()<<" added, "
1303 <<"packet size is "<<reply.getSize()<<std::endl;
1308 Send object messages
1311 JMutexAutoLock envlock(m_env_mutex);
1312 JMutexAutoLock conlock(m_con_mutex);
1315 // Value = data sent by object
1316 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1318 // Get active object messages from environment
1321 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1325 core::list<ActiveObjectMessage>* message_list = NULL;
1326 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1327 n = buffered_messages.find(aom.id);
1330 message_list = new core::list<ActiveObjectMessage>;
1331 buffered_messages.insert(aom.id, message_list);
1335 message_list = n->getValue();
1337 message_list->push_back(aom);
1340 // Route data to every client
1341 for(core::map<u16, RemoteClient*>::Iterator
1342 i = m_clients.getIterator();
1343 i.atEnd()==false; i++)
1345 RemoteClient *client = i.getNode()->getValue();
1346 std::string reliable_data;
1347 std::string unreliable_data;
1348 // Go through all objects in message buffer
1349 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1350 j = buffered_messages.getIterator();
1351 j.atEnd()==false; j++)
1353 // If object is not known by client, skip it
1354 u16 id = j.getNode()->getKey();
1355 if(client->m_known_objects.find(id) == NULL)
1357 // Get message list of object
1358 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1359 // Go through every message
1360 for(core::list<ActiveObjectMessage>::Iterator
1361 k = list->begin(); k != list->end(); k++)
1363 // Compose the full new data with header
1364 ActiveObjectMessage aom = *k;
1365 std::string new_data;
1368 writeU16((u8*)&buf[0], aom.id);
1369 new_data.append(buf, 2);
1371 new_data += serializeString(aom.datastring);
1372 // Add data to buffer
1374 reliable_data += new_data;
1376 unreliable_data += new_data;
1380 reliable_data and unreliable_data are now ready.
1383 if(reliable_data.size() > 0)
1385 SharedBuffer<u8> reply(2 + reliable_data.size());
1386 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1387 memcpy((char*)&reply[2], reliable_data.c_str(),
1388 reliable_data.size());
1390 m_con.Send(client->peer_id, 0, reply, true);
1392 if(unreliable_data.size() > 0)
1394 SharedBuffer<u8> reply(2 + unreliable_data.size());
1395 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1396 memcpy((char*)&reply[2], unreliable_data.c_str(),
1397 unreliable_data.size());
1398 // Send as unreliable
1399 m_con.Send(client->peer_id, 0, reply, false);
1402 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1404 dstream<<"INFO: Server: Size of object message data: "
1405 <<"reliable: "<<reliable_data.size()
1406 <<", unreliable: "<<unreliable_data.size()
1411 // Clear buffered_messages
1412 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1413 i = buffered_messages.getIterator();
1414 i.atEnd()==false; i++)
1416 delete i.getNode()->getValue();
1420 } // enable_experimental
1423 Send queued-for-sending map edit events.
1426 while(m_unsent_map_edit_queue.size() != 0)
1428 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1430 if(event->type == MEET_ADDNODE)
1432 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1433 sendAddNode(event->p, event->n, event->already_known_by_peer);
1435 else if(event->type == MEET_REMOVENODE)
1437 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1438 sendRemoveNode(event->p, event->already_known_by_peer);
1440 else if(event->type == MEET_OTHER)
1442 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1447 dstream<<"WARNING: Server: Unknown MapEditEvent "
1448 <<((u32)event->type)<<std::endl;
1456 Send object positions
1457 TODO: Get rid of MapBlockObjects
1460 float &counter = m_objectdata_timer;
1462 if(counter >= g_settings.getFloat("objectdata_interval"))
1464 JMutexAutoLock lock1(m_env_mutex);
1465 JMutexAutoLock lock2(m_con_mutex);
1466 SendObjectData(counter);
1473 Trigger emergethread (it somehow gets to a non-triggered but
1474 bysy state sometimes)
1477 float &counter = m_emergethread_trigger_timer;
1483 m_emergethread.trigger();
1489 float &counter = m_savemap_timer;
1491 if(counter >= g_settings.getFloat("server_map_save_interval"))
1495 JMutexAutoLock lock(m_env_mutex);
1497 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1499 // Save only changed parts
1500 m_env.getMap().save(true);
1502 // Delete unused sectors
1503 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1504 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1505 if(deleted_count > 0)
1507 dout_server<<"Server: Unloaded "<<deleted_count
1508 <<" sectors from memory"<<std::endl;
1512 m_env.serializePlayers(m_mapsavedir);
1518 void Server::Receive()
1520 DSTACK(__FUNCTION_NAME);
1521 u32 data_maxsize = 10000;
1522 Buffer<u8> data(data_maxsize);
1527 JMutexAutoLock conlock(m_con_mutex);
1528 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1531 // This has to be called so that the client list gets synced
1532 // with the peer list of the connection
1533 handlePeerChanges();
1535 ProcessData(*data, datasize, peer_id);
1537 catch(con::InvalidIncomingDataException &e)
1539 derr_server<<"Server::Receive(): "
1540 "InvalidIncomingDataException: what()="
1541 <<e.what()<<std::endl;
1543 catch(con::PeerNotFoundException &e)
1545 //NOTE: This is not needed anymore
1547 // The peer has been disconnected.
1548 // Find the associated player and remove it.
1550 /*JMutexAutoLock envlock(m_env_mutex);
1552 dout_server<<"ServerThread: peer_id="<<peer_id
1553 <<" has apparently closed connection. "
1554 <<"Removing player."<<std::endl;
1556 m_env.removePlayer(peer_id);*/
1560 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1562 DSTACK(__FUNCTION_NAME);
1563 // Environment is locked first.
1564 JMutexAutoLock envlock(m_env_mutex);
1565 JMutexAutoLock conlock(m_con_mutex);
1569 peer = m_con.GetPeer(peer_id);
1571 catch(con::PeerNotFoundException &e)
1573 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1574 <<peer_id<<" not found"<<std::endl;
1578 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1586 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1588 if(command == TOSERVER_INIT)
1590 // [0] u16 TOSERVER_INIT
1591 // [2] u8 SER_FMT_VER_HIGHEST
1592 // [3] u8[20] player_name
1597 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1598 <<peer->id<<std::endl;
1600 // First byte after command is maximum supported
1601 // serialization version
1602 u8 client_max = data[2];
1603 u8 our_max = SER_FMT_VER_HIGHEST;
1604 // Use the highest version supported by both
1605 u8 deployed = core::min_(client_max, our_max);
1606 // If it's lower than the lowest supported, give up.
1607 if(deployed < SER_FMT_VER_LOWEST)
1608 deployed = SER_FMT_VER_INVALID;
1610 //peer->serialization_version = deployed;
1611 getClient(peer->id)->pending_serialization_version = deployed;
1613 if(deployed == SER_FMT_VER_INVALID)
1615 derr_server<<DTIME<<"Server: Cannot negotiate "
1616 "serialization version with peer "
1617 <<peer_id<<std::endl;
1626 const u32 playername_size = 20;
1627 char playername[playername_size];
1628 for(u32 i=0; i<playername_size-1; i++)
1630 playername[i] = data[3+i];
1632 playername[playername_size-1] = 0;
1635 Player *player = emergePlayer(playername, "", peer_id);
1636 //Player *player = m_env.getPlayer(peer_id);
1639 // DEBUG: Test serialization
1640 std::ostringstream test_os;
1641 player->serialize(test_os);
1642 dstream<<"Player serialization test: \""<<test_os.str()
1644 std::istringstream test_is(test_os.str());
1645 player->deSerialize(test_is);
1648 // If failed, cancel
1651 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1652 <<": failed to emerge player"<<std::endl;
1657 // If a client is already connected to the player, cancel
1658 if(player->peer_id != 0)
1660 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1661 <<" tried to connect to "
1662 "an already connected player (peer_id="
1663 <<player->peer_id<<")"<<std::endl;
1666 // Set client of player
1667 player->peer_id = peer_id;
1670 // Check if player doesn't exist
1672 throw con::InvalidIncomingDataException
1673 ("Server::ProcessData(): INIT: Player doesn't exist");
1675 /*// update name if it was supplied
1676 if(datasize >= 20+3)
1679 player->updateName((const char*)&data[3]);
1682 // Now answer with a TOCLIENT_INIT
1684 SharedBuffer<u8> reply(2+1+6);
1685 writeU16(&reply[0], TOCLIENT_INIT);
1686 writeU8(&reply[2], deployed);
1687 writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1689 m_con.Send(peer_id, 0, reply, true);
1693 if(command == TOSERVER_INIT2)
1695 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1696 <<peer->id<<std::endl;
1699 getClient(peer->id)->serialization_version
1700 = getClient(peer->id)->pending_serialization_version;
1703 Send some initialization data
1706 // Send player info to all players
1709 // Send inventory to player
1710 SendInventory(peer->id);
1714 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1715 m_time_of_day.get());
1716 m_con.Send(peer->id, 0, data, true);
1719 // Send information about server to player in chat
1720 SendChatMessage(peer_id, getStatusString());
1722 // Send information about joining in chat
1724 std::wstring name = L"unknown";
1725 Player *player = m_env.getPlayer(peer_id);
1727 name = narrow_to_wide(player->getName());
1729 std::wstring message;
1732 message += L" joined game";
1733 BroadcastChatMessage(message);
1739 if(peer_ser_ver == SER_FMT_VER_INVALID)
1741 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1742 " serialization format invalid or not initialized."
1743 " Skipping incoming command="<<command<<std::endl;
1747 Player *player = m_env.getPlayer(peer_id);
1750 derr_server<<"Server::ProcessData(): Cancelling: "
1751 "No player for peer_id="<<peer_id
1755 if(command == TOSERVER_PLAYERPOS)
1757 if(datasize < 2+12+12+4+4)
1761 v3s32 ps = readV3S32(&data[start+2]);
1762 v3s32 ss = readV3S32(&data[start+2+12]);
1763 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1764 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1765 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1766 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1767 pitch = wrapDegrees(pitch);
1768 yaw = wrapDegrees(yaw);
1769 player->setPosition(position);
1770 player->setSpeed(speed);
1771 player->setPitch(pitch);
1772 player->setYaw(yaw);
1774 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1775 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1776 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1778 else if(command == TOSERVER_GOTBLOCKS)
1791 u16 count = data[2];
1792 for(u16 i=0; i<count; i++)
1794 if((s16)datasize < 2+1+(i+1)*6)
1795 throw con::InvalidIncomingDataException
1796 ("GOTBLOCKS length is too short");
1797 v3s16 p = readV3S16(&data[2+1+i*6]);
1798 /*dstream<<"Server: GOTBLOCKS ("
1799 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1800 RemoteClient *client = getClient(peer_id);
1801 client->GotBlock(p);
1804 else if(command == TOSERVER_DELETEDBLOCKS)
1817 u16 count = data[2];
1818 for(u16 i=0; i<count; i++)
1820 if((s16)datasize < 2+1+(i+1)*6)
1821 throw con::InvalidIncomingDataException
1822 ("DELETEDBLOCKS length is too short");
1823 v3s16 p = readV3S16(&data[2+1+i*6]);
1824 /*dstream<<"Server: DELETEDBLOCKS ("
1825 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1826 RemoteClient *client = getClient(peer_id);
1827 client->SetBlockNotSent(p);
1830 else if(command == TOSERVER_CLICK_OBJECT)
1837 [2] u8 button (0=left, 1=right)
1842 u8 button = readU8(&data[2]);
1844 p.X = readS16(&data[3]);
1845 p.Y = readS16(&data[5]);
1846 p.Z = readS16(&data[7]);
1847 s16 id = readS16(&data[9]);
1848 //u16 item_i = readU16(&data[11]);
1850 MapBlock *block = NULL;
1853 block = m_env.getMap().getBlockNoCreate(p);
1855 catch(InvalidPositionException &e)
1857 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1861 MapBlockObject *obj = block->getObject(id);
1865 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1869 //TODO: Check that object is reasonably close
1874 InventoryList *ilist = player->inventory.getList("main");
1875 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1878 // Skip if inventory has no free space
1879 if(ilist->getUsedSlots() == ilist->getSize())
1881 dout_server<<"Player inventory has no free space"<<std::endl;
1886 Create the inventory item
1888 InventoryItem *item = NULL;
1889 // If it is an item-object, take the item from it
1890 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1892 item = ((ItemObject*)obj)->createInventoryItem();
1894 // Else create an item of the object
1897 item = new MapBlockObjectItem
1898 (obj->getInventoryString());
1901 // Add to inventory and send inventory
1902 ilist->addItem(item);
1903 SendInventory(player->peer_id);
1906 // Remove from block
1907 block->removeObject(id);
1910 else if(command == TOSERVER_GROUND_ACTION)
1918 [3] v3s16 nodepos_undersurface
1919 [9] v3s16 nodepos_abovesurface
1924 2: stop digging (all parameters ignored)
1925 3: digging completed
1927 u8 action = readU8(&data[2]);
1929 p_under.X = readS16(&data[3]);
1930 p_under.Y = readS16(&data[5]);
1931 p_under.Z = readS16(&data[7]);
1933 p_over.X = readS16(&data[9]);
1934 p_over.Y = readS16(&data[11]);
1935 p_over.Z = readS16(&data[13]);
1936 u16 item_i = readU16(&data[15]);
1938 //TODO: Check that target is reasonably close
1946 NOTE: This can be used in the future to check if
1947 somebody is cheating, by checking the timing.
1954 else if(action == 2)
1957 RemoteClient *client = getClient(peer->id);
1958 JMutexAutoLock digmutex(client->m_dig_mutex);
1959 client->m_dig_tool_item = -1;
1964 3: Digging completed
1966 else if(action == 3)
1968 // Mandatory parameter; actually used for nothing
1969 core::map<v3s16, MapBlock*> modified_blocks;
1972 u8 mineral = MINERAL_NONE;
1976 MapNode n = m_env.getMap().getNode(p_under);
1977 // Get material at position
1979 // If it's not diggable, do nothing
1980 if(content_diggable(material) == false)
1982 derr_server<<"Server: Not finishing digging: Node not diggable"
1985 // Client probably has wrong data.
1986 // Set block not sent, so that client will get
1988 dstream<<"Client "<<peer_id<<" tried to dig "
1989 <<"node from invalid position; setting"
1990 <<" MapBlock not sent."<<std::endl;
1991 RemoteClient *client = getClient(peer_id);
1992 v3s16 blockpos = getNodeBlockPos(p_under);
1993 client->SetBlockNotSent(blockpos);
1998 mineral = n.getMineral();
2000 catch(InvalidPositionException &e)
2002 derr_server<<"Server: Not finishing digging: Node not found."
2003 <<" Adding block to emerge queue."
2005 m_emerge_queue.addBlock(peer_id,
2006 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2011 Send the removal to all other clients
2013 sendRemoveNode(p_over, peer_id);
2016 Update and send inventory
2019 if(g_settings.getBool("creative_mode") == false)
2024 InventoryList *mlist = player->inventory.getList("main");
2027 InventoryItem *item = mlist->getItem(item_i);
2028 if(item && (std::string)item->getName() == "ToolItem")
2030 ToolItem *titem = (ToolItem*)item;
2031 std::string toolname = titem->getToolName();
2033 // Get digging properties for material and tool
2034 DiggingProperties prop =
2035 getDiggingProperties(material, toolname);
2037 if(prop.diggable == false)
2039 derr_server<<"Server: WARNING: Player digged"
2040 <<" with impossible material + tool"
2041 <<" combination"<<std::endl;
2044 bool weared_out = titem->addWear(prop.wear);
2048 mlist->deleteItem(item_i);
2054 Add dug item to inventory
2057 InventoryItem *item = NULL;
2059 if(mineral != MINERAL_NONE)
2060 item = getDiggedMineralItem(mineral);
2065 std::string &dug_s = content_features(material).dug_item;
2068 std::istringstream is(dug_s, std::ios::binary);
2069 item = InventoryItem::deSerialize(is);
2075 // Add a item to inventory
2076 player->inventory.addItem("main", item);
2079 SendInventory(player->peer_id);
2085 (this takes some time so it is done after the quick stuff)
2087 m_ignore_map_edit_events = true;
2088 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2089 m_ignore_map_edit_events = false;
2095 else if(action == 1)
2098 InventoryList *ilist = player->inventory.getList("main");
2103 InventoryItem *item = ilist->getItem(item_i);
2105 // If there is no item, it is not possible to add it anywhere
2110 Handle material items
2112 if(std::string("MaterialItem") == item->getName())
2115 // Don't add a node if this is not a free space
2116 MapNode n2 = m_env.getMap().getNode(p_over);
2117 if(content_buildable_to(n2.d) == false)
2119 // Client probably has wrong data.
2120 // Set block not sent, so that client will get
2122 dstream<<"Client "<<peer_id<<" tried to place"
2123 <<" node in invalid position; setting"
2124 <<" MapBlock not sent."<<std::endl;
2125 RemoteClient *client = getClient(peer_id);
2126 v3s16 blockpos = getNodeBlockPos(p_over);
2127 client->SetBlockNotSent(blockpos);
2131 catch(InvalidPositionException &e)
2133 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2134 <<" Adding block to emerge queue."
2136 m_emerge_queue.addBlock(peer_id,
2137 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2141 // Reset build time counter
2142 getClient(peer->id)->m_time_from_building = 0.0;
2145 MaterialItem *mitem = (MaterialItem*)item;
2147 n.d = mitem->getMaterial();
2148 if(content_features(n.d).wall_mounted)
2149 n.dir = packDir(p_under - p_over);
2154 sendAddNode(p_over, n, 0);
2159 InventoryList *ilist = player->inventory.getList("main");
2160 if(g_settings.getBool("creative_mode") == false && ilist)
2162 // Remove from inventory and send inventory
2163 if(mitem->getCount() == 1)
2164 ilist->deleteItem(item_i);
2168 SendInventory(peer_id);
2174 This takes some time so it is done after the quick stuff
2176 core::map<v3s16, MapBlock*> modified_blocks;
2177 m_ignore_map_edit_events = true;
2178 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2179 m_ignore_map_edit_events = false;
2182 Calculate special events
2185 /*if(n.d == CONTENT_MESE)
2188 for(s16 z=-1; z<=1; z++)
2189 for(s16 y=-1; y<=1; y++)
2190 for(s16 x=-1; x<=1; x++)
2201 v3s16 blockpos = getNodeBlockPos(p_over);
2203 MapBlock *block = NULL;
2206 block = m_env.getMap().getBlockNoCreate(blockpos);
2208 catch(InvalidPositionException &e)
2210 derr_server<<"Error while placing object: "
2211 "block not found"<<std::endl;
2215 v3s16 block_pos_i_on_map = block->getPosRelative();
2216 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2218 v3f pos = intToFloat(p_over, BS);
2219 pos -= block_pos_f_on_map;
2221 /*dout_server<<"pos="
2222 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2225 MapBlockObject *obj = NULL;
2228 Handle block object items
2230 if(std::string("MBOItem") == item->getName())
2232 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2234 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2235 "inventorystring=\""
2236 <<oitem->getInventoryString()
2237 <<"\""<<std::endl;*/
2239 obj = oitem->createObject
2240 (pos, player->getYaw(), player->getPitch());
2247 dout_server<<"Placing a miscellaneous item on map"
2250 Create an ItemObject that contains the item.
2252 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2253 std::ostringstream os(std::ios_base::binary);
2254 item->serialize(os);
2255 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2256 iobj->setItemString(os.str());
2262 derr_server<<"WARNING: item resulted in NULL object, "
2263 <<"not placing onto map"
2268 block->addObject(obj);
2270 dout_server<<"Placed object"<<std::endl;
2272 InventoryList *ilist = player->inventory.getList("main");
2273 if(g_settings.getBool("creative_mode") == false && ilist)
2275 // Remove from inventory and send inventory
2276 ilist->deleteItem(item_i);
2278 SendInventory(peer_id);
2286 Catch invalid actions
2290 derr_server<<"WARNING: Server: Invalid action "
2291 <<action<<std::endl;
2295 else if(command == TOSERVER_RELEASE)
2304 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2307 else if(command == TOSERVER_SIGNTEXT)
2316 std::string datastring((char*)&data[2], datasize-2);
2317 std::istringstream is(datastring, std::ios_base::binary);
2320 is.read((char*)buf, 6);
2321 v3s16 blockpos = readV3S16(buf);
2322 is.read((char*)buf, 2);
2323 s16 id = readS16(buf);
2324 is.read((char*)buf, 2);
2325 u16 textlen = readU16(buf);
2327 for(u16 i=0; i<textlen; i++)
2329 is.read((char*)buf, 1);
2330 text += (char)buf[0];
2333 MapBlock *block = NULL;
2336 block = m_env.getMap().getBlockNoCreate(blockpos);
2338 catch(InvalidPositionException &e)
2340 derr_server<<"Error while setting sign text: "
2341 "block not found"<<std::endl;
2345 MapBlockObject *obj = block->getObject(id);
2348 derr_server<<"Error while setting sign text: "
2349 "object not found"<<std::endl;
2353 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2355 derr_server<<"Error while setting sign text: "
2356 "object is not a sign"<<std::endl;
2360 ((SignObject*)obj)->setText(text);
2362 obj->getBlock()->setChangedFlag();
2364 else if(command == TOSERVER_INVENTORY_ACTION)
2366 /*// Ignore inventory changes if in creative mode
2367 if(g_settings.getBool("creative_mode") == true)
2369 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2373 // Strip command and create a stream
2374 std::string datastring((char*)&data[2], datasize-2);
2375 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2376 std::istringstream is(datastring, std::ios_base::binary);
2378 InventoryAction *a = InventoryAction::deSerialize(is);
2382 Handle craftresult specially if not in creative mode
2384 bool disable_action = false;
2385 if(a->getType() == IACTION_MOVE
2386 && g_settings.getBool("creative_mode") == false)
2388 IMoveAction *ma = (IMoveAction*)a;
2389 // Don't allow moving anything to craftresult
2390 if(ma->to_name == "craftresult")
2393 disable_action = true;
2395 // When something is removed from craftresult
2396 if(ma->from_name == "craftresult")
2398 disable_action = true;
2399 // Remove stuff from craft
2400 InventoryList *clist = player->inventory.getList("craft");
2403 u16 count = ma->count;
2406 clist->decrementMaterials(count);
2409 // Feed action to player inventory
2410 a->apply(&player->inventory);
2413 // If something appeared in craftresult, throw it
2415 InventoryList *rlist = player->inventory.getList("craftresult");
2416 InventoryList *mlist = player->inventory.getList("main");
2417 if(rlist && mlist && rlist->getUsedSlots() == 1)
2419 InventoryItem *item1 = rlist->changeItem(0, NULL);
2420 mlist->addItem(item1);
2424 if(disable_action == false)
2426 // Feed action to player inventory
2427 a->apply(&player->inventory);
2432 SendInventory(player->peer_id);
2436 dstream<<"TOSERVER_INVENTORY_ACTION: "
2437 <<"InventoryAction::deSerialize() returned NULL"
2441 else if(command == TOSERVER_CHAT_MESSAGE)
2449 std::string datastring((char*)&data[2], datasize-2);
2450 std::istringstream is(datastring, std::ios_base::binary);
2453 is.read((char*)buf, 2);
2454 u16 len = readU16(buf);
2456 std::wstring message;
2457 for(u16 i=0; i<len; i++)
2459 is.read((char*)buf, 2);
2460 message += (wchar_t)readU16(buf);
2463 // Get player name of this client
2464 std::wstring name = narrow_to_wide(player->getName());
2466 // Line to send to players
2468 // Whether to send to the player that sent the line
2469 bool send_to_sender = false;
2470 // Whether to send to other players
2471 bool send_to_others = false;
2474 std::wstring commandprefix = L"/#";
2475 if(message.substr(0, commandprefix.size()) == commandprefix)
2477 line += L"Server: ";
2479 message = message.substr(commandprefix.size());
2480 // Get player name as narrow string
2481 std::string name_s = player->getName();
2482 // Convert message to narrow string
2483 std::string message_s = wide_to_narrow(message);
2484 // Operator is the single name defined in config.
2485 std::string operator_name = g_settings.get("name");
2486 bool is_operator = (operator_name != "" &&
2487 wide_to_narrow(name) == operator_name);
2488 bool valid_command = false;
2489 if(message_s == "help")
2491 line += L"-!- Available commands: ";
2495 line += L"shutdown setting ";
2500 send_to_sender = true;
2501 valid_command = true;
2503 else if(message_s == "status")
2505 line = getStatusString();
2506 send_to_sender = true;
2507 valid_command = true;
2509 else if(is_operator)
2511 if(message_s == "shutdown")
2513 dstream<<DTIME<<" Server: Operator requested shutdown."
2515 m_shutdown_requested.set(true);
2517 line += L"*** Server shutting down (operator request)";
2518 send_to_sender = true;
2519 valid_command = true;
2521 else if(message_s.substr(0,8) == "setting ")
2523 std::string confline = message_s.substr(8);
2524 g_settings.parseConfigLine(confline);
2525 line += L"-!- Setting changed.";
2526 send_to_sender = true;
2527 valid_command = true;
2531 if(valid_command == false)
2533 line += L"-!- Invalid command: " + message;
2534 send_to_sender = true;
2545 send_to_others = true;
2550 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2553 Send the message to clients
2555 for(core::map<u16, RemoteClient*>::Iterator
2556 i = m_clients.getIterator();
2557 i.atEnd() == false; i++)
2559 // Get client and check that it is valid
2560 RemoteClient *client = i.getNode()->getValue();
2561 assert(client->peer_id == i.getNode()->getKey());
2562 if(client->serialization_version == SER_FMT_VER_INVALID)
2566 bool sender_selected = (peer_id == client->peer_id);
2567 if(sender_selected == true && send_to_sender == false)
2569 if(sender_selected == false && send_to_others == false)
2572 SendChatMessage(client->peer_id, line);
2578 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2579 "unknown command "<<command<<std::endl;
2583 catch(SendFailedException &e)
2585 derr_server<<"Server::ProcessData(): SendFailedException: "
2591 void Server::onMapEditEvent(MapEditEvent *event)
2593 dstream<<"Server::onMapEditEvent()"<<std::endl;
2594 if(m_ignore_map_edit_events)
2596 MapEditEvent *e = event->clone();
2597 m_unsent_map_edit_queue.push_back(e);
2600 core::list<PlayerInfo> Server::getPlayerInfo()
2602 DSTACK(__FUNCTION_NAME);
2603 JMutexAutoLock envlock(m_env_mutex);
2604 JMutexAutoLock conlock(m_con_mutex);
2606 core::list<PlayerInfo> list;
2608 core::list<Player*> players = m_env.getPlayers();
2610 core::list<Player*>::Iterator i;
2611 for(i = players.begin();
2612 i != players.end(); i++)
2616 Player *player = *i;
2619 con::Peer *peer = m_con.GetPeer(player->peer_id);
2620 // Copy info from peer to info struct
2622 info.address = peer->address;
2623 info.avg_rtt = peer->avg_rtt;
2625 catch(con::PeerNotFoundException &e)
2627 // Set dummy peer info
2629 info.address = Address(0,0,0,0,0);
2633 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2634 info.position = player->getPosition();
2636 list.push_back(info);
2643 void Server::peerAdded(con::Peer *peer)
2645 DSTACK(__FUNCTION_NAME);
2646 dout_server<<"Server::peerAdded(): peer->id="
2647 <<peer->id<<std::endl;
2650 c.type = PEER_ADDED;
2651 c.peer_id = peer->id;
2653 m_peer_change_queue.push_back(c);
2656 void Server::deletingPeer(con::Peer *peer, bool timeout)
2658 DSTACK(__FUNCTION_NAME);
2659 dout_server<<"Server::deletingPeer(): peer->id="
2660 <<peer->id<<", timeout="<<timeout<<std::endl;
2663 c.type = PEER_REMOVED;
2664 c.peer_id = peer->id;
2665 c.timeout = timeout;
2666 m_peer_change_queue.push_back(c);
2669 void Server::SendObjectData(float dtime)
2671 DSTACK(__FUNCTION_NAME);
2673 core::map<v3s16, bool> stepped_blocks;
2675 for(core::map<u16, RemoteClient*>::Iterator
2676 i = m_clients.getIterator();
2677 i.atEnd() == false; i++)
2679 u16 peer_id = i.getNode()->getKey();
2680 RemoteClient *client = i.getNode()->getValue();
2681 assert(client->peer_id == peer_id);
2683 if(client->serialization_version == SER_FMT_VER_INVALID)
2686 client->SendObjectData(this, dtime, stepped_blocks);
2690 void Server::SendPlayerInfos()
2692 DSTACK(__FUNCTION_NAME);
2694 //JMutexAutoLock envlock(m_env_mutex);
2696 // Get connected players
2697 core::list<Player*> players = m_env.getPlayers(true);
2699 u32 player_count = players.getSize();
2700 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2702 SharedBuffer<u8> data(datasize);
2703 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2706 core::list<Player*>::Iterator i;
2707 for(i = players.begin();
2708 i != players.end(); i++)
2710 Player *player = *i;
2712 /*dstream<<"Server sending player info for player with "
2713 "peer_id="<<player->peer_id<<std::endl;*/
2715 writeU16(&data[start], player->peer_id);
2716 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2717 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2718 start += 2+PLAYERNAME_SIZE;
2721 //JMutexAutoLock conlock(m_con_mutex);
2724 m_con.SendToAll(0, data, true);
2728 Craft checking system
2746 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2752 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2758 enum ItemSpecType type;
2759 // Only other one of these is used
2765 items: a pointer to an array of 9 pointers to items
2766 specs: a pointer to an array of 9 ItemSpecs
2768 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2770 u16 items_min_x = 100;
2771 u16 items_max_x = 100;
2772 u16 items_min_y = 100;
2773 u16 items_max_y = 100;
2774 for(u16 y=0; y<3; y++)
2775 for(u16 x=0; x<3; x++)
2777 if(items[y*3 + x] == NULL)
2779 if(items_min_x == 100 || x < items_min_x)
2781 if(items_min_y == 100 || y < items_min_y)
2783 if(items_max_x == 100 || x > items_max_x)
2785 if(items_max_y == 100 || y > items_max_y)
2788 // No items at all, just return false
2789 if(items_min_x == 100)
2792 u16 items_w = items_max_x - items_min_x + 1;
2793 u16 items_h = items_max_y - items_min_y + 1;
2795 u16 specs_min_x = 100;
2796 u16 specs_max_x = 100;
2797 u16 specs_min_y = 100;
2798 u16 specs_max_y = 100;
2799 for(u16 y=0; y<3; y++)
2800 for(u16 x=0; x<3; x++)
2802 if(specs[y*3 + x].type == ITEM_NONE)
2804 if(specs_min_x == 100 || x < specs_min_x)
2806 if(specs_min_y == 100 || y < specs_min_y)
2808 if(specs_max_x == 100 || x > specs_max_x)
2810 if(specs_max_y == 100 || y > specs_max_y)
2813 // No specs at all, just return false
2814 if(specs_min_x == 100)
2817 u16 specs_w = specs_max_x - specs_min_x + 1;
2818 u16 specs_h = specs_max_y - specs_min_y + 1;
2821 if(items_w != specs_w || items_h != specs_h)
2824 for(u16 y=0; y<specs_h; y++)
2825 for(u16 x=0; x<specs_w; x++)
2827 u16 items_x = items_min_x + x;
2828 u16 items_y = items_min_y + y;
2829 u16 specs_x = specs_min_x + x;
2830 u16 specs_y = specs_min_y + y;
2831 InventoryItem *item = items[items_y * 3 + items_x];
2832 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2834 if(spec.type == ITEM_NONE)
2836 // Has to be no item
2842 // There should be an item
2846 std::string itemname = item->getName();
2848 if(spec.type == ITEM_MATERIAL)
2850 if(itemname != "MaterialItem")
2852 MaterialItem *mitem = (MaterialItem*)item;
2853 if(mitem->getMaterial() != spec.num)
2856 else if(spec.type == ITEM_CRAFT)
2858 if(itemname != "CraftItem")
2860 CraftItem *mitem = (CraftItem*)item;
2861 if(mitem->getSubName() != spec.name)
2864 else if(spec.type == ITEM_TOOL)
2866 // Not supported yet
2869 else if(spec.type == ITEM_MBO)
2871 // Not supported yet
2876 // Not supported yet
2884 void Server::SendInventory(u16 peer_id)
2886 DSTACK(__FUNCTION_NAME);
2888 Player* player = m_env.getPlayer(peer_id);
2891 Calculate crafting stuff
2893 if(g_settings.getBool("creative_mode") == false)
2895 InventoryList *clist = player->inventory.getList("craft");
2896 InventoryList *rlist = player->inventory.getList("craftresult");
2899 rlist->clearItems();
2903 InventoryItem *items[9];
2904 for(u16 i=0; i<9; i++)
2906 items[i] = clist->getItem(i);
2915 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2916 if(checkItemCombination(items, specs))
2918 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2927 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2928 if(checkItemCombination(items, specs))
2930 rlist->addItem(new CraftItem("Stick", 4));
2939 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2940 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2941 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2942 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2943 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2944 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2945 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2946 if(checkItemCombination(items, specs))
2948 rlist->addItem(new MapBlockObjectItem("Sign"));
2957 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2958 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2959 if(checkItemCombination(items, specs))
2961 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2970 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2971 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2972 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2973 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2974 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2975 if(checkItemCombination(items, specs))
2977 rlist->addItem(new ToolItem("WPick", 0));
2986 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2987 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2988 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2989 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2990 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2991 if(checkItemCombination(items, specs))
2993 rlist->addItem(new ToolItem("STPick", 0));
3002 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3003 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3004 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3005 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3006 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3007 if(checkItemCombination(items, specs))
3009 rlist->addItem(new ToolItem("MesePick", 0));
3014 } // if creative_mode == false
3020 std::ostringstream os;
3021 //os.imbue(std::locale("C"));
3023 player->inventory.serialize(os);
3025 std::string s = os.str();
3027 SharedBuffer<u8> data(s.size()+2);
3028 writeU16(&data[0], TOCLIENT_INVENTORY);
3029 memcpy(&data[2], s.c_str(), s.size());
3032 m_con.Send(peer_id, 0, data, true);
3035 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3037 DSTACK(__FUNCTION_NAME);
3039 std::ostringstream os(std::ios_base::binary);
3043 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3044 os.write((char*)buf, 2);
3047 writeU16(buf, message.size());
3048 os.write((char*)buf, 2);
3051 for(u32 i=0; i<message.size(); i++)
3055 os.write((char*)buf, 2);
3059 std::string s = os.str();
3060 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3062 m_con.Send(peer_id, 0, data, true);
3065 void Server::BroadcastChatMessage(const std::wstring &message)
3067 for(core::map<u16, RemoteClient*>::Iterator
3068 i = m_clients.getIterator();
3069 i.atEnd() == false; i++)
3071 // Get client and check that it is valid
3072 RemoteClient *client = i.getNode()->getValue();
3073 assert(client->peer_id == i.getNode()->getKey());
3074 if(client->serialization_version == SER_FMT_VER_INVALID)
3077 SendChatMessage(client->peer_id, message);
3081 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3085 SharedBuffer<u8> reply(replysize);
3086 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3087 writeS16(&reply[2], p.X);
3088 writeS16(&reply[4], p.Y);
3089 writeS16(&reply[6], p.Z);
3091 for(core::map<u16, RemoteClient*>::Iterator
3092 i = m_clients.getIterator();
3093 i.atEnd() == false; i++)
3095 // Get client and check that it is valid
3096 RemoteClient *client = i.getNode()->getValue();
3097 assert(client->peer_id == i.getNode()->getKey());
3098 if(client->serialization_version == SER_FMT_VER_INVALID)
3101 // Don't send if it's the same one
3102 if(client->peer_id == ignore_id)
3106 m_con.Send(client->peer_id, 0, reply, true);
3110 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3112 for(core::map<u16, RemoteClient*>::Iterator
3113 i = m_clients.getIterator();
3114 i.atEnd() == false; i++)
3116 // Get client and check that it is valid
3117 RemoteClient *client = i.getNode()->getValue();
3118 assert(client->peer_id == i.getNode()->getKey());
3119 if(client->serialization_version == SER_FMT_VER_INVALID)
3122 // Don't send if it's the same one
3123 if(client->peer_id == ignore_id)
3127 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3128 SharedBuffer<u8> reply(replysize);
3129 writeU16(&reply[0], TOCLIENT_ADDNODE);
3130 writeS16(&reply[2], p.X);
3131 writeS16(&reply[4], p.Y);
3132 writeS16(&reply[6], p.Z);
3133 n.serialize(&reply[8], client->serialization_version);
3136 m_con.Send(client->peer_id, 0, reply, true);
3140 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3142 DSTACK(__FUNCTION_NAME);
3144 Create a packet with the block in the right format
3147 std::ostringstream os(std::ios_base::binary);
3148 block->serialize(os, ver);
3149 std::string s = os.str();
3150 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3152 u32 replysize = 8 + blockdata.getSize();
3153 SharedBuffer<u8> reply(replysize);
3154 v3s16 p = block->getPos();
3155 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3156 writeS16(&reply[2], p.X);
3157 writeS16(&reply[4], p.Y);
3158 writeS16(&reply[6], p.Z);
3159 memcpy(&reply[8], *blockdata, blockdata.getSize());
3161 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3162 <<": \tpacket size: "<<replysize<<std::endl;*/
3167 m_con.Send(peer_id, 1, reply, true);
3170 void Server::SendBlocks(float dtime)
3172 DSTACK(__FUNCTION_NAME);
3174 JMutexAutoLock envlock(m_env_mutex);
3175 JMutexAutoLock conlock(m_con_mutex);
3177 //TimeTaker timer("Server::SendBlocks");
3179 core::array<PrioritySortedBlockTransfer> queue;
3181 s32 total_sending = 0;
3183 for(core::map<u16, RemoteClient*>::Iterator
3184 i = m_clients.getIterator();
3185 i.atEnd() == false; i++)
3187 RemoteClient *client = i.getNode()->getValue();
3188 assert(client->peer_id == i.getNode()->getKey());
3190 total_sending += client->SendingCount();
3192 if(client->serialization_version == SER_FMT_VER_INVALID)
3195 client->GetNextBlocks(this, dtime, queue);
3199 // Lowest priority number comes first.
3200 // Lowest is most important.
3203 for(u32 i=0; i<queue.size(); i++)
3205 //TODO: Calculate limit dynamically
3206 if(total_sending >= g_settings.getS32
3207 ("max_simultaneous_block_sends_server_total"))
3210 PrioritySortedBlockTransfer q = queue[i];
3212 MapBlock *block = NULL;
3215 block = m_env.getMap().getBlockNoCreate(q.pos);
3217 catch(InvalidPositionException &e)
3222 RemoteClient *client = getClient(q.peer_id);
3224 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3226 client->SentBlock(q.pos);
3233 RemoteClient* Server::getClient(u16 peer_id)
3235 DSTACK(__FUNCTION_NAME);
3236 //JMutexAutoLock lock(m_con_mutex);
3237 core::map<u16, RemoteClient*>::Node *n;
3238 n = m_clients.find(peer_id);
3239 // A client should exist for all peers
3241 return n->getValue();
3244 std::wstring Server::getStatusString()
3246 std::wostringstream os(std::ios_base::binary);
3249 os<<L"uptime="<<m_uptime.get();
3250 // Information about clients
3252 for(core::map<u16, RemoteClient*>::Iterator
3253 i = m_clients.getIterator();
3254 i.atEnd() == false; i++)
3256 // Get client and check that it is valid
3257 RemoteClient *client = i.getNode()->getValue();
3258 assert(client->peer_id == i.getNode()->getKey());
3259 if(client->serialization_version == SER_FMT_VER_INVALID)
3262 Player *player = m_env.getPlayer(client->peer_id);
3263 // Get name of player
3264 std::wstring name = L"unknown";
3266 name = narrow_to_wide(player->getName());
3267 // Add name to information string
3271 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3272 os<<" WARNING: Map saving is disabled."<<std::endl;
3277 void setCreativeInventory(Player *player)
3279 player->resetInventory();
3281 // Give some good picks
3283 InventoryItem *item = new ToolItem("STPick", 0);
3284 void* r = player->inventory.addItem("main", item);
3288 InventoryItem *item = new ToolItem("MesePick", 0);
3289 void* r = player->inventory.addItem("main", item);
3296 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3299 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3300 player->inventory.addItem("main", item);
3303 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3305 // Skip some materials
3306 if(i == CONTENT_WATER || i == CONTENT_TORCH
3307 || i == CONTENT_COALSTONE)
3310 InventoryItem *item = new MaterialItem(i, 1);
3311 player->inventory.addItem("main", item);
3315 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3316 void* r = player->inventory.addItem("main", item);
3321 Player *Server::emergePlayer(const char *name, const char *password,
3325 Try to get an existing player
3327 Player *player = m_env.getPlayer(name);
3330 // If player is already connected, cancel
3331 if(player->peer_id != 0)
3333 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3338 player->peer_id = peer_id;
3340 // Reset inventory to creative if in creative mode
3341 if(g_settings.getBool("creative_mode"))
3343 setCreativeInventory(player);
3350 If player with the wanted peer_id already exists, cancel.
3352 if(m_env.getPlayer(peer_id) != NULL)
3354 dstream<<"emergePlayer(): Player with wrong name but same"
3355 " peer_id already exists"<<std::endl;
3363 player = new ServerRemotePlayer();
3364 //player->peer_id = c.peer_id;
3365 //player->peer_id = PEER_ID_INEXISTENT;
3366 player->peer_id = peer_id;
3367 player->updateName(name);
3373 dstream<<"Server: Finding spawn place for player \""
3374 <<player->getName()<<"\""<<std::endl;
3378 player->setPosition(intToFloat(v3s16(
3385 s16 groundheight = 0;
3387 // Try to find a good place a few times
3388 for(s32 i=0; i<500; i++)
3391 // We're going to try to throw the player to this position
3392 nodepos = v2s16(-range + (myrand()%(range*2)),
3393 -range + (myrand()%(range*2)));
3394 v2s16 sectorpos = getNodeSectorPos(nodepos);
3396 Ignore position if it is near a chunk edge.
3397 Otherwise it would cause excessive loading time at
3401 if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1))
3402 != m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1)))
3406 m_env.getMap().emergeSector(sectorpos);
3407 // Get ground height at point
3408 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3409 // Don't go underwater
3410 if(groundheight < WATER_LEVEL)
3412 //dstream<<"-> Underwater"<<std::endl;
3415 #if 0 // Doesn't work, generating blocks is a bit too complicated for doing here
3416 // Get block at point
3418 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3419 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3420 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3421 // Don't go inside ground
3423 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3424 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3425 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3426 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3427 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3428 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3430 dstream<<"-> Inside ground"<<std::endl;
3434 }catch(InvalidPositionException &e)
3436 dstream<<"-> Invalid position"<<std::endl;
3437 // Ignore invalid position
3441 // Found a good place
3442 dstream<<"Searched through "<<i<<" places."<<std::endl;
3447 // If no suitable place was not found, go above water at least.
3448 if(groundheight < WATER_LEVEL)
3449 groundheight = WATER_LEVEL;
3451 player->setPosition(intToFloat(v3s16(
3459 Add player to environment
3462 m_env.addPlayer(player);
3465 Add stuff to inventory
3468 if(g_settings.getBool("creative_mode"))
3470 setCreativeInventory(player);
3475 InventoryItem *item = new ToolItem("WPick", 32000);
3476 void* r = player->inventory.addItem("main", item);
3480 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3481 void* r = player->inventory.addItem("main", item);
3485 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3486 void* r = player->inventory.addItem("main", item);
3490 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3491 void* r = player->inventory.addItem("main", item);
3495 InventoryItem *item = new CraftItem("Stick", 4);
3496 void* r = player->inventory.addItem("main", item);
3500 InventoryItem *item = new ToolItem("WPick", 32000);
3501 void* r = player->inventory.addItem("main", item);
3505 InventoryItem *item = new ToolItem("STPick", 32000);
3506 void* r = player->inventory.addItem("main", item);
3509 /*// Give some lights
3511 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3512 bool r = player->inventory.addItem("main", item);
3516 for(u16 i=0; i<4; i++)
3518 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3519 bool r = player->inventory.addItem("main", item);
3522 /*// Give some other stuff
3524 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3525 bool r = player->inventory.addItem("main", item);
3532 } // create new player
3536 void Server::UpdateBlockWaterPressure(MapBlock *block,
3537 core::map<v3s16, MapBlock*> &modified_blocks)
3539 MapVoxelManipulator v(&m_env.getMap());
3540 v.m_disable_water_climb =
3541 g_settings.getBool("disable_water_climb");
3543 VoxelArea area(block->getPosRelative(),
3544 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3548 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3550 catch(ProcessingLimitException &e)
3552 dstream<<"Processing limit reached (1)"<<std::endl;
3555 v.blitBack(modified_blocks);
3559 void Server::handlePeerChange(PeerChange &c)
3561 JMutexAutoLock envlock(m_env_mutex);
3562 JMutexAutoLock conlock(m_con_mutex);
3564 if(c.type == PEER_ADDED)
3571 core::map<u16, RemoteClient*>::Node *n;
3572 n = m_clients.find(c.peer_id);
3573 // The client shouldn't already exist
3577 RemoteClient *client = new RemoteClient();
3578 client->peer_id = c.peer_id;
3579 m_clients.insert(client->peer_id, client);
3582 else if(c.type == PEER_REMOVED)
3589 core::map<u16, RemoteClient*>::Node *n;
3590 n = m_clients.find(c.peer_id);
3591 // The client should exist
3594 // Collect information about leaving in chat
3595 std::wstring message;
3597 std::wstring name = L"unknown";
3598 Player *player = m_env.getPlayer(c.peer_id);
3600 name = narrow_to_wide(player->getName());
3604 message += L" left game";
3606 message += L" (timed out)";
3611 m_env.removePlayer(c.peer_id);
3614 // Set player client disconnected
3616 Player *player = m_env.getPlayer(c.peer_id);
3618 player->peer_id = 0;
3622 delete m_clients[c.peer_id];
3623 m_clients.remove(c.peer_id);
3625 // Send player info to all remaining clients
3628 // Send leave chat message to all remaining clients
3629 BroadcastChatMessage(message);
3638 void Server::handlePeerChanges()
3640 while(m_peer_change_queue.size() > 0)
3642 PeerChange c = m_peer_change_queue.pop_front();
3644 dout_server<<"Server: Handling peer change: "
3645 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3648 handlePeerChange(c);
3652 void dedicated_server_loop(Server &server, bool &kill)
3654 DSTACK(__FUNCTION_NAME);
3656 std::cout<<DTIME<<std::endl;
3657 std::cout<<"========================"<<std::endl;
3658 std::cout<<"Running dedicated server"<<std::endl;
3659 std::cout<<"========================"<<std::endl;
3660 std::cout<<std::endl;
3664 // This is kind of a hack but can be done like this
3665 // because server.step() is very light
3669 if(server.getShutdownRequested() || kill)
3671 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3675 static int counter = 0;
3681 core::list<PlayerInfo> list = server.getPlayerInfo();
3682 core::list<PlayerInfo>::Iterator i;
3683 static u32 sum_old = 0;
3684 u32 sum = PIChecksum(list);
3687 std::cout<<DTIME<<"Player info:"<<std::endl;
3688 for(i=list.begin(); i!=list.end(); i++)
3690 i->PrintLine(&std::cout);