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;
554 If block has been marked to not exist on disk (dummy)
555 and generating new ones is not wanted, skip block.
557 if(generate == false && surely_not_found_on_disk == true)
564 Record the lowest d from which a a block has been
565 found being not sent and possibly to exist
567 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
569 new_nearest_unsent_d = d;
573 Add inexistent block to emerge queue.
575 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
577 //TODO: Get value from somewhere
578 // Allow only one block in emerge queue
579 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
580 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
582 //dstream<<"Adding block to emerge queue"<<std::endl;
584 // Add it to the emerge queue and trigger the thread
587 if(generate == false)
588 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
590 server->m_emerge_queue.addBlock(peer_id, p, flags);
591 server->m_emergethread.trigger();
599 Add block to send queue
602 PrioritySortedBlockTransfer q((float)d, p, peer_id);
606 num_blocks_selected += 1;
611 if(new_nearest_unsent_d != -1)
613 m_nearest_unsent_d = new_nearest_unsent_d;
617 void RemoteClient::SendObjectData(
620 core::map<v3s16, bool> &stepped_blocks
623 DSTACK(__FUNCTION_NAME);
625 // Can't send anything without knowing version
626 if(serialization_version == SER_FMT_VER_INVALID)
628 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
634 Send a TOCLIENT_OBJECTDATA packet.
638 u16 number of player positions
649 std::ostringstream os(std::ios_base::binary);
653 writeU16(buf, TOCLIENT_OBJECTDATA);
654 os.write((char*)buf, 2);
657 Get and write player data
660 // Get connected players
661 core::list<Player*> players = server->m_env.getPlayers(true);
663 // Write player count
664 u16 playercount = players.size();
665 writeU16(buf, playercount);
666 os.write((char*)buf, 2);
668 core::list<Player*>::Iterator i;
669 for(i = players.begin();
670 i != players.end(); i++)
674 v3f pf = player->getPosition();
675 v3f sf = player->getSpeed();
677 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
678 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
679 s32 pitch_i (player->getPitch() * 100);
680 s32 yaw_i (player->getYaw() * 100);
682 writeU16(buf, player->peer_id);
683 os.write((char*)buf, 2);
684 writeV3S32(buf, position_i);
685 os.write((char*)buf, 12);
686 writeV3S32(buf, speed_i);
687 os.write((char*)buf, 12);
688 writeS32(buf, pitch_i);
689 os.write((char*)buf, 4);
690 writeS32(buf, yaw_i);
691 os.write((char*)buf, 4);
695 Get and write object data
701 For making players to be able to build to their nearby
702 environment (building is not possible on blocks that are not
705 - Add blocks to emerge queue if they are not found
707 SUGGESTION: These could be ignored from the backside of the player
710 Player *player = server->m_env.getPlayer(peer_id);
714 v3f playerpos = player->getPosition();
715 v3f playerspeed = player->getSpeed();
717 v3s16 center_nodepos = floatToInt(playerpos, BS);
718 v3s16 center = getNodeBlockPos(center_nodepos);
720 s16 d_max = g_settings.getS16("active_object_range");
722 // Number of blocks whose objects were written to bos
725 std::ostringstream bos(std::ios_base::binary);
727 for(s16 d = 0; d <= d_max; d++)
729 core::list<v3s16> list;
730 getFacePositions(list, d);
732 core::list<v3s16>::Iterator li;
733 for(li=list.begin(); li!=list.end(); li++)
735 v3s16 p = *li + center;
738 Ignore blocks that haven't been sent to the client
741 if(m_blocks_sent.find(p) == NULL)
745 // Try stepping block and add it to a send queue
750 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
753 Step block if not in stepped_blocks and add to stepped_blocks.
755 if(stepped_blocks.find(p) == NULL)
757 block->stepObjects(dtime, true, server->getDayNightRatio());
758 stepped_blocks.insert(p, true);
759 block->setChangedFlag();
762 // Skip block if there are no objects
763 if(block->getObjectCount() == 0)
772 bos.write((char*)buf, 6);
775 block->serializeObjects(bos, serialization_version);
780 Stop collecting objects if data is already too big
782 // Sum of player and object data sizes
783 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
784 // break out if data too big
785 if(sum > MAX_OBJECTDATA_SIZE)
787 goto skip_subsequent;
791 catch(InvalidPositionException &e)
794 // Add it to the emerge queue and trigger the thread.
795 // Fetch the block only if it is on disk.
797 // Grab and increment counter
798 /*SharedPtr<JMutexAutoLock> lock
799 (m_num_blocks_in_emerge_queue.getLock());
800 m_num_blocks_in_emerge_queue.m_value++;*/
802 // Add to queue as an anonymous fetch from disk
803 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
804 server->m_emerge_queue.addBlock(0, p, flags);
805 server->m_emergethread.trigger();
813 writeU16(buf, blockcount);
814 os.write((char*)buf, 2);
816 // Write block objects
823 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
826 std::string s = os.str();
827 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
828 // Send as unreliable
829 server->m_con.Send(peer_id, 0, data, false);
832 void RemoteClient::GotBlock(v3s16 p)
834 if(m_blocks_sending.find(p) != NULL)
835 m_blocks_sending.remove(p);
838 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
839 " m_blocks_sending"<<std::endl;*/
840 m_excess_gotblocks++;
842 m_blocks_sent.insert(p, true);
845 void RemoteClient::SentBlock(v3s16 p)
847 if(m_blocks_sending.find(p) == NULL)
848 m_blocks_sending.insert(p, 0.0);
850 dstream<<"RemoteClient::SentBlock(): Sent block"
851 " already in m_blocks_sending"<<std::endl;
854 void RemoteClient::SetBlockNotSent(v3s16 p)
856 m_nearest_unsent_d = 0;
858 if(m_blocks_sending.find(p) != NULL)
859 m_blocks_sending.remove(p);
860 if(m_blocks_sent.find(p) != NULL)
861 m_blocks_sent.remove(p);
864 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
866 m_nearest_unsent_d = 0;
868 for(core::map<v3s16, MapBlock*>::Iterator
869 i = blocks.getIterator();
870 i.atEnd()==false; i++)
872 v3s16 p = i.getNode()->getKey();
874 if(m_blocks_sending.find(p) != NULL)
875 m_blocks_sending.remove(p);
876 if(m_blocks_sent.find(p) != NULL)
877 m_blocks_sent.remove(p);
885 PlayerInfo::PlayerInfo()
890 void PlayerInfo::PrintLine(std::ostream *s)
893 (*s)<<"\""<<name<<"\" ("
894 <<(position.X/10)<<","<<(position.Y/10)
895 <<","<<(position.Z/10)<<") ";
897 (*s)<<" avg_rtt="<<avg_rtt;
901 u32 PIChecksum(core::list<PlayerInfo> &l)
903 core::list<PlayerInfo>::Iterator i;
906 for(i=l.begin(); i!=l.end(); i++)
908 checksum += a * (i->id+1);
909 checksum ^= 0x435aafcd;
920 std::string mapsavedir
922 m_env(new ServerMap(mapsavedir), this),
923 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
925 m_emergethread(this),
928 m_time_of_day_send_timer(0),
930 m_mapsavedir(mapsavedir),
931 m_shutdown_requested(false),
932 m_ignore_map_edit_events(false),
933 m_ignore_map_edit_events_peer_id(0)
935 m_liquid_transform_timer = 0.0;
936 m_print_info_timer = 0.0;
937 m_objectdata_timer = 0.0;
938 m_emergethread_trigger_timer = 0.0;
939 m_savemap_timer = 0.0;
943 m_step_dtime_mutex.Init();
946 m_env.getMap().addEventReceiver(this);
949 m_env.deSerializePlayers(m_mapsavedir);
955 Send shutdown message
958 JMutexAutoLock conlock(m_con_mutex);
960 std::wstring line = L"*** Server shutting down";
963 Send the message to clients
965 for(core::map<u16, RemoteClient*>::Iterator
966 i = m_clients.getIterator();
967 i.atEnd() == false; i++)
969 // Get client and check that it is valid
970 RemoteClient *client = i.getNode()->getValue();
971 assert(client->peer_id == i.getNode()->getKey());
972 if(client->serialization_version == SER_FMT_VER_INVALID)
975 SendChatMessage(client->peer_id, line);
982 m_env.serializePlayers(m_mapsavedir);
993 JMutexAutoLock clientslock(m_con_mutex);
995 for(core::map<u16, RemoteClient*>::Iterator
996 i = m_clients.getIterator();
997 i.atEnd() == false; i++)
1000 // NOTE: These are removed by env destructor
1002 u16 peer_id = i.getNode()->getKey();
1003 JMutexAutoLock envlock(m_env_mutex);
1004 m_env.removePlayer(peer_id);
1008 delete i.getNode()->getValue();
1013 void Server::start(unsigned short port)
1015 DSTACK(__FUNCTION_NAME);
1016 // Stop thread if already running
1019 // Initialize connection
1020 m_con.setTimeoutMs(30);
1024 m_thread.setRun(true);
1027 dout_server<<"Server: Started on port "<<port<<std::endl;
1032 DSTACK(__FUNCTION_NAME);
1034 // Stop threads (set run=false first so both start stopping)
1035 m_thread.setRun(false);
1036 m_emergethread.setRun(false);
1038 m_emergethread.stop();
1040 dout_server<<"Server: Threads stopped"<<std::endl;
1042 dout_server<<"Server: Saving players"<<std::endl;
1044 // FIXME: Apparently this does not do anything here
1045 //m_env.serializePlayers(m_mapsavedir);
1048 void Server::step(float dtime)
1050 DSTACK(__FUNCTION_NAME);
1055 JMutexAutoLock lock(m_step_dtime_mutex);
1056 m_step_dtime += dtime;
1060 void Server::AsyncRunStep()
1062 DSTACK(__FUNCTION_NAME);
1066 JMutexAutoLock lock1(m_step_dtime_mutex);
1067 dtime = m_step_dtime;
1070 // Send blocks to clients
1076 //dstream<<"Server steps "<<dtime<<std::endl;
1077 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1080 JMutexAutoLock lock1(m_step_dtime_mutex);
1081 m_step_dtime -= dtime;
1088 m_uptime.set(m_uptime.get() + dtime);
1092 Update m_time_of_day
1095 m_time_counter += dtime;
1096 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1097 u32 units = (u32)(m_time_counter*speed);
1098 m_time_counter -= (f32)units / speed;
1099 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1101 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1104 Send to clients at constant intervals
1107 m_time_of_day_send_timer -= dtime;
1108 if(m_time_of_day_send_timer < 0.0)
1110 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1112 //JMutexAutoLock envlock(m_env_mutex);
1113 JMutexAutoLock conlock(m_con_mutex);
1115 for(core::map<u16, RemoteClient*>::Iterator
1116 i = m_clients.getIterator();
1117 i.atEnd() == false; i++)
1119 RemoteClient *client = i.getNode()->getValue();
1120 //Player *player = m_env.getPlayer(client->peer_id);
1122 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1123 m_time_of_day.get());
1125 m_con.Send(client->peer_id, 0, data, true);
1131 // Process connection's timeouts
1132 JMutexAutoLock lock2(m_con_mutex);
1133 m_con.RunTimeouts(dtime);
1137 // This has to be called so that the client list gets synced
1138 // with the peer list of the connection
1139 handlePeerChanges();
1144 // This also runs Map's timers
1145 JMutexAutoLock lock(m_env_mutex);
1156 m_liquid_transform_timer += dtime;
1157 if(m_liquid_transform_timer >= 1.00)
1159 m_liquid_transform_timer -= 1.00;
1161 JMutexAutoLock lock(m_env_mutex);
1163 core::map<v3s16, MapBlock*> modified_blocks;
1164 m_env.getMap().transformLiquids(modified_blocks);
1169 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1170 ServerMap &map = ((ServerMap&)m_env.getMap());
1171 map.updateLighting(modified_blocks, lighting_modified_blocks);
1173 // Add blocks modified by lighting to modified_blocks
1174 for(core::map<v3s16, MapBlock*>::Iterator
1175 i = lighting_modified_blocks.getIterator();
1176 i.atEnd() == false; i++)
1178 MapBlock *block = i.getNode()->getValue();
1179 modified_blocks.insert(block->getPos(), block);
1183 Set the modified blocks unsent for all the clients
1186 JMutexAutoLock lock2(m_con_mutex);
1188 for(core::map<u16, RemoteClient*>::Iterator
1189 i = m_clients.getIterator();
1190 i.atEnd() == false; i++)
1192 RemoteClient *client = i.getNode()->getValue();
1194 if(modified_blocks.size() > 0)
1196 // Remove block from sent history
1197 client->SetBlocksNotSent(modified_blocks);
1202 // Periodically print some info
1204 float &counter = m_print_info_timer;
1210 JMutexAutoLock lock2(m_con_mutex);
1212 for(core::map<u16, RemoteClient*>::Iterator
1213 i = m_clients.getIterator();
1214 i.atEnd() == false; i++)
1216 //u16 peer_id = i.getNode()->getKey();
1217 RemoteClient *client = i.getNode()->getValue();
1218 client->PrintInfo(std::cout);
1223 if(g_settings.getBool("enable_experimental"))
1227 Check added and deleted active objects
1230 JMutexAutoLock envlock(m_env_mutex);
1231 JMutexAutoLock conlock(m_con_mutex);
1233 // Radius inside which objects are active
1236 for(core::map<u16, RemoteClient*>::Iterator
1237 i = m_clients.getIterator();
1238 i.atEnd() == false; i++)
1240 RemoteClient *client = i.getNode()->getValue();
1241 Player *player = m_env.getPlayer(client->peer_id);
1244 v3s16 pos = floatToInt(player->getPosition(), BS);
1246 core::map<u16, bool> removed_objects;
1247 core::map<u16, bool> added_objects;
1248 m_env.getRemovedActiveObjects(pos, radius,
1249 client->m_known_objects, removed_objects);
1250 m_env.getAddedActiveObjects(pos, radius,
1251 client->m_known_objects, added_objects);
1253 // Ignore if nothing happened
1254 if(removed_objects.size() == 0 && added_objects.size() == 0)
1257 std::string data_buffer;
1261 // Handle removed objects
1262 writeU16((u8*)buf, removed_objects.size());
1263 data_buffer.append(buf, 2);
1264 for(core::map<u16, bool>::Iterator
1265 i = removed_objects.getIterator();
1266 i.atEnd()==false; i++)
1269 u16 id = i.getNode()->getKey();
1270 ServerActiveObject* obj = m_env.getActiveObject(id);
1272 // Add to data buffer for sending
1273 writeU16((u8*)buf, i.getNode()->getKey());
1274 data_buffer.append(buf, 2);
1276 // Remove from known objects
1277 client->m_known_objects.remove(i.getNode()->getKey());
1279 if(obj && obj->m_known_by_count > 0)
1280 obj->m_known_by_count--;
1283 // Handle added objects
1284 writeU16((u8*)buf, added_objects.size());
1285 data_buffer.append(buf, 2);
1286 for(core::map<u16, bool>::Iterator
1287 i = added_objects.getIterator();
1288 i.atEnd()==false; i++)
1291 u16 id = i.getNode()->getKey();
1292 ServerActiveObject* obj = m_env.getActiveObject(id);
1295 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1297 dstream<<"WARNING: "<<__FUNCTION_NAME
1298 <<": NULL object"<<std::endl;
1300 type = obj->getType();
1302 // Add to data buffer for sending
1303 writeU16((u8*)buf, id);
1304 data_buffer.append(buf, 2);
1305 writeU8((u8*)buf, type);
1306 data_buffer.append(buf, 1);
1308 data_buffer.append(serializeLongString(
1309 obj->getClientInitializationData()));
1311 // Add to known objects
1312 client->m_known_objects.insert(i.getNode()->getKey(), false);
1315 obj->m_known_by_count++;
1319 SharedBuffer<u8> reply(2 + data_buffer.size());
1320 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1321 memcpy((char*)&reply[2], data_buffer.c_str(),
1322 data_buffer.size());
1324 m_con.Send(client->peer_id, 0, reply, true);
1326 dstream<<"INFO: Server: Sent object remove/add: "
1327 <<removed_objects.size()<<" removed, "
1328 <<added_objects.size()<<" added, "
1329 <<"packet size is "<<reply.getSize()<<std::endl;
1334 Send object messages
1337 JMutexAutoLock envlock(m_env_mutex);
1338 JMutexAutoLock conlock(m_con_mutex);
1341 // Value = data sent by object
1342 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1344 // Get active object messages from environment
1347 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1351 core::list<ActiveObjectMessage>* message_list = NULL;
1352 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1353 n = buffered_messages.find(aom.id);
1356 message_list = new core::list<ActiveObjectMessage>;
1357 buffered_messages.insert(aom.id, message_list);
1361 message_list = n->getValue();
1363 message_list->push_back(aom);
1366 // Route data to every client
1367 for(core::map<u16, RemoteClient*>::Iterator
1368 i = m_clients.getIterator();
1369 i.atEnd()==false; i++)
1371 RemoteClient *client = i.getNode()->getValue();
1372 std::string reliable_data;
1373 std::string unreliable_data;
1374 // Go through all objects in message buffer
1375 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1376 j = buffered_messages.getIterator();
1377 j.atEnd()==false; j++)
1379 // If object is not known by client, skip it
1380 u16 id = j.getNode()->getKey();
1381 if(client->m_known_objects.find(id) == NULL)
1383 // Get message list of object
1384 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1385 // Go through every message
1386 for(core::list<ActiveObjectMessage>::Iterator
1387 k = list->begin(); k != list->end(); k++)
1389 // Compose the full new data with header
1390 ActiveObjectMessage aom = *k;
1391 std::string new_data;
1394 writeU16((u8*)&buf[0], aom.id);
1395 new_data.append(buf, 2);
1397 new_data += serializeString(aom.datastring);
1398 // Add data to buffer
1400 reliable_data += new_data;
1402 unreliable_data += new_data;
1406 reliable_data and unreliable_data are now ready.
1409 if(reliable_data.size() > 0)
1411 SharedBuffer<u8> reply(2 + reliable_data.size());
1412 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1413 memcpy((char*)&reply[2], reliable_data.c_str(),
1414 reliable_data.size());
1416 m_con.Send(client->peer_id, 0, reply, true);
1418 if(unreliable_data.size() > 0)
1420 SharedBuffer<u8> reply(2 + unreliable_data.size());
1421 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1422 memcpy((char*)&reply[2], unreliable_data.c_str(),
1423 unreliable_data.size());
1424 // Send as unreliable
1425 m_con.Send(client->peer_id, 0, reply, false);
1428 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1430 dstream<<"INFO: Server: Size of object message data: "
1431 <<"reliable: "<<reliable_data.size()
1432 <<", unreliable: "<<unreliable_data.size()
1437 // Clear buffered_messages
1438 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1439 i = buffered_messages.getIterator();
1440 i.atEnd()==false; i++)
1442 delete i.getNode()->getValue();
1446 } // enable_experimental
1449 Send queued-for-sending map edit events.
1452 while(m_unsent_map_edit_queue.size() != 0)
1454 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1456 if(event->type == MEET_ADDNODE)
1458 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1459 sendAddNode(event->p, event->n, event->already_known_by_peer);
1461 else if(event->type == MEET_REMOVENODE)
1463 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1464 sendRemoveNode(event->p, event->already_known_by_peer);
1466 else if(event->type == MEET_OTHER)
1468 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1473 dstream<<"WARNING: Server: Unknown MapEditEvent "
1474 <<((u32)event->type)<<std::endl;
1482 Send object positions
1483 TODO: Get rid of MapBlockObjects
1486 float &counter = m_objectdata_timer;
1488 if(counter >= g_settings.getFloat("objectdata_interval"))
1490 JMutexAutoLock lock1(m_env_mutex);
1491 JMutexAutoLock lock2(m_con_mutex);
1492 SendObjectData(counter);
1499 Trigger emergethread (it somehow gets to a non-triggered but
1500 bysy state sometimes)
1503 float &counter = m_emergethread_trigger_timer;
1509 m_emergethread.trigger();
1515 float &counter = m_savemap_timer;
1517 if(counter >= g_settings.getFloat("server_map_save_interval"))
1521 JMutexAutoLock lock(m_env_mutex);
1523 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1525 // Save only changed parts
1526 m_env.getMap().save(true);
1528 // Delete unused sectors
1529 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1530 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1531 if(deleted_count > 0)
1533 dout_server<<"Server: Unloaded "<<deleted_count
1534 <<" sectors from memory"<<std::endl;
1538 m_env.serializePlayers(m_mapsavedir);
1544 void Server::Receive()
1546 DSTACK(__FUNCTION_NAME);
1547 u32 data_maxsize = 10000;
1548 Buffer<u8> data(data_maxsize);
1553 JMutexAutoLock conlock(m_con_mutex);
1554 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1557 // This has to be called so that the client list gets synced
1558 // with the peer list of the connection
1559 handlePeerChanges();
1561 ProcessData(*data, datasize, peer_id);
1563 catch(con::InvalidIncomingDataException &e)
1565 derr_server<<"Server::Receive(): "
1566 "InvalidIncomingDataException: what()="
1567 <<e.what()<<std::endl;
1569 catch(con::PeerNotFoundException &e)
1571 //NOTE: This is not needed anymore
1573 // The peer has been disconnected.
1574 // Find the associated player and remove it.
1576 /*JMutexAutoLock envlock(m_env_mutex);
1578 dout_server<<"ServerThread: peer_id="<<peer_id
1579 <<" has apparently closed connection. "
1580 <<"Removing player."<<std::endl;
1582 m_env.removePlayer(peer_id);*/
1586 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1588 DSTACK(__FUNCTION_NAME);
1589 // Environment is locked first.
1590 JMutexAutoLock envlock(m_env_mutex);
1591 JMutexAutoLock conlock(m_con_mutex);
1595 peer = m_con.GetPeer(peer_id);
1597 catch(con::PeerNotFoundException &e)
1599 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1600 <<peer_id<<" not found"<<std::endl;
1604 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1612 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1614 if(command == TOSERVER_INIT)
1616 // [0] u16 TOSERVER_INIT
1617 // [2] u8 SER_FMT_VER_HIGHEST
1618 // [3] u8[20] player_name
1623 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1624 <<peer->id<<std::endl;
1626 // First byte after command is maximum supported
1627 // serialization version
1628 u8 client_max = data[2];
1629 u8 our_max = SER_FMT_VER_HIGHEST;
1630 // Use the highest version supported by both
1631 u8 deployed = core::min_(client_max, our_max);
1632 // If it's lower than the lowest supported, give up.
1633 if(deployed < SER_FMT_VER_LOWEST)
1634 deployed = SER_FMT_VER_INVALID;
1636 //peer->serialization_version = deployed;
1637 getClient(peer->id)->pending_serialization_version = deployed;
1639 if(deployed == SER_FMT_VER_INVALID)
1641 derr_server<<DTIME<<"Server: Cannot negotiate "
1642 "serialization version with peer "
1643 <<peer_id<<std::endl;
1652 const u32 playername_size = 20;
1653 char playername[playername_size];
1654 for(u32 i=0; i<playername_size-1; i++)
1656 playername[i] = data[3+i];
1658 playername[playername_size-1] = 0;
1661 Player *player = emergePlayer(playername, "", peer_id);
1662 //Player *player = m_env.getPlayer(peer_id);
1665 // DEBUG: Test serialization
1666 std::ostringstream test_os;
1667 player->serialize(test_os);
1668 dstream<<"Player serialization test: \""<<test_os.str()
1670 std::istringstream test_is(test_os.str());
1671 player->deSerialize(test_is);
1674 // If failed, cancel
1677 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1678 <<": failed to emerge player"<<std::endl;
1683 // If a client is already connected to the player, cancel
1684 if(player->peer_id != 0)
1686 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1687 <<" tried to connect to "
1688 "an already connected player (peer_id="
1689 <<player->peer_id<<")"<<std::endl;
1692 // Set client of player
1693 player->peer_id = peer_id;
1696 // Check if player doesn't exist
1698 throw con::InvalidIncomingDataException
1699 ("Server::ProcessData(): INIT: Player doesn't exist");
1701 /*// update name if it was supplied
1702 if(datasize >= 20+3)
1705 player->updateName((const char*)&data[3]);
1708 // Now answer with a TOCLIENT_INIT
1710 SharedBuffer<u8> reply(2+1+6+8);
1711 writeU16(&reply[0], TOCLIENT_INIT);
1712 writeU8(&reply[2], deployed);
1713 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1714 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1716 m_con.Send(peer_id, 0, reply, true);
1720 if(command == TOSERVER_INIT2)
1722 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1723 <<peer->id<<std::endl;
1726 getClient(peer->id)->serialization_version
1727 = getClient(peer->id)->pending_serialization_version;
1730 Send some initialization data
1733 // Send player info to all players
1736 // Send inventory to player
1737 SendInventory(peer->id);
1741 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1742 m_time_of_day.get());
1743 m_con.Send(peer->id, 0, data, true);
1746 // Send information about server to player in chat
1747 SendChatMessage(peer_id, getStatusString());
1749 // Send information about joining in chat
1751 std::wstring name = L"unknown";
1752 Player *player = m_env.getPlayer(peer_id);
1754 name = narrow_to_wide(player->getName());
1756 std::wstring message;
1759 message += L" joined game";
1760 BroadcastChatMessage(message);
1766 if(peer_ser_ver == SER_FMT_VER_INVALID)
1768 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1769 " serialization format invalid or not initialized."
1770 " Skipping incoming command="<<command<<std::endl;
1774 Player *player = m_env.getPlayer(peer_id);
1777 derr_server<<"Server::ProcessData(): Cancelling: "
1778 "No player for peer_id="<<peer_id
1782 if(command == TOSERVER_PLAYERPOS)
1784 if(datasize < 2+12+12+4+4)
1788 v3s32 ps = readV3S32(&data[start+2]);
1789 v3s32 ss = readV3S32(&data[start+2+12]);
1790 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1791 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1792 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1793 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1794 pitch = wrapDegrees(pitch);
1795 yaw = wrapDegrees(yaw);
1796 player->setPosition(position);
1797 player->setSpeed(speed);
1798 player->setPitch(pitch);
1799 player->setYaw(yaw);
1801 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1802 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1803 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1805 else if(command == TOSERVER_GOTBLOCKS)
1818 u16 count = data[2];
1819 for(u16 i=0; i<count; i++)
1821 if((s16)datasize < 2+1+(i+1)*6)
1822 throw con::InvalidIncomingDataException
1823 ("GOTBLOCKS length is too short");
1824 v3s16 p = readV3S16(&data[2+1+i*6]);
1825 /*dstream<<"Server: GOTBLOCKS ("
1826 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1827 RemoteClient *client = getClient(peer_id);
1828 client->GotBlock(p);
1831 else if(command == TOSERVER_DELETEDBLOCKS)
1844 u16 count = data[2];
1845 for(u16 i=0; i<count; i++)
1847 if((s16)datasize < 2+1+(i+1)*6)
1848 throw con::InvalidIncomingDataException
1849 ("DELETEDBLOCKS length is too short");
1850 v3s16 p = readV3S16(&data[2+1+i*6]);
1851 /*dstream<<"Server: DELETEDBLOCKS ("
1852 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1853 RemoteClient *client = getClient(peer_id);
1854 client->SetBlockNotSent(p);
1857 else if(command == TOSERVER_CLICK_OBJECT)
1864 [2] u8 button (0=left, 1=right)
1869 u8 button = readU8(&data[2]);
1871 p.X = readS16(&data[3]);
1872 p.Y = readS16(&data[5]);
1873 p.Z = readS16(&data[7]);
1874 s16 id = readS16(&data[9]);
1875 //u16 item_i = readU16(&data[11]);
1877 MapBlock *block = NULL;
1880 block = m_env.getMap().getBlockNoCreate(p);
1882 catch(InvalidPositionException &e)
1884 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1888 MapBlockObject *obj = block->getObject(id);
1892 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1896 //TODO: Check that object is reasonably close
1901 InventoryList *ilist = player->inventory.getList("main");
1902 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1905 // Skip if inventory has no free space
1906 if(ilist->getUsedSlots() == ilist->getSize())
1908 dout_server<<"Player inventory has no free space"<<std::endl;
1913 Create the inventory item
1915 InventoryItem *item = NULL;
1916 // If it is an item-object, take the item from it
1917 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1919 item = ((ItemObject*)obj)->createInventoryItem();
1921 // Else create an item of the object
1924 item = new MapBlockObjectItem
1925 (obj->getInventoryString());
1928 // Add to inventory and send inventory
1929 ilist->addItem(item);
1930 SendInventory(player->peer_id);
1933 // Remove from block
1934 block->removeObject(id);
1937 else if(command == TOSERVER_GROUND_ACTION)
1945 [3] v3s16 nodepos_undersurface
1946 [9] v3s16 nodepos_abovesurface
1951 2: stop digging (all parameters ignored)
1952 3: digging completed
1954 u8 action = readU8(&data[2]);
1956 p_under.X = readS16(&data[3]);
1957 p_under.Y = readS16(&data[5]);
1958 p_under.Z = readS16(&data[7]);
1960 p_over.X = readS16(&data[9]);
1961 p_over.Y = readS16(&data[11]);
1962 p_over.Z = readS16(&data[13]);
1963 u16 item_i = readU16(&data[15]);
1965 //TODO: Check that target is reasonably close
1973 NOTE: This can be used in the future to check if
1974 somebody is cheating, by checking the timing.
1981 else if(action == 2)
1984 RemoteClient *client = getClient(peer->id);
1985 JMutexAutoLock digmutex(client->m_dig_mutex);
1986 client->m_dig_tool_item = -1;
1991 3: Digging completed
1993 else if(action == 3)
1995 // Mandatory parameter; actually used for nothing
1996 core::map<v3s16, MapBlock*> modified_blocks;
1999 u8 mineral = MINERAL_NONE;
2003 MapNode n = m_env.getMap().getNode(p_under);
2004 // Get material at position
2006 // If it's not diggable, do nothing
2007 if(content_diggable(material) == false)
2009 derr_server<<"Server: Not finishing digging: Node not diggable"
2012 // Client probably has wrong data.
2013 // Set block not sent, so that client will get
2015 dstream<<"Client "<<peer_id<<" tried to dig "
2016 <<"node from invalid position; setting"
2017 <<" MapBlock not sent."<<std::endl;
2018 RemoteClient *client = getClient(peer_id);
2019 v3s16 blockpos = getNodeBlockPos(p_under);
2020 client->SetBlockNotSent(blockpos);
2025 mineral = n.getMineral();
2027 catch(InvalidPositionException &e)
2029 derr_server<<"Server: Not finishing digging: Node not found."
2030 <<" Adding block to emerge queue."
2032 m_emerge_queue.addBlock(peer_id,
2033 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2038 Send the removal to all other clients
2040 sendRemoveNode(p_under, peer_id);
2043 Update and send inventory
2046 if(g_settings.getBool("creative_mode") == false)
2051 InventoryList *mlist = player->inventory.getList("main");
2054 InventoryItem *item = mlist->getItem(item_i);
2055 if(item && (std::string)item->getName() == "ToolItem")
2057 ToolItem *titem = (ToolItem*)item;
2058 std::string toolname = titem->getToolName();
2060 // Get digging properties for material and tool
2061 DiggingProperties prop =
2062 getDiggingProperties(material, toolname);
2064 if(prop.diggable == false)
2066 derr_server<<"Server: WARNING: Player digged"
2067 <<" with impossible material + tool"
2068 <<" combination"<<std::endl;
2071 bool weared_out = titem->addWear(prop.wear);
2075 mlist->deleteItem(item_i);
2081 Add dug item to inventory
2084 InventoryItem *item = NULL;
2086 if(mineral != MINERAL_NONE)
2087 item = getDiggedMineralItem(mineral);
2092 std::string &dug_s = content_features(material).dug_item;
2095 std::istringstream is(dug_s, std::ios::binary);
2096 item = InventoryItem::deSerialize(is);
2102 // Add a item to inventory
2103 player->inventory.addItem("main", item);
2106 SendInventory(player->peer_id);
2112 (this takes some time so it is done after the quick stuff)
2114 m_ignore_map_edit_events = true;
2115 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2116 m_ignore_map_edit_events = false;
2122 else if(action == 1)
2125 InventoryList *ilist = player->inventory.getList("main");
2130 InventoryItem *item = ilist->getItem(item_i);
2132 // If there is no item, it is not possible to add it anywhere
2137 Handle material items
2139 if(std::string("MaterialItem") == item->getName())
2142 // Don't add a node if this is not a free space
2143 MapNode n2 = m_env.getMap().getNode(p_over);
2144 if(content_buildable_to(n2.d) == false)
2146 // Client probably has wrong data.
2147 // Set block not sent, so that client will get
2149 dstream<<"Client "<<peer_id<<" tried to place"
2150 <<" node in invalid position; setting"
2151 <<" MapBlock not sent."<<std::endl;
2152 RemoteClient *client = getClient(peer_id);
2153 v3s16 blockpos = getNodeBlockPos(p_over);
2154 client->SetBlockNotSent(blockpos);
2158 catch(InvalidPositionException &e)
2160 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2161 <<" Adding block to emerge queue."
2163 m_emerge_queue.addBlock(peer_id,
2164 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2168 // Reset build time counter
2169 getClient(peer->id)->m_time_from_building = 0.0;
2172 MaterialItem *mitem = (MaterialItem*)item;
2174 n.d = mitem->getMaterial();
2175 if(content_features(n.d).wall_mounted)
2176 n.dir = packDir(p_under - p_over);
2181 sendAddNode(p_over, n, 0);
2186 InventoryList *ilist = player->inventory.getList("main");
2187 if(g_settings.getBool("creative_mode") == false && ilist)
2189 // Remove from inventory and send inventory
2190 if(mitem->getCount() == 1)
2191 ilist->deleteItem(item_i);
2195 SendInventory(peer_id);
2201 This takes some time so it is done after the quick stuff
2203 core::map<v3s16, MapBlock*> modified_blocks;
2204 m_ignore_map_edit_events = true;
2205 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2206 m_ignore_map_edit_events = false;
2209 Calculate special events
2212 /*if(n.d == CONTENT_MESE)
2215 for(s16 z=-1; z<=1; z++)
2216 for(s16 y=-1; y<=1; y++)
2217 for(s16 x=-1; x<=1; x++)
2228 v3s16 blockpos = getNodeBlockPos(p_over);
2230 MapBlock *block = NULL;
2233 block = m_env.getMap().getBlockNoCreate(blockpos);
2235 catch(InvalidPositionException &e)
2237 derr_server<<"Error while placing object: "
2238 "block not found"<<std::endl;
2242 v3s16 block_pos_i_on_map = block->getPosRelative();
2243 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
2245 v3f pos = intToFloat(p_over, BS);
2246 pos -= block_pos_f_on_map;
2248 /*dout_server<<"pos="
2249 <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
2252 MapBlockObject *obj = NULL;
2255 Handle block object items
2257 if(std::string("MBOItem") == item->getName())
2259 MapBlockObjectItem *oitem = (MapBlockObjectItem*)item;
2261 /*dout_server<<"Trying to place a MapBlockObjectItem: "
2262 "inventorystring=\""
2263 <<oitem->getInventoryString()
2264 <<"\""<<std::endl;*/
2266 obj = oitem->createObject
2267 (pos, player->getYaw(), player->getPitch());
2274 dout_server<<"Placing a miscellaneous item on map"
2277 Create an ItemObject that contains the item.
2279 ItemObject *iobj = new ItemObject(NULL, -1, pos);
2280 std::ostringstream os(std::ios_base::binary);
2281 item->serialize(os);
2282 dout_server<<"Item string is \""<<os.str()<<"\""<<std::endl;
2283 iobj->setItemString(os.str());
2289 derr_server<<"WARNING: item resulted in NULL object, "
2290 <<"not placing onto map"
2295 block->addObject(obj);
2297 dout_server<<"Placed object"<<std::endl;
2299 InventoryList *ilist = player->inventory.getList("main");
2300 if(g_settings.getBool("creative_mode") == false && ilist)
2302 // Remove from inventory and send inventory
2303 ilist->deleteItem(item_i);
2305 SendInventory(peer_id);
2313 Catch invalid actions
2317 derr_server<<"WARNING: Server: Invalid action "
2318 <<action<<std::endl;
2322 else if(command == TOSERVER_RELEASE)
2331 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2334 else if(command == TOSERVER_SIGNTEXT)
2343 std::string datastring((char*)&data[2], datasize-2);
2344 std::istringstream is(datastring, std::ios_base::binary);
2347 is.read((char*)buf, 6);
2348 v3s16 blockpos = readV3S16(buf);
2349 is.read((char*)buf, 2);
2350 s16 id = readS16(buf);
2351 is.read((char*)buf, 2);
2352 u16 textlen = readU16(buf);
2354 for(u16 i=0; i<textlen; i++)
2356 is.read((char*)buf, 1);
2357 text += (char)buf[0];
2360 MapBlock *block = NULL;
2363 block = m_env.getMap().getBlockNoCreate(blockpos);
2365 catch(InvalidPositionException &e)
2367 derr_server<<"Error while setting sign text: "
2368 "block not found"<<std::endl;
2372 MapBlockObject *obj = block->getObject(id);
2375 derr_server<<"Error while setting sign text: "
2376 "object not found"<<std::endl;
2380 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2382 derr_server<<"Error while setting sign text: "
2383 "object is not a sign"<<std::endl;
2387 ((SignObject*)obj)->setText(text);
2389 obj->getBlock()->setChangedFlag();
2391 else if(command == TOSERVER_INVENTORY_ACTION)
2393 /*// Ignore inventory changes if in creative mode
2394 if(g_settings.getBool("creative_mode") == true)
2396 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2400 // Strip command and create a stream
2401 std::string datastring((char*)&data[2], datasize-2);
2402 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2403 std::istringstream is(datastring, std::ios_base::binary);
2405 InventoryAction *a = InventoryAction::deSerialize(is);
2409 Handle craftresult specially if not in creative mode
2411 bool disable_action = false;
2412 if(a->getType() == IACTION_MOVE
2413 && g_settings.getBool("creative_mode") == false)
2415 IMoveAction *ma = (IMoveAction*)a;
2416 // Don't allow moving anything to craftresult
2417 if(ma->to_name == "craftresult")
2420 disable_action = true;
2422 // When something is removed from craftresult
2423 if(ma->from_name == "craftresult")
2425 disable_action = true;
2426 // Remove stuff from craft
2427 InventoryList *clist = player->inventory.getList("craft");
2430 u16 count = ma->count;
2433 clist->decrementMaterials(count);
2436 // Feed action to player inventory
2437 a->apply(&player->inventory);
2440 // If something appeared in craftresult, throw it
2442 InventoryList *rlist = player->inventory.getList("craftresult");
2443 InventoryList *mlist = player->inventory.getList("main");
2444 if(rlist && mlist && rlist->getUsedSlots() == 1)
2446 InventoryItem *item1 = rlist->changeItem(0, NULL);
2447 mlist->addItem(item1);
2451 if(disable_action == false)
2453 // Feed action to player inventory
2454 a->apply(&player->inventory);
2459 SendInventory(player->peer_id);
2463 dstream<<"TOSERVER_INVENTORY_ACTION: "
2464 <<"InventoryAction::deSerialize() returned NULL"
2468 else if(command == TOSERVER_CHAT_MESSAGE)
2476 std::string datastring((char*)&data[2], datasize-2);
2477 std::istringstream is(datastring, std::ios_base::binary);
2480 is.read((char*)buf, 2);
2481 u16 len = readU16(buf);
2483 std::wstring message;
2484 for(u16 i=0; i<len; i++)
2486 is.read((char*)buf, 2);
2487 message += (wchar_t)readU16(buf);
2490 // Get player name of this client
2491 std::wstring name = narrow_to_wide(player->getName());
2493 // Line to send to players
2495 // Whether to send to the player that sent the line
2496 bool send_to_sender = false;
2497 // Whether to send to other players
2498 bool send_to_others = false;
2501 std::wstring commandprefix = L"/#";
2502 if(message.substr(0, commandprefix.size()) == commandprefix)
2504 line += L"Server: ";
2506 message = message.substr(commandprefix.size());
2507 // Get player name as narrow string
2508 std::string name_s = player->getName();
2509 // Convert message to narrow string
2510 std::string message_s = wide_to_narrow(message);
2511 // Operator is the single name defined in config.
2512 std::string operator_name = g_settings.get("name");
2513 bool is_operator = (operator_name != "" &&
2514 wide_to_narrow(name) == operator_name);
2515 bool valid_command = false;
2516 if(message_s == "help")
2518 line += L"-!- Available commands: ";
2522 line += L"shutdown setting ";
2527 send_to_sender = true;
2528 valid_command = true;
2530 else if(message_s == "status")
2532 line = getStatusString();
2533 send_to_sender = true;
2534 valid_command = true;
2536 else if(is_operator)
2538 if(message_s == "shutdown")
2540 dstream<<DTIME<<" Server: Operator requested shutdown."
2542 m_shutdown_requested.set(true);
2544 line += L"*** Server shutting down (operator request)";
2545 send_to_sender = true;
2546 valid_command = true;
2548 else if(message_s.substr(0,8) == "setting ")
2550 std::string confline = message_s.substr(8);
2551 g_settings.parseConfigLine(confline);
2552 line += L"-!- Setting changed.";
2553 send_to_sender = true;
2554 valid_command = true;
2558 if(valid_command == false)
2560 line += L"-!- Invalid command: " + message;
2561 send_to_sender = true;
2572 send_to_others = true;
2577 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2580 Send the message to clients
2582 for(core::map<u16, RemoteClient*>::Iterator
2583 i = m_clients.getIterator();
2584 i.atEnd() == false; i++)
2586 // Get client and check that it is valid
2587 RemoteClient *client = i.getNode()->getValue();
2588 assert(client->peer_id == i.getNode()->getKey());
2589 if(client->serialization_version == SER_FMT_VER_INVALID)
2593 bool sender_selected = (peer_id == client->peer_id);
2594 if(sender_selected == true && send_to_sender == false)
2596 if(sender_selected == false && send_to_others == false)
2599 SendChatMessage(client->peer_id, line);
2605 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2606 "unknown command "<<command<<std::endl;
2610 catch(SendFailedException &e)
2612 derr_server<<"Server::ProcessData(): SendFailedException: "
2618 void Server::onMapEditEvent(MapEditEvent *event)
2620 dstream<<"Server::onMapEditEvent()"<<std::endl;
2621 if(m_ignore_map_edit_events)
2623 MapEditEvent *e = event->clone();
2624 m_unsent_map_edit_queue.push_back(e);
2627 core::list<PlayerInfo> Server::getPlayerInfo()
2629 DSTACK(__FUNCTION_NAME);
2630 JMutexAutoLock envlock(m_env_mutex);
2631 JMutexAutoLock conlock(m_con_mutex);
2633 core::list<PlayerInfo> list;
2635 core::list<Player*> players = m_env.getPlayers();
2637 core::list<Player*>::Iterator i;
2638 for(i = players.begin();
2639 i != players.end(); i++)
2643 Player *player = *i;
2646 con::Peer *peer = m_con.GetPeer(player->peer_id);
2647 // Copy info from peer to info struct
2649 info.address = peer->address;
2650 info.avg_rtt = peer->avg_rtt;
2652 catch(con::PeerNotFoundException &e)
2654 // Set dummy peer info
2656 info.address = Address(0,0,0,0,0);
2660 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2661 info.position = player->getPosition();
2663 list.push_back(info);
2670 void Server::peerAdded(con::Peer *peer)
2672 DSTACK(__FUNCTION_NAME);
2673 dout_server<<"Server::peerAdded(): peer->id="
2674 <<peer->id<<std::endl;
2677 c.type = PEER_ADDED;
2678 c.peer_id = peer->id;
2680 m_peer_change_queue.push_back(c);
2683 void Server::deletingPeer(con::Peer *peer, bool timeout)
2685 DSTACK(__FUNCTION_NAME);
2686 dout_server<<"Server::deletingPeer(): peer->id="
2687 <<peer->id<<", timeout="<<timeout<<std::endl;
2690 c.type = PEER_REMOVED;
2691 c.peer_id = peer->id;
2692 c.timeout = timeout;
2693 m_peer_change_queue.push_back(c);
2696 void Server::SendObjectData(float dtime)
2698 DSTACK(__FUNCTION_NAME);
2700 core::map<v3s16, bool> stepped_blocks;
2702 for(core::map<u16, RemoteClient*>::Iterator
2703 i = m_clients.getIterator();
2704 i.atEnd() == false; i++)
2706 u16 peer_id = i.getNode()->getKey();
2707 RemoteClient *client = i.getNode()->getValue();
2708 assert(client->peer_id == peer_id);
2710 if(client->serialization_version == SER_FMT_VER_INVALID)
2713 client->SendObjectData(this, dtime, stepped_blocks);
2717 void Server::SendPlayerInfos()
2719 DSTACK(__FUNCTION_NAME);
2721 //JMutexAutoLock envlock(m_env_mutex);
2723 // Get connected players
2724 core::list<Player*> players = m_env.getPlayers(true);
2726 u32 player_count = players.getSize();
2727 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2729 SharedBuffer<u8> data(datasize);
2730 writeU16(&data[0], TOCLIENT_PLAYERINFO);
2733 core::list<Player*>::Iterator i;
2734 for(i = players.begin();
2735 i != players.end(); i++)
2737 Player *player = *i;
2739 /*dstream<<"Server sending player info for player with "
2740 "peer_id="<<player->peer_id<<std::endl;*/
2742 writeU16(&data[start], player->peer_id);
2743 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
2744 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2745 start += 2+PLAYERNAME_SIZE;
2748 //JMutexAutoLock conlock(m_con_mutex);
2751 m_con.SendToAll(0, data, true);
2755 Craft checking system
2773 ItemSpec(enum ItemSpecType a_type, std::string a_name):
2779 ItemSpec(enum ItemSpecType a_type, u16 a_num):
2785 enum ItemSpecType type;
2786 // Only other one of these is used
2792 items: a pointer to an array of 9 pointers to items
2793 specs: a pointer to an array of 9 ItemSpecs
2795 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2797 u16 items_min_x = 100;
2798 u16 items_max_x = 100;
2799 u16 items_min_y = 100;
2800 u16 items_max_y = 100;
2801 for(u16 y=0; y<3; y++)
2802 for(u16 x=0; x<3; x++)
2804 if(items[y*3 + x] == NULL)
2806 if(items_min_x == 100 || x < items_min_x)
2808 if(items_min_y == 100 || y < items_min_y)
2810 if(items_max_x == 100 || x > items_max_x)
2812 if(items_max_y == 100 || y > items_max_y)
2815 // No items at all, just return false
2816 if(items_min_x == 100)
2819 u16 items_w = items_max_x - items_min_x + 1;
2820 u16 items_h = items_max_y - items_min_y + 1;
2822 u16 specs_min_x = 100;
2823 u16 specs_max_x = 100;
2824 u16 specs_min_y = 100;
2825 u16 specs_max_y = 100;
2826 for(u16 y=0; y<3; y++)
2827 for(u16 x=0; x<3; x++)
2829 if(specs[y*3 + x].type == ITEM_NONE)
2831 if(specs_min_x == 100 || x < specs_min_x)
2833 if(specs_min_y == 100 || y < specs_min_y)
2835 if(specs_max_x == 100 || x > specs_max_x)
2837 if(specs_max_y == 100 || y > specs_max_y)
2840 // No specs at all, just return false
2841 if(specs_min_x == 100)
2844 u16 specs_w = specs_max_x - specs_min_x + 1;
2845 u16 specs_h = specs_max_y - specs_min_y + 1;
2848 if(items_w != specs_w || items_h != specs_h)
2851 for(u16 y=0; y<specs_h; y++)
2852 for(u16 x=0; x<specs_w; x++)
2854 u16 items_x = items_min_x + x;
2855 u16 items_y = items_min_y + y;
2856 u16 specs_x = specs_min_x + x;
2857 u16 specs_y = specs_min_y + y;
2858 InventoryItem *item = items[items_y * 3 + items_x];
2859 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2861 if(spec.type == ITEM_NONE)
2863 // Has to be no item
2869 // There should be an item
2873 std::string itemname = item->getName();
2875 if(spec.type == ITEM_MATERIAL)
2877 if(itemname != "MaterialItem")
2879 MaterialItem *mitem = (MaterialItem*)item;
2880 if(mitem->getMaterial() != spec.num)
2883 else if(spec.type == ITEM_CRAFT)
2885 if(itemname != "CraftItem")
2887 CraftItem *mitem = (CraftItem*)item;
2888 if(mitem->getSubName() != spec.name)
2891 else if(spec.type == ITEM_TOOL)
2893 // Not supported yet
2896 else if(spec.type == ITEM_MBO)
2898 // Not supported yet
2903 // Not supported yet
2911 void Server::SendInventory(u16 peer_id)
2913 DSTACK(__FUNCTION_NAME);
2915 Player* player = m_env.getPlayer(peer_id);
2918 Calculate crafting stuff
2920 if(g_settings.getBool("creative_mode") == false)
2922 InventoryList *clist = player->inventory.getList("craft");
2923 InventoryList *rlist = player->inventory.getList("craftresult");
2926 rlist->clearItems();
2930 InventoryItem *items[9];
2931 for(u16 i=0; i<9; i++)
2933 items[i] = clist->getItem(i);
2942 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2943 if(checkItemCombination(items, specs))
2945 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2954 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2955 if(checkItemCombination(items, specs))
2957 rlist->addItem(new CraftItem("Stick", 4));
2966 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2967 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2968 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2969 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2970 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2971 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2972 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2973 if(checkItemCombination(items, specs))
2975 rlist->addItem(new MapBlockObjectItem("Sign"));
2984 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
2985 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2986 if(checkItemCombination(items, specs))
2988 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2997 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2998 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2999 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3000 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3001 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3002 if(checkItemCombination(items, specs))
3004 rlist->addItem(new ToolItem("WPick", 0));
3013 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3014 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3015 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
3016 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3017 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3018 if(checkItemCombination(items, specs))
3020 rlist->addItem(new ToolItem("STPick", 0));
3029 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3030 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3031 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3032 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3033 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3034 if(checkItemCombination(items, specs))
3036 rlist->addItem(new ToolItem("MesePick", 0));
3041 } // if creative_mode == false
3047 std::ostringstream os;
3048 //os.imbue(std::locale("C"));
3050 player->inventory.serialize(os);
3052 std::string s = os.str();
3054 SharedBuffer<u8> data(s.size()+2);
3055 writeU16(&data[0], TOCLIENT_INVENTORY);
3056 memcpy(&data[2], s.c_str(), s.size());
3059 m_con.Send(peer_id, 0, data, true);
3062 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3064 DSTACK(__FUNCTION_NAME);
3066 std::ostringstream os(std::ios_base::binary);
3070 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3071 os.write((char*)buf, 2);
3074 writeU16(buf, message.size());
3075 os.write((char*)buf, 2);
3078 for(u32 i=0; i<message.size(); i++)
3082 os.write((char*)buf, 2);
3086 std::string s = os.str();
3087 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3089 m_con.Send(peer_id, 0, data, true);
3092 void Server::BroadcastChatMessage(const std::wstring &message)
3094 for(core::map<u16, RemoteClient*>::Iterator
3095 i = m_clients.getIterator();
3096 i.atEnd() == false; i++)
3098 // Get client and check that it is valid
3099 RemoteClient *client = i.getNode()->getValue();
3100 assert(client->peer_id == i.getNode()->getKey());
3101 if(client->serialization_version == SER_FMT_VER_INVALID)
3104 SendChatMessage(client->peer_id, message);
3108 void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
3112 SharedBuffer<u8> reply(replysize);
3113 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3114 writeS16(&reply[2], p.X);
3115 writeS16(&reply[4], p.Y);
3116 writeS16(&reply[6], p.Z);
3118 for(core::map<u16, RemoteClient*>::Iterator
3119 i = m_clients.getIterator();
3120 i.atEnd() == false; i++)
3122 // Get client and check that it is valid
3123 RemoteClient *client = i.getNode()->getValue();
3124 assert(client->peer_id == i.getNode()->getKey());
3125 if(client->serialization_version == SER_FMT_VER_INVALID)
3128 // Don't send if it's the same one
3129 if(client->peer_id == ignore_id)
3133 m_con.Send(client->peer_id, 0, reply, true);
3137 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
3139 for(core::map<u16, RemoteClient*>::Iterator
3140 i = m_clients.getIterator();
3141 i.atEnd() == false; i++)
3143 // Get client and check that it is valid
3144 RemoteClient *client = i.getNode()->getValue();
3145 assert(client->peer_id == i.getNode()->getKey());
3146 if(client->serialization_version == SER_FMT_VER_INVALID)
3149 // Don't send if it's the same one
3150 if(client->peer_id == ignore_id)
3154 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3155 SharedBuffer<u8> reply(replysize);
3156 writeU16(&reply[0], TOCLIENT_ADDNODE);
3157 writeS16(&reply[2], p.X);
3158 writeS16(&reply[4], p.Y);
3159 writeS16(&reply[6], p.Z);
3160 n.serialize(&reply[8], client->serialization_version);
3163 m_con.Send(client->peer_id, 0, reply, true);
3167 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3169 DSTACK(__FUNCTION_NAME);
3171 Create a packet with the block in the right format
3174 std::ostringstream os(std::ios_base::binary);
3175 block->serialize(os, ver);
3176 std::string s = os.str();
3177 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3179 u32 replysize = 8 + blockdata.getSize();
3180 SharedBuffer<u8> reply(replysize);
3181 v3s16 p = block->getPos();
3182 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3183 writeS16(&reply[2], p.X);
3184 writeS16(&reply[4], p.Y);
3185 writeS16(&reply[6], p.Z);
3186 memcpy(&reply[8], *blockdata, blockdata.getSize());
3188 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3189 <<": \tpacket size: "<<replysize<<std::endl;*/
3194 m_con.Send(peer_id, 1, reply, true);
3197 void Server::SendBlocks(float dtime)
3199 DSTACK(__FUNCTION_NAME);
3201 JMutexAutoLock envlock(m_env_mutex);
3202 JMutexAutoLock conlock(m_con_mutex);
3204 //TimeTaker timer("Server::SendBlocks");
3206 core::array<PrioritySortedBlockTransfer> queue;
3208 s32 total_sending = 0;
3210 for(core::map<u16, RemoteClient*>::Iterator
3211 i = m_clients.getIterator();
3212 i.atEnd() == false; i++)
3214 RemoteClient *client = i.getNode()->getValue();
3215 assert(client->peer_id == i.getNode()->getKey());
3217 total_sending += client->SendingCount();
3219 if(client->serialization_version == SER_FMT_VER_INVALID)
3222 client->GetNextBlocks(this, dtime, queue);
3226 // Lowest priority number comes first.
3227 // Lowest is most important.
3230 for(u32 i=0; i<queue.size(); i++)
3232 //TODO: Calculate limit dynamically
3233 if(total_sending >= g_settings.getS32
3234 ("max_simultaneous_block_sends_server_total"))
3237 PrioritySortedBlockTransfer q = queue[i];
3239 MapBlock *block = NULL;
3242 block = m_env.getMap().getBlockNoCreate(q.pos);
3244 catch(InvalidPositionException &e)
3249 RemoteClient *client = getClient(q.peer_id);
3251 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3253 client->SentBlock(q.pos);
3260 RemoteClient* Server::getClient(u16 peer_id)
3262 DSTACK(__FUNCTION_NAME);
3263 //JMutexAutoLock lock(m_con_mutex);
3264 core::map<u16, RemoteClient*>::Node *n;
3265 n = m_clients.find(peer_id);
3266 // A client should exist for all peers
3268 return n->getValue();
3271 std::wstring Server::getStatusString()
3273 std::wostringstream os(std::ios_base::binary);
3276 os<<L"uptime="<<m_uptime.get();
3277 // Information about clients
3279 for(core::map<u16, RemoteClient*>::Iterator
3280 i = m_clients.getIterator();
3281 i.atEnd() == false; i++)
3283 // Get client and check that it is valid
3284 RemoteClient *client = i.getNode()->getValue();
3285 assert(client->peer_id == i.getNode()->getKey());
3286 if(client->serialization_version == SER_FMT_VER_INVALID)
3289 Player *player = m_env.getPlayer(client->peer_id);
3290 // Get name of player
3291 std::wstring name = L"unknown";
3293 name = narrow_to_wide(player->getName());
3294 // Add name to information string
3298 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3299 os<<" WARNING: Map saving is disabled."<<std::endl;
3304 void setCreativeInventory(Player *player)
3306 player->resetInventory();
3308 // Give some good picks
3310 InventoryItem *item = new ToolItem("STPick", 0);
3311 void* r = player->inventory.addItem("main", item);
3315 InventoryItem *item = new ToolItem("MesePick", 0);
3316 void* r = player->inventory.addItem("main", item);
3323 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3326 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3327 player->inventory.addItem("main", item);
3330 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3332 // Skip some materials
3333 if(i == CONTENT_WATER || i == CONTENT_TORCH
3334 || i == CONTENT_COALSTONE)
3337 InventoryItem *item = new MaterialItem(i, 1);
3338 player->inventory.addItem("main", item);
3342 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3343 void* r = player->inventory.addItem("main", item);
3348 Player *Server::emergePlayer(const char *name, const char *password,
3352 Try to get an existing player
3354 Player *player = m_env.getPlayer(name);
3357 // If player is already connected, cancel
3358 if(player->peer_id != 0)
3360 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3365 player->peer_id = peer_id;
3367 // Reset inventory to creative if in creative mode
3368 if(g_settings.getBool("creative_mode"))
3370 setCreativeInventory(player);
3377 If player with the wanted peer_id already exists, cancel.
3379 if(m_env.getPlayer(peer_id) != NULL)
3381 dstream<<"emergePlayer(): Player with wrong name but same"
3382 " peer_id already exists"<<std::endl;
3390 player = new ServerRemotePlayer();
3391 //player->peer_id = c.peer_id;
3392 //player->peer_id = PEER_ID_INEXISTENT;
3393 player->peer_id = peer_id;
3394 player->updateName(name);
3400 dstream<<"Server: Finding spawn place for player \""
3401 <<player->getName()<<"\""<<std::endl;
3405 player->setPosition(intToFloat(v3s16(
3412 s16 groundheight = 0;
3414 // Try to find a good place a few times
3415 for(s32 i=0; i<1000; i++)
3418 // We're going to try to throw the player to this position
3419 nodepos = v2s16(-range + (myrand()%(range*2)),
3420 -range + (myrand()%(range*2)));
3421 v2s16 sectorpos = getNodeSectorPos(nodepos);
3422 // Get sector (NOTE: Don't get because it's slow)
3423 //m_env.getMap().emergeSector(sectorpos);
3424 // Get ground height at point (fallbacks to heightmap function)
3425 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3426 // Don't go underwater
3427 if(groundheight < WATER_LEVEL)
3429 //dstream<<"-> Underwater"<<std::endl;
3432 // Don't go to high places
3433 if(groundheight > WATER_LEVEL + 4)
3435 //dstream<<"-> Underwater"<<std::endl;
3440 // Doesn't work, generating blocks is a bit too complicated for doing here
3441 // Get block at point
3443 nodepos3d = v3s16(nodepos.X, groundheight+1, nodepos.Y);
3444 v3s16 blockpos = getNodeBlockPos(nodepos3d);
3445 ((ServerMap*)(&m_env.getMap()))->emergeBlock(blockpos);
3446 // Don't go inside ground
3448 /*v3s16 footpos(nodepos.X, groundheight+1, nodepos.Y);
3449 v3s16 headpos(nodepos.X, groundheight+2, nodepos.Y);*/
3450 v3s16 footpos = nodepos3d + v3s16(0,0,0);
3451 v3s16 headpos = nodepos3d + v3s16(0,1,0);
3452 if(m_env.getMap().getNode(footpos).d != CONTENT_AIR
3453 || m_env.getMap().getNode(headpos).d != CONTENT_AIR)
3455 dstream<<"-> Inside ground"<<std::endl;
3459 }catch(InvalidPositionException &e)
3461 dstream<<"-> Invalid position"<<std::endl;
3462 // Ignore invalid position
3467 // Found a good place
3468 dstream<<"Searched through "<<i<<" places."<<std::endl;
3473 // If no suitable place was not found, go above water at least.
3474 if(groundheight < WATER_LEVEL)
3475 groundheight = WATER_LEVEL;
3477 player->setPosition(intToFloat(v3s16(
3485 Add player to environment
3488 m_env.addPlayer(player);
3491 Add stuff to inventory
3494 if(g_settings.getBool("creative_mode"))
3496 setCreativeInventory(player);
3501 InventoryItem *item = new ToolItem("WPick", 32000);
3502 void* r = player->inventory.addItem("main", item);
3506 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3507 void* r = player->inventory.addItem("main", item);
3511 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3512 void* r = player->inventory.addItem("main", item);
3516 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3517 void* r = player->inventory.addItem("main", item);
3521 InventoryItem *item = new CraftItem("Stick", 4);
3522 void* r = player->inventory.addItem("main", item);
3526 InventoryItem *item = new ToolItem("WPick", 32000);
3527 void* r = player->inventory.addItem("main", item);
3531 InventoryItem *item = new ToolItem("STPick", 32000);
3532 void* r = player->inventory.addItem("main", item);
3535 /*// Give some lights
3537 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3538 bool r = player->inventory.addItem("main", item);
3542 for(u16 i=0; i<4; i++)
3544 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3545 bool r = player->inventory.addItem("main", item);
3548 /*// Give some other stuff
3550 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3551 bool r = player->inventory.addItem("main", item);
3558 } // create new player
3562 void Server::UpdateBlockWaterPressure(MapBlock *block,
3563 core::map<v3s16, MapBlock*> &modified_blocks)
3565 MapVoxelManipulator v(&m_env.getMap());
3566 v.m_disable_water_climb =
3567 g_settings.getBool("disable_water_climb");
3569 VoxelArea area(block->getPosRelative(),
3570 block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
3574 v.updateAreaWaterPressure(area, m_flow_active_nodes);
3576 catch(ProcessingLimitException &e)
3578 dstream<<"Processing limit reached (1)"<<std::endl;
3581 v.blitBack(modified_blocks);
3585 void Server::handlePeerChange(PeerChange &c)
3587 JMutexAutoLock envlock(m_env_mutex);
3588 JMutexAutoLock conlock(m_con_mutex);
3590 if(c.type == PEER_ADDED)
3597 core::map<u16, RemoteClient*>::Node *n;
3598 n = m_clients.find(c.peer_id);
3599 // The client shouldn't already exist
3603 RemoteClient *client = new RemoteClient();
3604 client->peer_id = c.peer_id;
3605 m_clients.insert(client->peer_id, client);
3608 else if(c.type == PEER_REMOVED)
3615 core::map<u16, RemoteClient*>::Node *n;
3616 n = m_clients.find(c.peer_id);
3617 // The client should exist
3620 // Collect information about leaving in chat
3621 std::wstring message;
3623 std::wstring name = L"unknown";
3624 Player *player = m_env.getPlayer(c.peer_id);
3626 name = narrow_to_wide(player->getName());
3630 message += L" left game";
3632 message += L" (timed out)";
3637 m_env.removePlayer(c.peer_id);
3640 // Set player client disconnected
3642 Player *player = m_env.getPlayer(c.peer_id);
3644 player->peer_id = 0;
3648 delete m_clients[c.peer_id];
3649 m_clients.remove(c.peer_id);
3651 // Send player info to all remaining clients
3654 // Send leave chat message to all remaining clients
3655 BroadcastChatMessage(message);
3664 void Server::handlePeerChanges()
3666 while(m_peer_change_queue.size() > 0)
3668 PeerChange c = m_peer_change_queue.pop_front();
3670 dout_server<<"Server: Handling peer change: "
3671 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3674 handlePeerChange(c);
3678 void dedicated_server_loop(Server &server, bool &kill)
3680 DSTACK(__FUNCTION_NAME);
3682 std::cout<<DTIME<<std::endl;
3683 std::cout<<"========================"<<std::endl;
3684 std::cout<<"Running dedicated server"<<std::endl;
3685 std::cout<<"========================"<<std::endl;
3686 std::cout<<std::endl;
3690 // This is kind of a hack but can be done like this
3691 // because server.step() is very light
3695 if(server.getShutdownRequested() || kill)
3697 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
3701 static int counter = 0;
3707 core::list<PlayerInfo> list = server.getPlayerInfo();
3708 core::list<PlayerInfo>::Iterator i;
3709 static u32 sum_old = 0;
3710 u32 sum = PIChecksum(list);
3713 std::cout<<DTIME<<"Player info:"<<std::endl;
3714 for(i=list.begin(); i!=list.end(); i++)
3716 i->PrintLine(&std::cout);