3 Copyright (C) 2010-2011 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 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
40 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
42 class MapEditEventIgnorer
45 MapEditEventIgnorer(bool *flag):
54 ~MapEditEventIgnorer()
67 void * ServerThread::Thread()
71 DSTACK(__FUNCTION_NAME);
73 BEGIN_DEBUG_EXCEPTION_HANDLER
78 //TimeTaker timer("AsyncRunStep() + Receive()");
81 //TimeTaker timer("AsyncRunStep()");
82 m_server->AsyncRunStep();
85 //dout_server<<"Running m_server->Receive()"<<std::endl;
88 catch(con::NoIncomingDataException &e)
91 catch(con::PeerNotFoundException &e)
93 dout_server<<"Server: PeerNotFoundException"<<std::endl;
97 END_DEBUG_EXCEPTION_HANDLER
102 void * EmergeThread::Thread()
106 DSTACK(__FUNCTION_NAME);
108 BEGIN_DEBUG_EXCEPTION_HANDLER
110 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
113 Get block info from queue, emerge them and send them
116 After queue is empty, exit.
120 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
124 SharedPtr<QueuedBlockEmerge> q(qptr);
130 Do not generate over-limit
132 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
133 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
134 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
137 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
140 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
142 //TimeTaker timer("block emerge");
145 Try to emerge it from somewhere.
147 If it is only wanted as optional, only loading from disk
152 Check if any peer wants it as non-optional. In that case it
155 Also decrement the emerge queue count in clients.
158 bool only_from_disk = true;
161 core::map<u16, u8>::Iterator i;
162 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
164 //u16 peer_id = i.getNode()->getKey();
167 u8 flags = i.getNode()->getValue();
168 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
169 only_from_disk = false;
174 if(enable_mapgen_debug_info)
175 dstream<<"EmergeThread: p="
176 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
177 <<"only_from_disk="<<only_from_disk<<std::endl;
179 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
181 //core::map<v3s16, MapBlock*> changed_blocks;
182 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
184 MapBlock *block = NULL;
185 bool got_block = true;
186 core::map<v3s16, MapBlock*> modified_blocks;
189 Fetch block from map or generate a single block
192 JMutexAutoLock envlock(m_server->m_env_mutex);
194 // Load sector if it isn't loaded
195 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196 //map.loadSectorFull(p2d);
197 map.loadSectorMeta(p2d);
199 block = map.getBlockNoCreateNoEx(p);
200 if(!block || block->isDummy() || !block->isGenerated())
202 if(enable_mapgen_debug_info)
203 dstream<<"EmergeThread: not in memory, loading"<<std::endl;
205 // Get, load or create sector
206 /*ServerMapSector *sector =
207 (ServerMapSector*)map.createSector(p2d);*/
209 // Load/generate block
211 /*block = map.emergeBlock(p, sector, changed_blocks,
212 lighting_invalidated_blocks);*/
214 block = map.loadBlock(p);
216 if(only_from_disk == false)
218 if(block == NULL || block->isGenerated() == false)
220 if(enable_mapgen_debug_info)
221 dstream<<"EmergeThread: generating"<<std::endl;
222 block = map.generateBlock(p, modified_blocks);
226 if(enable_mapgen_debug_info)
227 dstream<<"EmergeThread: ended up with: "
228 <<analyze_block(block)<<std::endl;
237 Ignore map edit events, they will not need to be
238 sent to anybody because the block hasn't been sent
241 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
243 // Activate objects and stuff
244 m_server->m_env.activateBlock(block, 3600);
249 /*if(block->getLightingExpired()){
250 lighting_invalidated_blocks[block->getPos()] = block;
254 // TODO: Some additional checking and lighting updating,
259 JMutexAutoLock envlock(m_server->m_env_mutex);
264 Collect a list of blocks that have been modified in
265 addition to the fetched one.
269 if(lighting_invalidated_blocks.size() > 0)
271 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
272 <<" blocks"<<std::endl;*/
274 // 50-100ms for single block generation
275 //TimeTaker timer("** EmergeThread updateLighting");
277 // Update lighting without locking the environment mutex,
278 // add modified blocks to changed blocks
279 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
282 // Add all from changed_blocks to modified_blocks
283 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
284 i.atEnd() == false; i++)
286 MapBlock *block = i.getNode()->getValue();
287 modified_blocks.insert(block->getPos(), block);
291 // If we got no block, there should be no invalidated blocks
294 //assert(lighting_invalidated_blocks.size() == 0);
300 Set sent status of modified blocks on clients
303 // NOTE: Server's clients are also behind the connection mutex
304 JMutexAutoLock lock(m_server->m_con_mutex);
307 Add the originally fetched block to the modified list
311 modified_blocks.insert(p, block);
315 Set the modified blocks unsent for all the clients
318 for(core::map<u16, RemoteClient*>::Iterator
319 i = m_server->m_clients.getIterator();
320 i.atEnd() == false; i++)
322 RemoteClient *client = i.getNode()->getValue();
324 if(modified_blocks.size() > 0)
326 // Remove block from sent history
327 client->SetBlocksNotSent(modified_blocks);
333 END_DEBUG_EXCEPTION_HANDLER
338 void RemoteClient::GetNextBlocks(Server *server, float dtime,
339 core::array<PrioritySortedBlockTransfer> &dest)
341 DSTACK(__FUNCTION_NAME);
344 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
347 m_nothing_to_send_pause_timer -= dtime;
349 if(m_nothing_to_send_pause_timer >= 0)
352 m_nearest_unsent_reset_timer = 0;
356 // Won't send anything if already sending
357 if(m_blocks_sending.size() >= g_settings.getU16
358 ("max_simultaneous_block_sends_per_client"))
360 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
364 //TimeTaker timer("RemoteClient::GetNextBlocks");
366 Player *player = server->m_env.getPlayer(peer_id);
368 assert(player != NULL);
370 v3f playerpos = player->getPosition();
371 v3f playerspeed = player->getSpeed();
372 v3f playerspeeddir(0,0,0);
373 if(playerspeed.getLength() > 1.0*BS)
374 playerspeeddir = playerspeed / playerspeed.getLength();
375 // Predict to next block
376 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
378 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
380 v3s16 center = getNodeBlockPos(center_nodepos);
382 // Camera position and direction
383 v3f camera_pos = player->getEyePosition();
384 v3f camera_dir = v3f(0,0,1);
385 camera_dir.rotateYZBy(player->getPitch());
386 camera_dir.rotateXZBy(player->getYaw());
388 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
389 <<camera_dir.Z<<")"<<std::endl;*/
392 Get the starting value of the block finder radius.
395 if(m_last_center != center)
397 m_nearest_unsent_d = 0;
398 m_last_center = center;
401 /*dstream<<"m_nearest_unsent_reset_timer="
402 <<m_nearest_unsent_reset_timer<<std::endl;*/
404 // This has to be incremented only when the nothing to send pause
406 m_nearest_unsent_reset_timer += dtime;
408 // Reset periodically to avoid possible bugs or other mishaps
409 if(m_nearest_unsent_reset_timer > 10.0)
411 m_nearest_unsent_reset_timer = 0;
412 m_nearest_unsent_d = 0;
413 /*dstream<<"Resetting m_nearest_unsent_d for "
414 <<server->getPlayerName(peer_id)<<std::endl;*/
417 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
418 s16 d_start = m_nearest_unsent_d;
420 //dstream<<"d_start="<<d_start<<std::endl;
422 u16 max_simul_sends_setting = g_settings.getU16
423 ("max_simultaneous_block_sends_per_client");
424 u16 max_simul_sends_usually = max_simul_sends_setting;
427 Check the time from last addNode/removeNode.
429 Decrease send rate if player is building stuff.
431 m_time_from_building += dtime;
432 if(m_time_from_building < g_settings.getFloat(
433 "full_block_send_enable_min_time_from_building"))
435 max_simul_sends_usually
436 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
440 Number of blocks sending + number of blocks selected for sending
442 u32 num_blocks_selected = m_blocks_sending.size();
445 next time d will be continued from the d from which the nearest
446 unsent block was found this time.
448 This is because not necessarily any of the blocks found this
449 time are actually sent.
451 s32 new_nearest_unsent_d = -1;
453 s16 d_max = g_settings.getS16("max_block_send_distance");
454 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
456 // Don't loop very much at a time
457 if(d_max > d_start+1)
459 /*if(d_max_gen > d_start+2)
460 d_max_gen = d_start+2;*/
462 //dstream<<"Starting from "<<d_start<<std::endl;
464 bool sending_something = false;
466 bool no_blocks_found_for_sending = true;
468 bool queue_is_full = false;
471 for(d = d_start; d <= d_max; d++)
473 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
476 If m_nearest_unsent_d was changed by the EmergeThread
477 (it can change it to 0 through SetBlockNotSent),
479 Else update m_nearest_unsent_d
481 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
483 d = m_nearest_unsent_d;
484 last_nearest_unsent_d = m_nearest_unsent_d;
488 Get the border/face dot coordinates of a "d-radiused"
491 core::list<v3s16> list;
492 getFacePositions(list, d);
494 core::list<v3s16>::Iterator li;
495 for(li=list.begin(); li!=list.end(); li++)
497 v3s16 p = *li + center;
501 - Don't allow too many simultaneous transfers
502 - EXCEPT when the blocks are very close
504 Also, don't send blocks that are already flying.
507 // Start with the usual maximum
508 u16 max_simul_dynamic = max_simul_sends_usually;
510 // If block is very close, allow full maximum
511 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
512 max_simul_dynamic = max_simul_sends_setting;
514 // Don't select too many blocks for sending
515 if(num_blocks_selected >= max_simul_dynamic)
517 queue_is_full = true;
518 goto queue_full_break;
521 // Don't send blocks that are currently being transferred
522 if(m_blocks_sending.find(p) != NULL)
528 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
529 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
536 // If this is true, inexistent block will be made from scratch
537 bool generate = d <= d_max_gen;
540 /*// Limit the generating area vertically to 2/3
541 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
544 // Limit the send area vertically to 2/3
545 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
551 If block is far away, don't generate it unless it is
557 // Block center y in nodes
558 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
559 // Don't generate if it's very high or very low
560 if(y < -64 || y > 64)
564 v2s16 p2d_nodes_center(
568 // Get ground height in nodes
569 s16 gh = server->m_env.getServerMap().findGroundLevel(
572 // If differs a lot, don't generate
573 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
575 // Actually, don't even send it
581 //dstream<<"d="<<d<<std::endl;
584 Don't generate or send if not in sight
587 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
593 Don't send already sent blocks
596 if(m_blocks_sent.find(p) != NULL)
603 Check if map has this block
605 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
607 bool surely_not_found_on_disk = false;
608 bool block_is_invalid = false;
611 // Reset usage timer, this block will be of use in the future.
612 block->resetUsageTimer();
614 // Block is dummy if data doesn't exist.
615 // It means it has been not found from disk and not generated
618 surely_not_found_on_disk = true;
621 // Block is valid if lighting is up-to-date and data exists
622 if(block->isValid() == false)
624 block_is_invalid = true;
627 /*if(block->isFullyGenerated() == false)
629 block_is_invalid = true;
634 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
635 v2s16 chunkpos = map->sector_to_chunk(p2d);
636 if(map->chunkNonVolatile(chunkpos) == false)
637 block_is_invalid = true;
639 if(block->isGenerated() == false)
640 block_is_invalid = true;
643 If block is not close, don't send it unless it is near
646 Block is near ground level if night-time mesh
647 differs from day-time mesh.
651 if(block->dayNightDiffed() == false)
658 If block has been marked to not exist on disk (dummy)
659 and generating new ones is not wanted, skip block.
661 if(generate == false && surely_not_found_on_disk == true)
668 Record the lowest d from which a block has been
669 found being not sent and possibly to exist
671 if(no_blocks_found_for_sending)
674 new_nearest_unsent_d = d;
677 no_blocks_found_for_sending = false;
680 Add inexistent block to emerge queue.
682 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
684 //TODO: Get value from somewhere
685 // Allow only one block in emerge queue
686 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
687 // Allow two blocks in queue per client
688 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
690 //dstream<<"Adding block to emerge queue"<<std::endl;
692 // Add it to the emerge queue and trigger the thread
695 if(generate == false)
696 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
698 server->m_emerge_queue.addBlock(peer_id, p, flags);
699 server->m_emergethread.trigger();
707 Add block to send queue
710 PrioritySortedBlockTransfer q((float)d, p, peer_id);
714 num_blocks_selected += 1;
715 sending_something = true;
720 //dstream<<"Stopped at "<<d<<std::endl;
722 if(no_blocks_found_for_sending)
724 if(queue_is_full == false)
725 new_nearest_unsent_d = d;
728 if(new_nearest_unsent_d != -1)
729 m_nearest_unsent_d = new_nearest_unsent_d;
731 if(sending_something == false)
733 m_nothing_to_send_counter++;
734 if((s16)m_nothing_to_send_counter >=
735 g_settings.getS16("max_block_send_distance"))
737 // Pause time in seconds
738 m_nothing_to_send_pause_timer = 1.0;
739 /*dstream<<"nothing to send to "
740 <<server->getPlayerName(peer_id)
741 <<" (d="<<d<<")"<<std::endl;*/
746 m_nothing_to_send_counter = 0;
749 /*timer_result = timer.stop(true);
750 if(timer_result != 0)
751 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
754 void RemoteClient::SendObjectData(
757 core::map<v3s16, bool> &stepped_blocks
760 DSTACK(__FUNCTION_NAME);
762 // Can't send anything without knowing version
763 if(serialization_version == SER_FMT_VER_INVALID)
765 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
771 Send a TOCLIENT_OBJECTDATA packet.
775 u16 number of player positions
787 std::ostringstream os(std::ios_base::binary);
791 writeU16(buf, TOCLIENT_OBJECTDATA);
792 os.write((char*)buf, 2);
795 Get and write player data
798 // Get connected players
799 core::list<Player*> players = server->m_env.getPlayers(true);
801 // Write player count
802 u16 playercount = players.size();
803 writeU16(buf, playercount);
804 os.write((char*)buf, 2);
806 core::list<Player*>::Iterator i;
807 for(i = players.begin();
808 i != players.end(); i++)
812 v3f pf = player->getPosition();
813 v3f sf = player->getSpeed();
815 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
816 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
817 s32 pitch_i (player->getPitch() * 100);
818 s32 yaw_i (player->getYaw() * 100);
820 writeU16(buf, player->peer_id);
821 os.write((char*)buf, 2);
822 writeV3S32(buf, position_i);
823 os.write((char*)buf, 12);
824 writeV3S32(buf, speed_i);
825 os.write((char*)buf, 12);
826 writeS32(buf, pitch_i);
827 os.write((char*)buf, 4);
828 writeS32(buf, yaw_i);
829 os.write((char*)buf, 4);
833 Get and write object data
839 For making players to be able to build to their nearby
840 environment (building is not possible on blocks that are not
843 - Add blocks to emerge queue if they are not found
845 SUGGESTION: These could be ignored from the backside of the player
848 Player *player = server->m_env.getPlayer(peer_id);
852 v3f playerpos = player->getPosition();
853 v3f playerspeed = player->getSpeed();
855 v3s16 center_nodepos = floatToInt(playerpos, BS);
856 v3s16 center = getNodeBlockPos(center_nodepos);
858 s16 d_max = g_settings.getS16("active_object_range");
860 // Number of blocks whose objects were written to bos
863 std::ostringstream bos(std::ios_base::binary);
865 for(s16 d = 0; d <= d_max; d++)
867 core::list<v3s16> list;
868 getFacePositions(list, d);
870 core::list<v3s16>::Iterator li;
871 for(li=list.begin(); li!=list.end(); li++)
873 v3s16 p = *li + center;
876 Ignore blocks that haven't been sent to the client
879 if(m_blocks_sent.find(p) == NULL)
883 // Try stepping block and add it to a send queue
888 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
891 Step block if not in stepped_blocks and add to stepped_blocks.
893 if(stepped_blocks.find(p) == NULL)
895 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
896 stepped_blocks.insert(p, true);
897 block->setChangedFlag();
900 // Skip block if there are no objects
901 if(block->getObjectCount() == 0)
910 bos.write((char*)buf, 6);
913 //block->serializeObjects(bos, serialization_version); // DEPRECATED
920 Stop collecting objects if data is already too big
922 // Sum of player and object data sizes
923 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
924 // break out if data too big
925 if(sum > MAX_OBJECTDATA_SIZE)
927 goto skip_subsequent;
931 catch(InvalidPositionException &e)
934 // Add it to the emerge queue and trigger the thread.
935 // Fetch the block only if it is on disk.
937 // Grab and increment counter
938 /*SharedPtr<JMutexAutoLock> lock
939 (m_num_blocks_in_emerge_queue.getLock());
940 m_num_blocks_in_emerge_queue.m_value++;*/
942 // Add to queue as an anonymous fetch from disk
943 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
944 server->m_emerge_queue.addBlock(0, p, flags);
945 server->m_emergethread.trigger();
953 writeU16(buf, blockcount);
954 os.write((char*)buf, 2);
956 // Write block objects
963 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
966 std::string s = os.str();
967 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
968 // Send as unreliable
969 server->m_con.Send(peer_id, 0, data, false);
972 void RemoteClient::GotBlock(v3s16 p)
974 if(m_blocks_sending.find(p) != NULL)
975 m_blocks_sending.remove(p);
978 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
979 " m_blocks_sending"<<std::endl;*/
980 m_excess_gotblocks++;
982 m_blocks_sent.insert(p, true);
985 void RemoteClient::SentBlock(v3s16 p)
987 if(m_blocks_sending.find(p) == NULL)
988 m_blocks_sending.insert(p, 0.0);
990 dstream<<"RemoteClient::SentBlock(): Sent block"
991 " already in m_blocks_sending"<<std::endl;
994 void RemoteClient::SetBlockNotSent(v3s16 p)
996 m_nearest_unsent_d = 0;
998 if(m_blocks_sending.find(p) != NULL)
999 m_blocks_sending.remove(p);
1000 if(m_blocks_sent.find(p) != NULL)
1001 m_blocks_sent.remove(p);
1004 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1006 m_nearest_unsent_d = 0;
1008 for(core::map<v3s16, MapBlock*>::Iterator
1009 i = blocks.getIterator();
1010 i.atEnd()==false; i++)
1012 v3s16 p = i.getNode()->getKey();
1014 if(m_blocks_sending.find(p) != NULL)
1015 m_blocks_sending.remove(p);
1016 if(m_blocks_sent.find(p) != NULL)
1017 m_blocks_sent.remove(p);
1025 PlayerInfo::PlayerInfo()
1031 void PlayerInfo::PrintLine(std::ostream *s)
1034 (*s)<<"\""<<name<<"\" ("
1035 <<(position.X/10)<<","<<(position.Y/10)
1036 <<","<<(position.Z/10)<<") ";
1038 (*s)<<" avg_rtt="<<avg_rtt;
1042 u32 PIChecksum(core::list<PlayerInfo> &l)
1044 core::list<PlayerInfo>::Iterator i;
1047 for(i=l.begin(); i!=l.end(); i++)
1049 checksum += a * (i->id+1);
1050 checksum ^= 0x435aafcd;
1061 std::string mapsavedir,
1062 std::string configpath
1064 m_env(new ServerMap(mapsavedir), this),
1065 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1066 m_authmanager(mapsavedir+"/auth.txt"),
1067 m_banmanager(mapsavedir+"/ipban.txt"),
1069 m_emergethread(this),
1071 m_time_of_day_send_timer(0),
1073 m_mapsavedir(mapsavedir),
1074 m_configpath(configpath),
1075 m_shutdown_requested(false),
1076 m_ignore_map_edit_events(false),
1077 m_ignore_map_edit_events_peer_id(0)
1079 m_liquid_transform_timer = 0.0;
1080 m_print_info_timer = 0.0;
1081 m_objectdata_timer = 0.0;
1082 m_emergethread_trigger_timer = 0.0;
1083 m_savemap_timer = 0.0;
1087 m_step_dtime_mutex.Init();
1090 // Register us to receive map edit events
1091 m_env.getMap().addEventReceiver(this);
1093 // If file exists, load environment metadata
1094 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1096 dstream<<"Server: Loading environment metadata"<<std::endl;
1097 m_env.loadMeta(m_mapsavedir);
1101 dstream<<"Server: Loading players"<<std::endl;
1102 m_env.deSerializePlayers(m_mapsavedir);
1107 dstream<<"Server::~Server()"<<std::endl;
1110 Send shutdown message
1113 JMutexAutoLock conlock(m_con_mutex);
1115 std::wstring line = L"*** Server shutting down";
1118 Send the message to clients
1120 for(core::map<u16, RemoteClient*>::Iterator
1121 i = m_clients.getIterator();
1122 i.atEnd() == false; i++)
1124 // Get client and check that it is valid
1125 RemoteClient *client = i.getNode()->getValue();
1126 assert(client->peer_id == i.getNode()->getKey());
1127 if(client->serialization_version == SER_FMT_VER_INVALID)
1131 SendChatMessage(client->peer_id, line);
1133 catch(con::PeerNotFoundException &e)
1141 dstream<<"Server: Saving players"<<std::endl;
1142 m_env.serializePlayers(m_mapsavedir);
1145 Save environment metadata
1147 dstream<<"Server: Saving environment metadata"<<std::endl;
1148 m_env.saveMeta(m_mapsavedir);
1159 JMutexAutoLock clientslock(m_con_mutex);
1161 for(core::map<u16, RemoteClient*>::Iterator
1162 i = m_clients.getIterator();
1163 i.atEnd() == false; i++)
1166 // NOTE: These are removed by env destructor
1168 u16 peer_id = i.getNode()->getKey();
1169 JMutexAutoLock envlock(m_env_mutex);
1170 m_env.removePlayer(peer_id);
1174 delete i.getNode()->getValue();
1179 void Server::start(unsigned short port)
1181 DSTACK(__FUNCTION_NAME);
1182 // Stop thread if already running
1185 // Initialize connection
1186 m_con.setTimeoutMs(30);
1190 m_thread.setRun(true);
1193 dout_server<<"Server: Started on port "<<port<<std::endl;
1198 DSTACK(__FUNCTION_NAME);
1200 // Stop threads (set run=false first so both start stopping)
1201 m_thread.setRun(false);
1202 m_emergethread.setRun(false);
1204 m_emergethread.stop();
1206 dout_server<<"Server: Threads stopped"<<std::endl;
1209 void Server::step(float dtime)
1211 DSTACK(__FUNCTION_NAME);
1216 JMutexAutoLock lock(m_step_dtime_mutex);
1217 m_step_dtime += dtime;
1221 void Server::AsyncRunStep()
1223 DSTACK(__FUNCTION_NAME);
1227 JMutexAutoLock lock1(m_step_dtime_mutex);
1228 dtime = m_step_dtime;
1232 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1233 "blocks to clients");
1234 // Send blocks to clients
1241 //dstream<<"Server steps "<<dtime<<std::endl;
1242 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1245 JMutexAutoLock lock1(m_step_dtime_mutex);
1246 m_step_dtime -= dtime;
1253 m_uptime.set(m_uptime.get() + dtime);
1257 // Process connection's timeouts
1258 JMutexAutoLock lock2(m_con_mutex);
1259 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1260 m_con.RunTimeouts(dtime);
1264 // This has to be called so that the client list gets synced
1265 // with the peer list of the connection
1266 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1267 handlePeerChanges();
1271 Update m_time_of_day and overall game time
1274 JMutexAutoLock envlock(m_env_mutex);
1276 m_time_counter += dtime;
1277 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1278 u32 units = (u32)(m_time_counter*speed);
1279 m_time_counter -= (f32)units / speed;
1281 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1283 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1286 Send to clients at constant intervals
1289 m_time_of_day_send_timer -= dtime;
1290 if(m_time_of_day_send_timer < 0.0)
1292 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1294 //JMutexAutoLock envlock(m_env_mutex);
1295 JMutexAutoLock conlock(m_con_mutex);
1297 for(core::map<u16, RemoteClient*>::Iterator
1298 i = m_clients.getIterator();
1299 i.atEnd() == false; i++)
1301 RemoteClient *client = i.getNode()->getValue();
1302 //Player *player = m_env.getPlayer(client->peer_id);
1304 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1305 m_env.getTimeOfDay());
1307 m_con.Send(client->peer_id, 0, data, true);
1313 JMutexAutoLock lock(m_env_mutex);
1315 ScopeProfiler sp(&g_profiler, "Server: environment step");
1319 const float map_timer_and_unload_dtime = 5.15;
1320 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1322 JMutexAutoLock lock(m_env_mutex);
1323 // Run Map's timers and unload unused data
1324 ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
1325 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1326 g_settings.getFloat("server_unload_unused_data_timeout"));
1336 m_liquid_transform_timer += dtime;
1337 if(m_liquid_transform_timer >= 1.00)
1339 m_liquid_transform_timer -= 1.00;
1341 JMutexAutoLock lock(m_env_mutex);
1343 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1345 core::map<v3s16, MapBlock*> modified_blocks;
1346 m_env.getMap().transformLiquids(modified_blocks);
1351 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1352 ServerMap &map = ((ServerMap&)m_env.getMap());
1353 map.updateLighting(modified_blocks, lighting_modified_blocks);
1355 // Add blocks modified by lighting to modified_blocks
1356 for(core::map<v3s16, MapBlock*>::Iterator
1357 i = lighting_modified_blocks.getIterator();
1358 i.atEnd() == false; i++)
1360 MapBlock *block = i.getNode()->getValue();
1361 modified_blocks.insert(block->getPos(), block);
1365 Set the modified blocks unsent for all the clients
1368 JMutexAutoLock lock2(m_con_mutex);
1370 for(core::map<u16, RemoteClient*>::Iterator
1371 i = m_clients.getIterator();
1372 i.atEnd() == false; i++)
1374 RemoteClient *client = i.getNode()->getValue();
1376 if(modified_blocks.size() > 0)
1378 // Remove block from sent history
1379 client->SetBlocksNotSent(modified_blocks);
1384 // Periodically print some info
1386 float &counter = m_print_info_timer;
1392 JMutexAutoLock lock2(m_con_mutex);
1394 for(core::map<u16, RemoteClient*>::Iterator
1395 i = m_clients.getIterator();
1396 i.atEnd() == false; i++)
1398 //u16 peer_id = i.getNode()->getKey();
1399 RemoteClient *client = i.getNode()->getValue();
1400 Player *player = m_env.getPlayer(client->peer_id);
1403 std::cout<<player->getName()<<"\t";
1404 client->PrintInfo(std::cout);
1409 //if(g_settings.getBool("enable_experimental"))
1413 Check added and deleted active objects
1416 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1417 JMutexAutoLock envlock(m_env_mutex);
1418 JMutexAutoLock conlock(m_con_mutex);
1420 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1422 // Radius inside which objects are active
1425 for(core::map<u16, RemoteClient*>::Iterator
1426 i = m_clients.getIterator();
1427 i.atEnd() == false; i++)
1429 RemoteClient *client = i.getNode()->getValue();
1430 Player *player = m_env.getPlayer(client->peer_id);
1433 // This can happen if the client timeouts somehow
1434 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1436 <<" has no associated player"<<std::endl;*/
1439 v3s16 pos = floatToInt(player->getPosition(), BS);
1441 core::map<u16, bool> removed_objects;
1442 core::map<u16, bool> added_objects;
1443 m_env.getRemovedActiveObjects(pos, radius,
1444 client->m_known_objects, removed_objects);
1445 m_env.getAddedActiveObjects(pos, radius,
1446 client->m_known_objects, added_objects);
1448 // Ignore if nothing happened
1449 if(removed_objects.size() == 0 && added_objects.size() == 0)
1451 //dstream<<"INFO: active objects: none changed"<<std::endl;
1455 std::string data_buffer;
1459 // Handle removed objects
1460 writeU16((u8*)buf, removed_objects.size());
1461 data_buffer.append(buf, 2);
1462 for(core::map<u16, bool>::Iterator
1463 i = removed_objects.getIterator();
1464 i.atEnd()==false; i++)
1467 u16 id = i.getNode()->getKey();
1468 ServerActiveObject* obj = m_env.getActiveObject(id);
1470 // Add to data buffer for sending
1471 writeU16((u8*)buf, i.getNode()->getKey());
1472 data_buffer.append(buf, 2);
1474 // Remove from known objects
1475 client->m_known_objects.remove(i.getNode()->getKey());
1477 if(obj && obj->m_known_by_count > 0)
1478 obj->m_known_by_count--;
1481 // Handle added objects
1482 writeU16((u8*)buf, added_objects.size());
1483 data_buffer.append(buf, 2);
1484 for(core::map<u16, bool>::Iterator
1485 i = added_objects.getIterator();
1486 i.atEnd()==false; i++)
1489 u16 id = i.getNode()->getKey();
1490 ServerActiveObject* obj = m_env.getActiveObject(id);
1493 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1495 dstream<<"WARNING: "<<__FUNCTION_NAME
1496 <<": NULL object"<<std::endl;
1498 type = obj->getType();
1500 // Add to data buffer for sending
1501 writeU16((u8*)buf, id);
1502 data_buffer.append(buf, 2);
1503 writeU8((u8*)buf, type);
1504 data_buffer.append(buf, 1);
1507 data_buffer.append(serializeLongString(
1508 obj->getClientInitializationData()));
1510 data_buffer.append(serializeLongString(""));
1512 // Add to known objects
1513 client->m_known_objects.insert(i.getNode()->getKey(), false);
1516 obj->m_known_by_count++;
1520 SharedBuffer<u8> reply(2 + data_buffer.size());
1521 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1522 memcpy((char*)&reply[2], data_buffer.c_str(),
1523 data_buffer.size());
1525 m_con.Send(client->peer_id, 0, reply, true);
1527 dstream<<"INFO: Server: Sent object remove/add: "
1528 <<removed_objects.size()<<" removed, "
1529 <<added_objects.size()<<" added, "
1530 <<"packet size is "<<reply.getSize()<<std::endl;
1535 Collect a list of all the objects known by the clients
1536 and report it back to the environment.
1539 core::map<u16, bool> all_known_objects;
1541 for(core::map<u16, RemoteClient*>::Iterator
1542 i = m_clients.getIterator();
1543 i.atEnd() == false; i++)
1545 RemoteClient *client = i.getNode()->getValue();
1546 // Go through all known objects of client
1547 for(core::map<u16, bool>::Iterator
1548 i = client->m_known_objects.getIterator();
1549 i.atEnd()==false; i++)
1551 u16 id = i.getNode()->getKey();
1552 all_known_objects[id] = true;
1556 m_env.setKnownActiveObjects(whatever);
1562 Send object messages
1565 JMutexAutoLock envlock(m_env_mutex);
1566 JMutexAutoLock conlock(m_con_mutex);
1568 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1571 // Value = data sent by object
1572 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1574 // Get active object messages from environment
1577 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1581 core::list<ActiveObjectMessage>* message_list = NULL;
1582 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1583 n = buffered_messages.find(aom.id);
1586 message_list = new core::list<ActiveObjectMessage>;
1587 buffered_messages.insert(aom.id, message_list);
1591 message_list = n->getValue();
1593 message_list->push_back(aom);
1596 // Route data to every client
1597 for(core::map<u16, RemoteClient*>::Iterator
1598 i = m_clients.getIterator();
1599 i.atEnd()==false; i++)
1601 RemoteClient *client = i.getNode()->getValue();
1602 std::string reliable_data;
1603 std::string unreliable_data;
1604 // Go through all objects in message buffer
1605 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1606 j = buffered_messages.getIterator();
1607 j.atEnd()==false; j++)
1609 // If object is not known by client, skip it
1610 u16 id = j.getNode()->getKey();
1611 if(client->m_known_objects.find(id) == NULL)
1613 // Get message list of object
1614 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1615 // Go through every message
1616 for(core::list<ActiveObjectMessage>::Iterator
1617 k = list->begin(); k != list->end(); k++)
1619 // Compose the full new data with header
1620 ActiveObjectMessage aom = *k;
1621 std::string new_data;
1624 writeU16((u8*)&buf[0], aom.id);
1625 new_data.append(buf, 2);
1627 new_data += serializeString(aom.datastring);
1628 // Add data to buffer
1630 reliable_data += new_data;
1632 unreliable_data += new_data;
1636 reliable_data and unreliable_data are now ready.
1639 if(reliable_data.size() > 0)
1641 SharedBuffer<u8> reply(2 + reliable_data.size());
1642 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1643 memcpy((char*)&reply[2], reliable_data.c_str(),
1644 reliable_data.size());
1646 m_con.Send(client->peer_id, 0, reply, true);
1648 if(unreliable_data.size() > 0)
1650 SharedBuffer<u8> reply(2 + unreliable_data.size());
1651 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1652 memcpy((char*)&reply[2], unreliable_data.c_str(),
1653 unreliable_data.size());
1654 // Send as unreliable
1655 m_con.Send(client->peer_id, 0, reply, false);
1658 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1660 dstream<<"INFO: Server: Size of object message data: "
1661 <<"reliable: "<<reliable_data.size()
1662 <<", unreliable: "<<unreliable_data.size()
1667 // Clear buffered_messages
1668 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1669 i = buffered_messages.getIterator();
1670 i.atEnd()==false; i++)
1672 delete i.getNode()->getValue();
1676 } // enable_experimental
1679 Send queued-for-sending map edit events.
1682 // Don't send too many at a time
1685 // Single change sending is disabled if queue size is not small
1686 bool disable_single_change_sending = false;
1687 if(m_unsent_map_edit_queue.size() >= 4)
1688 disable_single_change_sending = true;
1690 bool got_any_events = false;
1692 // We'll log the amount of each
1695 while(m_unsent_map_edit_queue.size() != 0)
1697 got_any_events = true;
1699 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1701 // Players far away from the change are stored here.
1702 // Instead of sending the changes, MapBlocks are set not sent
1704 core::list<u16> far_players;
1706 if(event->type == MEET_ADDNODE)
1708 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1709 prof.add("MEET_ADDNODE", 1);
1710 if(disable_single_change_sending)
1711 sendAddNode(event->p, event->n, event->already_known_by_peer,
1714 sendAddNode(event->p, event->n, event->already_known_by_peer,
1717 else if(event->type == MEET_REMOVENODE)
1719 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1720 prof.add("MEET_REMOVENODE", 1);
1721 if(disable_single_change_sending)
1722 sendRemoveNode(event->p, event->already_known_by_peer,
1725 sendRemoveNode(event->p, event->already_known_by_peer,
1728 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1730 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1731 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1732 setBlockNotSent(event->p);
1734 else if(event->type == MEET_OTHER)
1736 dstream<<"Server: MEET_OTHER"<<std::endl;
1737 prof.add("MEET_OTHER", 1);
1738 for(core::map<v3s16, bool>::Iterator
1739 i = event->modified_blocks.getIterator();
1740 i.atEnd()==false; i++)
1742 v3s16 p = i.getNode()->getKey();
1748 prof.add("unknown", 1);
1749 dstream<<"WARNING: Server: Unknown MapEditEvent "
1750 <<((u32)event->type)<<std::endl;
1754 Set blocks not sent to far players
1756 if(far_players.size() > 0)
1758 // Convert list format to that wanted by SetBlocksNotSent
1759 core::map<v3s16, MapBlock*> modified_blocks2;
1760 for(core::map<v3s16, bool>::Iterator
1761 i = event->modified_blocks.getIterator();
1762 i.atEnd()==false; i++)
1764 v3s16 p = i.getNode()->getKey();
1765 modified_blocks2.insert(p,
1766 m_env.getMap().getBlockNoCreateNoEx(p));
1768 // Set blocks not sent
1769 for(core::list<u16>::Iterator
1770 i = far_players.begin();
1771 i != far_players.end(); i++)
1774 RemoteClient *client = getClient(peer_id);
1777 client->SetBlocksNotSent(modified_blocks2);
1783 /*// Don't send too many at a time
1785 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1791 dstream<<"Server: MapEditEvents:"<<std::endl;
1792 prof.print(dstream);
1798 Send object positions
1799 TODO: Get rid of MapBlockObjects
1802 float &counter = m_objectdata_timer;
1804 if(counter >= g_settings.getFloat("objectdata_interval"))
1806 JMutexAutoLock lock1(m_env_mutex);
1807 JMutexAutoLock lock2(m_con_mutex);
1809 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1811 SendObjectData(counter);
1818 Trigger emergethread (it somehow gets to a non-triggered but
1819 bysy state sometimes)
1822 float &counter = m_emergethread_trigger_timer;
1828 m_emergethread.trigger();
1832 // Save map, players and auth stuff
1834 float &counter = m_savemap_timer;
1836 if(counter >= g_settings.getFloat("server_map_save_interval"))
1840 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1843 if(m_authmanager.isModified())
1844 m_authmanager.save();
1847 if(m_banmanager.isModified())
1848 m_banmanager.save();
1851 JMutexAutoLock lock(m_env_mutex);
1853 /*// Unload unused data (delete from memory)
1854 m_env.getMap().unloadUnusedData(
1855 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1857 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1858 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1861 // Save only changed parts
1862 m_env.getMap().save(true);
1864 /*if(deleted_count > 0)
1866 dout_server<<"Server: Unloaded "<<deleted_count
1867 <<" blocks from memory"<<std::endl;
1871 m_env.serializePlayers(m_mapsavedir);
1873 // Save environment metadata
1874 m_env.saveMeta(m_mapsavedir);
1879 void Server::Receive()
1881 DSTACK(__FUNCTION_NAME);
1882 u32 data_maxsize = 10000;
1883 Buffer<u8> data(data_maxsize);
1888 JMutexAutoLock conlock(m_con_mutex);
1889 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1892 // This has to be called so that the client list gets synced
1893 // with the peer list of the connection
1894 handlePeerChanges();
1896 ProcessData(*data, datasize, peer_id);
1898 catch(con::InvalidIncomingDataException &e)
1900 derr_server<<"Server::Receive(): "
1901 "InvalidIncomingDataException: what()="
1902 <<e.what()<<std::endl;
1904 catch(con::PeerNotFoundException &e)
1906 //NOTE: This is not needed anymore
1908 // The peer has been disconnected.
1909 // Find the associated player and remove it.
1911 /*JMutexAutoLock envlock(m_env_mutex);
1913 dout_server<<"ServerThread: peer_id="<<peer_id
1914 <<" has apparently closed connection. "
1915 <<"Removing player."<<std::endl;
1917 m_env.removePlayer(peer_id);*/
1921 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1923 DSTACK(__FUNCTION_NAME);
1924 // Environment is locked first.
1925 JMutexAutoLock envlock(m_env_mutex);
1926 JMutexAutoLock conlock(m_con_mutex);
1930 peer = m_con.GetPeer(peer_id);
1932 catch(con::PeerNotFoundException &e)
1934 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1935 <<peer_id<<" not found"<<std::endl;
1939 // drop player if is ip is banned
1940 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1941 SendAccessDenied(m_con, peer_id,
1942 L"Your ip is banned. Banned name was "
1943 +narrow_to_wide(m_banmanager.getBanName(
1944 peer->address.serializeString())));
1945 m_con.deletePeer(peer_id, false);
1949 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1957 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1959 if(command == TOSERVER_INIT)
1961 // [0] u16 TOSERVER_INIT
1962 // [2] u8 SER_FMT_VER_HIGHEST
1963 // [3] u8[20] player_name
1964 // [23] u8[28] password <--- can be sent without this, from old versions
1966 if(datasize < 2+1+PLAYERNAME_SIZE)
1969 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1970 <<peer->id<<std::endl;
1972 // First byte after command is maximum supported
1973 // serialization version
1974 u8 client_max = data[2];
1975 u8 our_max = SER_FMT_VER_HIGHEST;
1976 // Use the highest version supported by both
1977 u8 deployed = core::min_(client_max, our_max);
1978 // If it's lower than the lowest supported, give up.
1979 if(deployed < SER_FMT_VER_LOWEST)
1980 deployed = SER_FMT_VER_INVALID;
1982 //peer->serialization_version = deployed;
1983 getClient(peer->id)->pending_serialization_version = deployed;
1985 if(deployed == SER_FMT_VER_INVALID)
1987 derr_server<<DTIME<<"Server: Cannot negotiate "
1988 "serialization version with peer "
1989 <<peer_id<<std::endl;
1990 SendAccessDenied(m_con, peer_id,
1991 L"Your client is too old (map format)");
1996 Read and check network protocol version
1999 u16 net_proto_version = 0;
2000 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2002 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2005 getClient(peer->id)->net_proto_version = net_proto_version;
2007 if(net_proto_version == 0)
2009 SendAccessDenied(m_con, peer_id,
2010 L"Your client is too old. Please upgrade.");
2014 /* Uhh... this should actually be a warning but let's do it like this */
2015 if(net_proto_version < 2)
2017 SendAccessDenied(m_con, peer_id,
2018 L"Your client is too old. Please upgrade.");
2027 char playername[PLAYERNAME_SIZE];
2028 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2030 playername[i] = data[3+i];
2032 playername[PLAYERNAME_SIZE-1] = 0;
2034 if(playername[0]=='\0')
2036 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2037 SendAccessDenied(m_con, peer_id,
2042 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2044 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2045 SendAccessDenied(m_con, peer_id,
2046 L"Name contains unallowed characters");
2051 char password[PASSWORD_SIZE];
2052 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2054 // old version - assume blank password
2059 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2061 password[i] = data[23+i];
2063 password[PASSWORD_SIZE-1] = 0;
2066 std::string checkpwd;
2067 if(m_authmanager.exists(playername))
2069 checkpwd = m_authmanager.getPassword(playername);
2073 checkpwd = g_settings.get("default_password");
2076 /*dstream<<"Server: Client gave password '"<<password
2077 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2079 if(password != checkpwd && m_authmanager.exists(playername))
2081 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2082 <<": supplied invalid password for "
2083 <<playername<<std::endl;
2084 SendAccessDenied(m_con, peer_id, L"Invalid password");
2088 // Add player to auth manager
2089 if(m_authmanager.exists(playername) == false)
2091 derr_server<<DTIME<<"Server: adding player "<<playername
2092 <<" to auth manager"<<std::endl;
2093 m_authmanager.add(playername);
2094 m_authmanager.setPassword(playername, checkpwd);
2095 m_authmanager.setPrivs(playername,
2096 stringToPrivs(g_settings.get("default_privs")));
2097 m_authmanager.save();
2101 Player *player = emergePlayer(playername, password, peer_id);
2105 // DEBUG: Test serialization
2106 std::ostringstream test_os;
2107 player->serialize(test_os);
2108 dstream<<"Player serialization test: \""<<test_os.str()
2110 std::istringstream test_is(test_os.str());
2111 player->deSerialize(test_is);
2114 // If failed, cancel
2117 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2118 <<": failed to emerge player"<<std::endl;
2123 // If a client is already connected to the player, cancel
2124 if(player->peer_id != 0)
2126 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2127 <<" tried to connect to "
2128 "an already connected player (peer_id="
2129 <<player->peer_id<<")"<<std::endl;
2132 // Set client of player
2133 player->peer_id = peer_id;
2136 // Check if player doesn't exist
2138 throw con::InvalidIncomingDataException
2139 ("Server::ProcessData(): INIT: Player doesn't exist");
2141 /*// update name if it was supplied
2142 if(datasize >= 20+3)
2145 player->updateName((const char*)&data[3]);
2149 Answer with a TOCLIENT_INIT
2152 SharedBuffer<u8> reply(2+1+6+8);
2153 writeU16(&reply[0], TOCLIENT_INIT);
2154 writeU8(&reply[2], deployed);
2155 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2156 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2159 m_con.Send(peer_id, 0, reply, true);
2163 Send complete position information
2165 SendMovePlayer(player);
2170 if(command == TOSERVER_INIT2)
2172 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2173 <<peer->id<<std::endl;
2176 getClient(peer->id)->serialization_version
2177 = getClient(peer->id)->pending_serialization_version;
2180 Send some initialization data
2183 // Send player info to all players
2186 // Send inventory to player
2187 UpdateCrafting(peer->id);
2188 SendInventory(peer->id);
2190 // Send player items to all players
2195 Player *player = m_env.getPlayer(peer_id);
2196 SendPlayerHP(player);
2201 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2202 m_env.getTimeOfDay());
2203 m_con.Send(peer->id, 0, data, true);
2206 // Send information about server to player in chat
2207 SendChatMessage(peer_id, getStatusString());
2209 // Send information about joining in chat
2211 std::wstring name = L"unknown";
2212 Player *player = m_env.getPlayer(peer_id);
2214 name = narrow_to_wide(player->getName());
2216 std::wstring message;
2219 message += L" joined game";
2220 BroadcastChatMessage(message);
2223 // Warnings about protocol version can be issued here
2224 /*if(getClient(peer->id)->net_proto_version == 0)
2226 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2232 if(peer_ser_ver == SER_FMT_VER_INVALID)
2234 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2235 " serialization format invalid or not initialized."
2236 " Skipping incoming command="<<command<<std::endl;
2240 Player *player = m_env.getPlayer(peer_id);
2243 derr_server<<"Server::ProcessData(): Cancelling: "
2244 "No player for peer_id="<<peer_id
2248 if(command == TOSERVER_PLAYERPOS)
2250 if(datasize < 2+12+12+4+4)
2254 v3s32 ps = readV3S32(&data[start+2]);
2255 v3s32 ss = readV3S32(&data[start+2+12]);
2256 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2257 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2258 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2259 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2260 pitch = wrapDegrees(pitch);
2261 yaw = wrapDegrees(yaw);
2262 player->setPosition(position);
2263 player->setSpeed(speed);
2264 player->setPitch(pitch);
2265 player->setYaw(yaw);
2267 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2268 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2269 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2271 else if(command == TOSERVER_GOTBLOCKS)
2284 u16 count = data[2];
2285 for(u16 i=0; i<count; i++)
2287 if((s16)datasize < 2+1+(i+1)*6)
2288 throw con::InvalidIncomingDataException
2289 ("GOTBLOCKS length is too short");
2290 v3s16 p = readV3S16(&data[2+1+i*6]);
2291 /*dstream<<"Server: GOTBLOCKS ("
2292 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2293 RemoteClient *client = getClient(peer_id);
2294 client->GotBlock(p);
2297 else if(command == TOSERVER_DELETEDBLOCKS)
2310 u16 count = data[2];
2311 for(u16 i=0; i<count; i++)
2313 if((s16)datasize < 2+1+(i+1)*6)
2314 throw con::InvalidIncomingDataException
2315 ("DELETEDBLOCKS length is too short");
2316 v3s16 p = readV3S16(&data[2+1+i*6]);
2317 /*dstream<<"Server: DELETEDBLOCKS ("
2318 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2319 RemoteClient *client = getClient(peer_id);
2320 client->SetBlockNotSent(p);
2323 else if(command == TOSERVER_CLICK_OBJECT)
2328 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2333 [2] u8 button (0=left, 1=right)
2338 u8 button = readU8(&data[2]);
2340 p.X = readS16(&data[3]);
2341 p.Y = readS16(&data[5]);
2342 p.Z = readS16(&data[7]);
2343 s16 id = readS16(&data[9]);
2344 //u16 item_i = readU16(&data[11]);
2346 MapBlock *block = NULL;
2349 block = m_env.getMap().getBlockNoCreate(p);
2351 catch(InvalidPositionException &e)
2353 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2357 MapBlockObject *obj = block->getObject(id);
2361 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2365 //TODO: Check that object is reasonably close
2370 InventoryList *ilist = player->inventory.getList("main");
2371 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2374 // Skip if inventory has no free space
2375 if(ilist->getUsedSlots() == ilist->getSize())
2377 dout_server<<"Player inventory has no free space"<<std::endl;
2382 Create the inventory item
2384 InventoryItem *item = NULL;
2385 // If it is an item-object, take the item from it
2386 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2388 item = ((ItemObject*)obj)->createInventoryItem();
2390 // Else create an item of the object
2393 item = new MapBlockObjectItem
2394 (obj->getInventoryString());
2397 // Add to inventory and send inventory
2398 ilist->addItem(item);
2399 UpdateCrafting(player->peer_id);
2400 SendInventory(player->peer_id);
2403 // Remove from block
2404 block->removeObject(id);
2407 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2412 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2418 [2] u8 button (0=left, 1=right)
2422 u8 button = readU8(&data[2]);
2423 u16 id = readS16(&data[3]);
2424 u16 item_i = readU16(&data[11]);
2426 ServerActiveObject *obj = m_env.getActiveObject(id);
2430 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2435 // Skip if object has been removed
2439 //TODO: Check that object is reasonably close
2441 // Left click, pick object up (usually)
2445 Try creating inventory item
2447 InventoryItem *item = obj->createPickedUpItem();
2451 InventoryList *ilist = player->inventory.getList("main");
2454 if(g_settings.getBool("creative_mode") == false)
2456 // Skip if inventory has no free space
2457 if(ilist->roomForItem(item) == false)
2459 dout_server<<"Player inventory has no free space"<<std::endl;
2463 // Add to inventory and send inventory
2464 ilist->addItem(item);
2465 UpdateCrafting(player->peer_id);
2466 SendInventory(player->peer_id);
2469 // Remove object from environment
2470 obj->m_removed = true;
2476 Item cannot be picked up. Punch it instead.
2479 ToolItem *titem = NULL;
2480 std::string toolname = "";
2482 InventoryList *mlist = player->inventory.getList("main");
2485 InventoryItem *item = mlist->getItem(item_i);
2486 if(item && (std::string)item->getName() == "ToolItem")
2488 titem = (ToolItem*)item;
2489 toolname = titem->getToolName();
2493 v3f playerpos = player->getPosition();
2494 v3f objpos = obj->getBasePosition();
2495 v3f dir = (objpos - playerpos).normalize();
2497 u16 wear = obj->punch(toolname, dir);
2501 bool weared_out = titem->addWear(wear);
2503 mlist->deleteItem(item_i);
2504 SendInventory(player->peer_id);
2508 // Right click, do something with object
2511 // Track hp changes super-crappily
2512 u16 oldhp = player->hp;
2515 obj->rightClick(player);
2518 if(player->hp != oldhp)
2520 SendPlayerHP(player);
2524 else if(command == TOSERVER_GROUND_ACTION)
2532 [3] v3s16 nodepos_undersurface
2533 [9] v3s16 nodepos_abovesurface
2538 2: stop digging (all parameters ignored)
2539 3: digging completed
2541 u8 action = readU8(&data[2]);
2543 p_under.X = readS16(&data[3]);
2544 p_under.Y = readS16(&data[5]);
2545 p_under.Z = readS16(&data[7]);
2547 p_over.X = readS16(&data[9]);
2548 p_over.Y = readS16(&data[11]);
2549 p_over.Z = readS16(&data[13]);
2550 u16 item_i = readU16(&data[15]);
2552 //TODO: Check that target is reasonably close
2560 NOTE: This can be used in the future to check if
2561 somebody is cheating, by checking the timing.
2568 else if(action == 2)
2571 RemoteClient *client = getClient(peer->id);
2572 JMutexAutoLock digmutex(client->m_dig_mutex);
2573 client->m_dig_tool_item = -1;
2578 3: Digging completed
2580 else if(action == 3)
2582 // Mandatory parameter; actually used for nothing
2583 core::map<v3s16, MapBlock*> modified_blocks;
2585 content_t material = CONTENT_IGNORE;
2586 u8 mineral = MINERAL_NONE;
2588 bool cannot_remove_node = false;
2592 MapNode n = m_env.getMap().getNode(p_under);
2594 mineral = n.getMineral();
2595 // Get material at position
2596 material = n.getContent();
2597 // If not yet cancelled
2598 if(cannot_remove_node == false)
2600 // If it's not diggable, do nothing
2601 if(content_diggable(material) == false)
2603 derr_server<<"Server: Not finishing digging: "
2604 <<"Node not diggable"
2606 cannot_remove_node = true;
2609 // If not yet cancelled
2610 if(cannot_remove_node == false)
2612 // Get node metadata
2613 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2614 if(meta && meta->nodeRemovalDisabled() == true)
2616 derr_server<<"Server: Not finishing digging: "
2617 <<"Node metadata disables removal"
2619 cannot_remove_node = true;
2623 catch(InvalidPositionException &e)
2625 derr_server<<"Server: Not finishing digging: Node not found."
2626 <<" Adding block to emerge queue."
2628 m_emerge_queue.addBlock(peer_id,
2629 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2630 cannot_remove_node = true;
2633 // Make sure the player is allowed to do it
2634 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2636 dstream<<"Player "<<player->getName()<<" cannot remove node"
2637 <<" because privileges are "<<getPlayerPrivs(player)
2639 cannot_remove_node = true;
2643 If node can't be removed, set block to be re-sent to
2646 if(cannot_remove_node)
2648 derr_server<<"Server: Not finishing digging."<<std::endl;
2650 // Client probably has wrong data.
2651 // Set block not sent, so that client will get
2653 dstream<<"Client "<<peer_id<<" tried to dig "
2654 <<"node; but node cannot be removed."
2655 <<" setting MapBlock not sent."<<std::endl;
2656 RemoteClient *client = getClient(peer_id);
2657 v3s16 blockpos = getNodeBlockPos(p_under);
2658 client->SetBlockNotSent(blockpos);
2664 Send the removal to all close-by players.
2665 - If other player is close, send REMOVENODE
2666 - Otherwise set blocks not sent
2668 core::list<u16> far_players;
2669 sendRemoveNode(p_under, peer_id, &far_players, 30);
2672 Update and send inventory
2675 if(g_settings.getBool("creative_mode") == false)
2680 InventoryList *mlist = player->inventory.getList("main");
2683 InventoryItem *item = mlist->getItem(item_i);
2684 if(item && (std::string)item->getName() == "ToolItem")
2686 ToolItem *titem = (ToolItem*)item;
2687 std::string toolname = titem->getToolName();
2689 // Get digging properties for material and tool
2690 DiggingProperties prop =
2691 getDiggingProperties(material, toolname);
2693 if(prop.diggable == false)
2695 derr_server<<"Server: WARNING: Player digged"
2696 <<" with impossible material + tool"
2697 <<" combination"<<std::endl;
2700 bool weared_out = titem->addWear(prop.wear);
2704 mlist->deleteItem(item_i);
2710 Add dug item to inventory
2713 InventoryItem *item = NULL;
2715 if(mineral != MINERAL_NONE)
2716 item = getDiggedMineralItem(mineral);
2721 std::string &dug_s = content_features(material).dug_item;
2724 std::istringstream is(dug_s, std::ios::binary);
2725 item = InventoryItem::deSerialize(is);
2731 // Add a item to inventory
2732 player->inventory.addItem("main", item);
2735 UpdateCrafting(player->peer_id);
2736 SendInventory(player->peer_id);
2742 (this takes some time so it is done after the quick stuff)
2745 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2747 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2750 Set blocks not sent to far players
2752 for(core::list<u16>::Iterator
2753 i = far_players.begin();
2754 i != far_players.end(); i++)
2757 RemoteClient *client = getClient(peer_id);
2760 client->SetBlocksNotSent(modified_blocks);
2767 else if(action == 1)
2770 InventoryList *ilist = player->inventory.getList("main");
2775 InventoryItem *item = ilist->getItem(item_i);
2777 // If there is no item, it is not possible to add it anywhere
2782 Handle material items
2784 if(std::string("MaterialItem") == item->getName())
2787 // Don't add a node if this is not a free space
2788 MapNode n2 = m_env.getMap().getNode(p_over);
2789 bool no_enough_privs =
2790 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2792 dstream<<"Player "<<player->getName()<<" cannot add node"
2793 <<" because privileges are "<<getPlayerPrivs(player)
2796 if(content_features(n2).buildable_to == false
2799 // Client probably has wrong data.
2800 // Set block not sent, so that client will get
2802 dstream<<"Client "<<peer_id<<" tried to place"
2803 <<" node in invalid position; setting"
2804 <<" MapBlock not sent."<<std::endl;
2805 RemoteClient *client = getClient(peer_id);
2806 v3s16 blockpos = getNodeBlockPos(p_over);
2807 client->SetBlockNotSent(blockpos);
2811 catch(InvalidPositionException &e)
2813 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2814 <<" Adding block to emerge queue."
2816 m_emerge_queue.addBlock(peer_id,
2817 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2821 // Reset build time counter
2822 getClient(peer->id)->m_time_from_building = 0.0;
2825 MaterialItem *mitem = (MaterialItem*)item;
2827 n.setContent(mitem->getMaterial());
2829 // Calculate direction for wall mounted stuff
2830 if(content_features(n).wall_mounted)
2831 n.param2 = packDir(p_under - p_over);
2833 // Calculate the direction for furnaces and chests and stuff
2834 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2836 v3f playerpos = player->getPosition();
2837 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2838 blockpos = blockpos.normalize();
2840 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2854 Send to all close-by players
2856 core::list<u16> far_players;
2857 sendAddNode(p_over, n, 0, &far_players, 30);
2862 InventoryList *ilist = player->inventory.getList("main");
2863 if(g_settings.getBool("creative_mode") == false && ilist)
2865 // Remove from inventory and send inventory
2866 if(mitem->getCount() == 1)
2867 ilist->deleteItem(item_i);
2871 UpdateCrafting(peer_id);
2872 SendInventory(peer_id);
2878 This takes some time so it is done after the quick stuff
2880 core::map<v3s16, MapBlock*> modified_blocks;
2882 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2884 std::string p_name = std::string(player->getName());
2885 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2888 Set blocks not sent to far players
2890 for(core::list<u16>::Iterator
2891 i = far_players.begin();
2892 i != far_players.end(); i++)
2895 RemoteClient *client = getClient(peer_id);
2898 client->SetBlocksNotSent(modified_blocks);
2902 Calculate special events
2905 /*if(n.d == CONTENT_MESE)
2908 for(s16 z=-1; z<=1; z++)
2909 for(s16 y=-1; y<=1; y++)
2910 for(s16 x=-1; x<=1; x++)
2917 Place other item (not a block)
2921 v3s16 blockpos = getNodeBlockPos(p_over);
2924 Check that the block is loaded so that the item
2925 can properly be added to the static list too
2927 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2930 derr_server<<"Error while placing object: "
2931 "block not found"<<std::endl;
2936 If in creative mode, item dropping is disabled unless
2937 player has build privileges
2939 if(g_settings.getBool("creative_mode") &&
2940 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2942 derr_server<<"Not allowing player to drop item: "
2943 "creative mode and no build privs"<<std::endl;
2947 dout_server<<"Placing a miscellaneous item on map"
2950 // Calculate a position for it
2951 v3f pos = intToFloat(p_over, BS);
2953 pos.Y -= BS*0.25; // let it drop a bit
2955 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2956 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2961 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2965 derr_server<<"WARNING: item resulted in NULL object, "
2966 <<"not placing onto map"
2971 // Add the object to the environment
2972 m_env.addActiveObject(obj);
2974 dout_server<<"Placed object"<<std::endl;
2976 if(g_settings.getBool("creative_mode") == false)
2978 // Delete the right amount of items from the slot
2979 u16 dropcount = item->getDropCount();
2981 // Delete item if all gone
2982 if(item->getCount() <= dropcount)
2984 if(item->getCount() < dropcount)
2985 dstream<<"WARNING: Server: dropped more items"
2986 <<" than the slot contains"<<std::endl;
2988 InventoryList *ilist = player->inventory.getList("main");
2990 // Remove from inventory and send inventory
2991 ilist->deleteItem(item_i);
2993 // Else decrement it
2995 item->remove(dropcount);
2998 UpdateCrafting(peer_id);
2999 SendInventory(peer_id);
3007 Catch invalid actions
3011 derr_server<<"WARNING: Server: Invalid action "
3012 <<action<<std::endl;
3016 else if(command == TOSERVER_RELEASE)
3025 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3028 else if(command == TOSERVER_SIGNTEXT)
3030 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3039 std::string datastring((char*)&data[2], datasize-2);
3040 std::istringstream is(datastring, std::ios_base::binary);
3043 is.read((char*)buf, 6);
3044 v3s16 blockpos = readV3S16(buf);
3045 is.read((char*)buf, 2);
3046 s16 id = readS16(buf);
3047 is.read((char*)buf, 2);
3048 u16 textlen = readU16(buf);
3050 for(u16 i=0; i<textlen; i++)
3052 is.read((char*)buf, 1);
3053 text += (char)buf[0];
3056 MapBlock *block = NULL;
3059 block = m_env.getMap().getBlockNoCreate(blockpos);
3061 catch(InvalidPositionException &e)
3063 derr_server<<"Error while setting sign text: "
3064 "block not found"<<std::endl;
3068 MapBlockObject *obj = block->getObject(id);
3071 derr_server<<"Error while setting sign text: "
3072 "object not found"<<std::endl;
3076 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3078 derr_server<<"Error while setting sign text: "
3079 "object is not a sign"<<std::endl;
3083 ((SignObject*)obj)->setText(text);
3085 obj->getBlock()->setChangedFlag();
3087 else if(command == TOSERVER_SIGNNODETEXT)
3089 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3097 std::string datastring((char*)&data[2], datasize-2);
3098 std::istringstream is(datastring, std::ios_base::binary);
3101 is.read((char*)buf, 6);
3102 v3s16 p = readV3S16(buf);
3103 is.read((char*)buf, 2);
3104 u16 textlen = readU16(buf);
3106 for(u16 i=0; i<textlen; i++)
3108 is.read((char*)buf, 1);
3109 text += (char)buf[0];
3112 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3115 if(meta->typeId() != CONTENT_SIGN_WALL)
3117 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3118 signmeta->setText(text);
3120 v3s16 blockpos = getNodeBlockPos(p);
3121 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3124 block->setChangedFlag();
3127 for(core::map<u16, RemoteClient*>::Iterator
3128 i = m_clients.getIterator();
3129 i.atEnd()==false; i++)
3131 RemoteClient *client = i.getNode()->getValue();
3132 client->SetBlockNotSent(blockpos);
3135 else if(command == TOSERVER_INVENTORY_ACTION)
3137 /*// Ignore inventory changes if in creative mode
3138 if(g_settings.getBool("creative_mode") == true)
3140 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3144 // Strip command and create a stream
3145 std::string datastring((char*)&data[2], datasize-2);
3146 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3147 std::istringstream is(datastring, std::ios_base::binary);
3149 InventoryAction *a = InventoryAction::deSerialize(is);
3154 c.current_player = player;
3157 Handle craftresult specially if not in creative mode
3159 bool disable_action = false;
3160 if(a->getType() == IACTION_MOVE
3161 && g_settings.getBool("creative_mode") == false)
3163 IMoveAction *ma = (IMoveAction*)a;
3164 if(ma->to_inv == "current_player" &&
3165 ma->from_inv == "current_player")
3167 InventoryList *rlist = player->inventory.getList("craftresult");
3169 InventoryList *clist = player->inventory.getList("craft");
3171 InventoryList *mlist = player->inventory.getList("main");
3174 Craftresult is no longer preview if something
3177 if(ma->to_list == "craftresult"
3178 && ma->from_list != "craftresult")
3180 // If it currently is a preview, remove
3182 if(player->craftresult_is_preview)
3184 rlist->deleteItem(0);
3186 player->craftresult_is_preview = false;
3189 Crafting takes place if this condition is true.
3191 if(player->craftresult_is_preview &&
3192 ma->from_list == "craftresult")
3194 player->craftresult_is_preview = false;
3195 clist->decrementMaterials(1);
3198 If the craftresult is placed on itself, move it to
3199 main inventory instead of doing the action
3201 if(ma->to_list == "craftresult"
3202 && ma->from_list == "craftresult")
3204 disable_action = true;
3206 InventoryItem *item1 = rlist->changeItem(0, NULL);
3207 mlist->addItem(item1);
3210 // Disallow moving items if not allowed to build
3211 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3215 // if it's a locking chest, only allow the owner or server admins to move items
3216 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3218 Strfnd fn(ma->from_inv);
3219 std::string id0 = fn.next(":");
3220 if(id0 == "nodemeta")
3223 p.X = stoi(fn.next(","));
3224 p.Y = stoi(fn.next(","));
3225 p.Z = stoi(fn.next(","));
3226 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3227 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3228 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3229 if (lcm->getOwner() != player->getName())
3234 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3236 Strfnd fn(ma->to_inv);
3237 std::string id0 = fn.next(":");
3238 if(id0 == "nodemeta")
3241 p.X = stoi(fn.next(","));
3242 p.Y = stoi(fn.next(","));
3243 p.Z = stoi(fn.next(","));
3244 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3245 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3246 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3247 if (lcm->getOwner() != player->getName())
3254 if(disable_action == false)
3256 // Feed action to player inventory
3264 UpdateCrafting(player->peer_id);
3265 SendInventory(player->peer_id);
3270 dstream<<"TOSERVER_INVENTORY_ACTION: "
3271 <<"InventoryAction::deSerialize() returned NULL"
3275 else if(command == TOSERVER_CHAT_MESSAGE)
3283 std::string datastring((char*)&data[2], datasize-2);
3284 std::istringstream is(datastring, std::ios_base::binary);
3287 is.read((char*)buf, 2);
3288 u16 len = readU16(buf);
3290 std::wstring message;
3291 for(u16 i=0; i<len; i++)
3293 is.read((char*)buf, 2);
3294 message += (wchar_t)readU16(buf);
3297 // Get player name of this client
3298 std::wstring name = narrow_to_wide(player->getName());
3300 // Line to send to players
3302 // Whether to send to the player that sent the line
3303 bool send_to_sender = false;
3304 // Whether to send to other players
3305 bool send_to_others = false;
3307 // Local player gets all privileges regardless of
3308 // what's set on their account.
3309 u64 privs = getPlayerPrivs(player);
3312 if(message[0] == L'/')
3314 size_t strip_size = 1;
3315 if (message[1] == L'#') // support old-style commans
3317 message = message.substr(strip_size);
3319 WStrfnd f1(message);
3320 f1.next(L" "); // Skip over /#whatever
3321 std::wstring paramstring = f1.next(L"");
3323 ServerCommandContext *ctx = new ServerCommandContext(
3324 str_split(message, L' '),
3331 std::wstring reply(processServerCommand(ctx));
3332 send_to_sender = ctx->flags & SEND_TO_SENDER;
3333 send_to_others = ctx->flags & SEND_TO_OTHERS;
3335 if (ctx->flags & SEND_NO_PREFIX)
3338 line += L"Server: " + reply;
3345 if(privs & PRIV_SHOUT)
3351 send_to_others = true;
3355 line += L"Server: You are not allowed to shout";
3356 send_to_sender = true;
3362 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3365 Send the message to clients
3367 for(core::map<u16, RemoteClient*>::Iterator
3368 i = m_clients.getIterator();
3369 i.atEnd() == false; i++)
3371 // Get client and check that it is valid
3372 RemoteClient *client = i.getNode()->getValue();
3373 assert(client->peer_id == i.getNode()->getKey());
3374 if(client->serialization_version == SER_FMT_VER_INVALID)
3378 bool sender_selected = (peer_id == client->peer_id);
3379 if(sender_selected == true && send_to_sender == false)
3381 if(sender_selected == false && send_to_others == false)
3384 SendChatMessage(client->peer_id, line);
3388 else if(command == TOSERVER_DAMAGE)
3390 if(g_settings.getBool("enable_damage"))
3392 std::string datastring((char*)&data[2], datasize-2);
3393 std::istringstream is(datastring, std::ios_base::binary);
3394 u8 damage = readU8(is);
3395 if(player->hp > damage)
3397 player->hp -= damage;
3403 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3406 v3f pos = findSpawnPos(m_env.getServerMap());
3407 player->setPosition(pos);
3409 SendMovePlayer(player);
3410 SendPlayerHP(player);
3412 //TODO: Throw items around
3416 SendPlayerHP(player);
3418 else if(command == TOSERVER_PASSWORD)
3421 [0] u16 TOSERVER_PASSWORD
3422 [2] u8[28] old password
3423 [30] u8[28] new password
3426 if(datasize != 2+PASSWORD_SIZE*2)
3428 /*char password[PASSWORD_SIZE];
3429 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3430 password[i] = data[2+i];
3431 password[PASSWORD_SIZE-1] = 0;*/
3433 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3441 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3443 char c = data[2+PASSWORD_SIZE+i];
3449 dstream<<"Server: Client requests a password change from "
3450 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3452 std::string playername = player->getName();
3454 if(m_authmanager.exists(playername) == false)
3456 dstream<<"Server: playername not found in authmanager"<<std::endl;
3457 // Wrong old password supplied!!
3458 SendChatMessage(peer_id, L"playername not found in authmanager");
3462 std::string checkpwd = m_authmanager.getPassword(playername);
3464 if(oldpwd != checkpwd)
3466 dstream<<"Server: invalid old password"<<std::endl;
3467 // Wrong old password supplied!!
3468 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3472 m_authmanager.setPassword(playername, newpwd);
3474 dstream<<"Server: password change successful for "<<playername
3476 SendChatMessage(peer_id, L"Password change successful");
3478 else if (command == TOSERVER_PLAYERITEM)
3483 u16 item = readU16(&data[2]);
3484 player->wieldItem(item);
3485 SendWieldedItem(player);
3489 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3490 "unknown command "<<command<<std::endl;
3494 catch(SendFailedException &e)
3496 derr_server<<"Server::ProcessData(): SendFailedException: "
3502 void Server::onMapEditEvent(MapEditEvent *event)
3504 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3505 if(m_ignore_map_edit_events)
3507 MapEditEvent *e = event->clone();
3508 m_unsent_map_edit_queue.push_back(e);
3511 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3513 if(id == "current_player")
3515 assert(c->current_player);
3516 return &(c->current_player->inventory);
3520 std::string id0 = fn.next(":");
3522 if(id0 == "nodemeta")
3525 p.X = stoi(fn.next(","));
3526 p.Y = stoi(fn.next(","));
3527 p.Z = stoi(fn.next(","));
3528 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3530 return meta->getInventory();
3531 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3532 <<"no metadata found"<<std::endl;
3536 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3539 void Server::inventoryModified(InventoryContext *c, std::string id)
3541 if(id == "current_player")
3543 assert(c->current_player);
3545 UpdateCrafting(c->current_player->peer_id);
3546 SendInventory(c->current_player->peer_id);
3551 std::string id0 = fn.next(":");
3553 if(id0 == "nodemeta")
3556 p.X = stoi(fn.next(","));
3557 p.Y = stoi(fn.next(","));
3558 p.Z = stoi(fn.next(","));
3559 v3s16 blockpos = getNodeBlockPos(p);
3561 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3563 meta->inventoryModified();
3565 for(core::map<u16, RemoteClient*>::Iterator
3566 i = m_clients.getIterator();
3567 i.atEnd()==false; i++)
3569 RemoteClient *client = i.getNode()->getValue();
3570 client->SetBlockNotSent(blockpos);
3576 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3579 core::list<PlayerInfo> Server::getPlayerInfo()
3581 DSTACK(__FUNCTION_NAME);
3582 JMutexAutoLock envlock(m_env_mutex);
3583 JMutexAutoLock conlock(m_con_mutex);
3585 core::list<PlayerInfo> list;
3587 core::list<Player*> players = m_env.getPlayers();
3589 core::list<Player*>::Iterator i;
3590 for(i = players.begin();
3591 i != players.end(); i++)
3595 Player *player = *i;
3598 con::Peer *peer = m_con.GetPeer(player->peer_id);
3599 // Copy info from peer to info struct
3601 info.address = peer->address;
3602 info.avg_rtt = peer->avg_rtt;
3604 catch(con::PeerNotFoundException &e)
3606 // Set dummy peer info
3608 info.address = Address(0,0,0,0,0);
3612 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3613 info.position = player->getPosition();
3615 list.push_back(info);
3622 void Server::peerAdded(con::Peer *peer)
3624 DSTACK(__FUNCTION_NAME);
3625 dout_server<<"Server::peerAdded(): peer->id="
3626 <<peer->id<<std::endl;
3629 c.type = PEER_ADDED;
3630 c.peer_id = peer->id;
3632 m_peer_change_queue.push_back(c);
3635 void Server::deletingPeer(con::Peer *peer, bool timeout)
3637 DSTACK(__FUNCTION_NAME);
3638 dout_server<<"Server::deletingPeer(): peer->id="
3639 <<peer->id<<", timeout="<<timeout<<std::endl;
3642 c.type = PEER_REMOVED;
3643 c.peer_id = peer->id;
3644 c.timeout = timeout;
3645 m_peer_change_queue.push_back(c);
3652 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3654 DSTACK(__FUNCTION_NAME);
3655 std::ostringstream os(std::ios_base::binary);
3657 writeU16(os, TOCLIENT_HP);
3661 std::string s = os.str();
3662 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3664 con.Send(peer_id, 0, data, true);
3667 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3668 const std::wstring &reason)
3670 DSTACK(__FUNCTION_NAME);
3671 std::ostringstream os(std::ios_base::binary);
3673 writeU16(os, TOCLIENT_ACCESS_DENIED);
3674 os<<serializeWideString(reason);
3677 std::string s = os.str();
3678 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3680 con.Send(peer_id, 0, data, true);
3684 Non-static send methods
3687 void Server::SendObjectData(float dtime)
3689 DSTACK(__FUNCTION_NAME);
3691 core::map<v3s16, bool> stepped_blocks;
3693 for(core::map<u16, RemoteClient*>::Iterator
3694 i = m_clients.getIterator();
3695 i.atEnd() == false; i++)
3697 u16 peer_id = i.getNode()->getKey();
3698 RemoteClient *client = i.getNode()->getValue();
3699 assert(client->peer_id == peer_id);
3701 if(client->serialization_version == SER_FMT_VER_INVALID)
3704 client->SendObjectData(this, dtime, stepped_blocks);
3708 void Server::SendPlayerInfos()
3710 DSTACK(__FUNCTION_NAME);
3712 //JMutexAutoLock envlock(m_env_mutex);
3714 // Get connected players
3715 core::list<Player*> players = m_env.getPlayers(true);
3717 u32 player_count = players.getSize();
3718 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3720 SharedBuffer<u8> data(datasize);
3721 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3724 core::list<Player*>::Iterator i;
3725 for(i = players.begin();
3726 i != players.end(); i++)
3728 Player *player = *i;
3730 /*dstream<<"Server sending player info for player with "
3731 "peer_id="<<player->peer_id<<std::endl;*/
3733 writeU16(&data[start], player->peer_id);
3734 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3735 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3736 start += 2+PLAYERNAME_SIZE;
3739 //JMutexAutoLock conlock(m_con_mutex);
3742 m_con.SendToAll(0, data, true);
3745 void Server::SendInventory(u16 peer_id)
3747 DSTACK(__FUNCTION_NAME);
3749 Player* player = m_env.getPlayer(peer_id);
3756 std::ostringstream os;
3757 //os.imbue(std::locale("C"));
3759 player->inventory.serialize(os);
3761 std::string s = os.str();
3763 SharedBuffer<u8> data(s.size()+2);
3764 writeU16(&data[0], TOCLIENT_INVENTORY);
3765 memcpy(&data[2], s.c_str(), s.size());
3768 m_con.Send(peer_id, 0, data, true);
3771 std::string getWieldedItemString(const Player *player)
3773 const InventoryItem *item = player->getWieldItem();
3775 return std::string("");
3776 std::ostringstream os(std::ios_base::binary);
3777 item->serialize(os);
3781 void Server::SendWieldedItem(const Player* player)
3783 DSTACK(__FUNCTION_NAME);
3787 std::ostringstream os(std::ios_base::binary);
3789 writeU16(os, TOCLIENT_PLAYERITEM);
3791 writeU16(os, player->peer_id);
3792 os<<serializeString(getWieldedItemString(player));
3795 std::string s = os.str();
3796 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3798 m_con.SendToAll(0, data, true);
3801 void Server::SendPlayerItems()
3803 DSTACK(__FUNCTION_NAME);
3805 std::ostringstream os(std::ios_base::binary);
3806 core::list<Player *> players = m_env.getPlayers(true);
3808 writeU16(os, TOCLIENT_PLAYERITEM);
3809 writeU16(os, players.size());
3810 core::list<Player *>::Iterator i;
3811 for(i = players.begin(); i != players.end(); ++i)
3814 writeU16(os, p->peer_id);
3815 os<<serializeString(getWieldedItemString(p));
3819 std::string s = os.str();
3820 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3822 m_con.SendToAll(0, data, true);
3825 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3827 DSTACK(__FUNCTION_NAME);
3829 std::ostringstream os(std::ios_base::binary);
3833 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3834 os.write((char*)buf, 2);
3837 writeU16(buf, message.size());
3838 os.write((char*)buf, 2);
3841 for(u32 i=0; i<message.size(); i++)
3845 os.write((char*)buf, 2);
3849 std::string s = os.str();
3850 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3852 m_con.Send(peer_id, 0, data, true);
3855 void Server::BroadcastChatMessage(const std::wstring &message)
3857 for(core::map<u16, RemoteClient*>::Iterator
3858 i = m_clients.getIterator();
3859 i.atEnd() == false; i++)
3861 // Get client and check that it is valid
3862 RemoteClient *client = i.getNode()->getValue();
3863 assert(client->peer_id == i.getNode()->getKey());
3864 if(client->serialization_version == SER_FMT_VER_INVALID)
3867 SendChatMessage(client->peer_id, message);
3871 void Server::SendPlayerHP(Player *player)
3873 SendHP(m_con, player->peer_id, player->hp);
3876 void Server::SendMovePlayer(Player *player)
3878 DSTACK(__FUNCTION_NAME);
3879 std::ostringstream os(std::ios_base::binary);
3881 writeU16(os, TOCLIENT_MOVE_PLAYER);
3882 writeV3F1000(os, player->getPosition());
3883 writeF1000(os, player->getPitch());
3884 writeF1000(os, player->getYaw());
3887 v3f pos = player->getPosition();
3888 f32 pitch = player->getPitch();
3889 f32 yaw = player->getYaw();
3890 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3891 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3898 std::string s = os.str();
3899 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3901 m_con.Send(player->peer_id, 0, data, true);
3904 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3905 core::list<u16> *far_players, float far_d_nodes)
3907 float maxd = far_d_nodes*BS;
3908 v3f p_f = intToFloat(p, BS);
3912 SharedBuffer<u8> reply(replysize);
3913 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3914 writeS16(&reply[2], p.X);
3915 writeS16(&reply[4], p.Y);
3916 writeS16(&reply[6], p.Z);
3918 for(core::map<u16, RemoteClient*>::Iterator
3919 i = m_clients.getIterator();
3920 i.atEnd() == false; i++)
3922 // Get client and check that it is valid
3923 RemoteClient *client = i.getNode()->getValue();
3924 assert(client->peer_id == i.getNode()->getKey());
3925 if(client->serialization_version == SER_FMT_VER_INVALID)
3928 // Don't send if it's the same one
3929 if(client->peer_id == ignore_id)
3935 Player *player = m_env.getPlayer(client->peer_id);
3938 // If player is far away, only set modified blocks not sent
3939 v3f player_pos = player->getPosition();
3940 if(player_pos.getDistanceFrom(p_f) > maxd)
3942 far_players->push_back(client->peer_id);
3949 m_con.Send(client->peer_id, 0, reply, true);
3953 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3954 core::list<u16> *far_players, float far_d_nodes)
3956 float maxd = far_d_nodes*BS;
3957 v3f p_f = intToFloat(p, BS);
3959 for(core::map<u16, RemoteClient*>::Iterator
3960 i = m_clients.getIterator();
3961 i.atEnd() == false; i++)
3963 // Get client and check that it is valid
3964 RemoteClient *client = i.getNode()->getValue();
3965 assert(client->peer_id == i.getNode()->getKey());
3966 if(client->serialization_version == SER_FMT_VER_INVALID)
3969 // Don't send if it's the same one
3970 if(client->peer_id == ignore_id)
3976 Player *player = m_env.getPlayer(client->peer_id);
3979 // If player is far away, only set modified blocks not sent
3980 v3f player_pos = player->getPosition();
3981 if(player_pos.getDistanceFrom(p_f) > maxd)
3983 far_players->push_back(client->peer_id);
3990 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3991 SharedBuffer<u8> reply(replysize);
3992 writeU16(&reply[0], TOCLIENT_ADDNODE);
3993 writeS16(&reply[2], p.X);
3994 writeS16(&reply[4], p.Y);
3995 writeS16(&reply[6], p.Z);
3996 n.serialize(&reply[8], client->serialization_version);
3999 m_con.Send(client->peer_id, 0, reply, true);
4003 void Server::setBlockNotSent(v3s16 p)
4005 for(core::map<u16, RemoteClient*>::Iterator
4006 i = m_clients.getIterator();
4007 i.atEnd()==false; i++)
4009 RemoteClient *client = i.getNode()->getValue();
4010 client->SetBlockNotSent(p);
4014 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4016 DSTACK(__FUNCTION_NAME);
4018 v3s16 p = block->getPos();
4022 bool completely_air = true;
4023 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4024 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4025 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4027 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4029 completely_air = false;
4030 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4035 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4037 dstream<<"[completely air] ";
4042 Create a packet with the block in the right format
4045 std::ostringstream os(std::ios_base::binary);
4046 block->serialize(os, ver);
4047 std::string s = os.str();
4048 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4050 u32 replysize = 8 + blockdata.getSize();
4051 SharedBuffer<u8> reply(replysize);
4052 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4053 writeS16(&reply[2], p.X);
4054 writeS16(&reply[4], p.Y);
4055 writeS16(&reply[6], p.Z);
4056 memcpy(&reply[8], *blockdata, blockdata.getSize());
4058 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4059 <<": \tpacket size: "<<replysize<<std::endl;*/
4064 m_con.Send(peer_id, 1, reply, true);
4067 void Server::SendBlocks(float dtime)
4069 DSTACK(__FUNCTION_NAME);
4071 JMutexAutoLock envlock(m_env_mutex);
4072 JMutexAutoLock conlock(m_con_mutex);
4074 //TimeTaker timer("Server::SendBlocks");
4076 core::array<PrioritySortedBlockTransfer> queue;
4078 s32 total_sending = 0;
4081 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
4083 for(core::map<u16, RemoteClient*>::Iterator
4084 i = m_clients.getIterator();
4085 i.atEnd() == false; i++)
4087 RemoteClient *client = i.getNode()->getValue();
4088 assert(client->peer_id == i.getNode()->getKey());
4090 total_sending += client->SendingCount();
4092 if(client->serialization_version == SER_FMT_VER_INVALID)
4095 client->GetNextBlocks(this, dtime, queue);
4100 // Lowest priority number comes first.
4101 // Lowest is most important.
4104 for(u32 i=0; i<queue.size(); i++)
4106 //TODO: Calculate limit dynamically
4107 if(total_sending >= g_settings.getS32
4108 ("max_simultaneous_block_sends_server_total"))
4111 PrioritySortedBlockTransfer q = queue[i];
4113 MapBlock *block = NULL;
4116 block = m_env.getMap().getBlockNoCreate(q.pos);
4118 catch(InvalidPositionException &e)
4123 RemoteClient *client = getClient(q.peer_id);
4125 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4127 client->SentBlock(q.pos);
4137 void Server::UpdateCrafting(u16 peer_id)
4139 DSTACK(__FUNCTION_NAME);
4141 Player* player = m_env.getPlayer(peer_id);
4145 Calculate crafting stuff
4147 if(g_settings.getBool("creative_mode") == false)
4149 InventoryList *clist = player->inventory.getList("craft");
4150 InventoryList *rlist = player->inventory.getList("craftresult");
4152 if(rlist && rlist->getUsedSlots() == 0)
4153 player->craftresult_is_preview = true;
4155 if(rlist && player->craftresult_is_preview)
4157 rlist->clearItems();
4159 if(clist && rlist && player->craftresult_is_preview)
4161 InventoryItem *items[9];
4162 for(u16 i=0; i<9; i++)
4164 items[i] = clist->getItem(i);
4167 // Get result of crafting grid
4168 InventoryItem *result = craft_get_result(items);
4170 rlist->addItem(result);
4173 } // if creative_mode == false
4176 RemoteClient* Server::getClient(u16 peer_id)
4178 DSTACK(__FUNCTION_NAME);
4179 //JMutexAutoLock lock(m_con_mutex);
4180 core::map<u16, RemoteClient*>::Node *n;
4181 n = m_clients.find(peer_id);
4182 // A client should exist for all peers
4184 return n->getValue();
4187 std::wstring Server::getStatusString()
4189 std::wostringstream os(std::ios_base::binary);
4192 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4194 os<<L", uptime="<<m_uptime.get();
4195 // Information about clients
4197 for(core::map<u16, RemoteClient*>::Iterator
4198 i = m_clients.getIterator();
4199 i.atEnd() == false; i++)
4201 // Get client and check that it is valid
4202 RemoteClient *client = i.getNode()->getValue();
4203 assert(client->peer_id == i.getNode()->getKey());
4204 if(client->serialization_version == SER_FMT_VER_INVALID)
4207 Player *player = m_env.getPlayer(client->peer_id);
4208 // Get name of player
4209 std::wstring name = L"unknown";
4211 name = narrow_to_wide(player->getName());
4212 // Add name to information string
4216 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4217 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4218 if(g_settings.get("motd") != "")
4219 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4223 v3f findSpawnPos(ServerMap &map)
4225 //return v3f(50,50,50)*BS;
4228 s16 groundheight = 0;
4231 nodepos = v2s16(0,0);
4236 // Try to find a good place a few times
4237 for(s32 i=0; i<1000; i++)
4240 // We're going to try to throw the player to this position
4241 nodepos = v2s16(-range + (myrand()%(range*2)),
4242 -range + (myrand()%(range*2)));
4243 v2s16 sectorpos = getNodeSectorPos(nodepos);
4244 // Get sector (NOTE: Don't get because it's slow)
4245 //m_env.getMap().emergeSector(sectorpos);
4246 // Get ground height at point (fallbacks to heightmap function)
4247 groundheight = map.findGroundLevel(nodepos);
4248 // Don't go underwater
4249 if(groundheight < WATER_LEVEL)
4251 //dstream<<"-> Underwater"<<std::endl;
4254 // Don't go to high places
4255 if(groundheight > WATER_LEVEL + 4)
4257 //dstream<<"-> Underwater"<<std::endl;
4261 // Found a good place
4262 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4267 // If no suitable place was not found, go above water at least.
4268 if(groundheight < WATER_LEVEL)
4269 groundheight = WATER_LEVEL;
4271 return intToFloat(v3s16(
4278 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4281 Try to get an existing player
4283 Player *player = m_env.getPlayer(name);
4286 // If player is already connected, cancel
4287 if(player->peer_id != 0)
4289 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4294 player->peer_id = peer_id;
4296 // Reset inventory to creative if in creative mode
4297 if(g_settings.getBool("creative_mode"))
4299 // Warning: double code below
4300 // Backup actual inventory
4301 player->inventory_backup = new Inventory();
4302 *(player->inventory_backup) = player->inventory;
4303 // Set creative inventory
4304 craft_set_creative_inventory(player);
4311 If player with the wanted peer_id already exists, cancel.
4313 if(m_env.getPlayer(peer_id) != NULL)
4315 dstream<<"emergePlayer(): Player with wrong name but same"
4316 " peer_id already exists"<<std::endl;
4324 player = new ServerRemotePlayer();
4325 //player->peer_id = c.peer_id;
4326 //player->peer_id = PEER_ID_INEXISTENT;
4327 player->peer_id = peer_id;
4328 player->updateName(name);
4329 m_authmanager.add(name);
4330 m_authmanager.setPassword(name, password);
4331 m_authmanager.setPrivs(name,
4332 stringToPrivs(g_settings.get("default_privs")));
4338 dstream<<"Server: Finding spawn place for player \""
4339 <<player->getName()<<"\""<<std::endl;
4341 v3f pos = findSpawnPos(m_env.getServerMap());
4343 player->setPosition(pos);
4346 Add player to environment
4349 m_env.addPlayer(player);
4352 Add stuff to inventory
4355 if(g_settings.getBool("creative_mode"))
4357 // Warning: double code above
4358 // Backup actual inventory
4359 player->inventory_backup = new Inventory();
4360 *(player->inventory_backup) = player->inventory;
4361 // Set creative inventory
4362 craft_set_creative_inventory(player);
4364 else if(g_settings.getBool("give_initial_stuff"))
4366 craft_give_initial_stuff(player);
4371 } // create new player
4374 void Server::handlePeerChange(PeerChange &c)
4376 JMutexAutoLock envlock(m_env_mutex);
4377 JMutexAutoLock conlock(m_con_mutex);
4379 if(c.type == PEER_ADDED)
4386 core::map<u16, RemoteClient*>::Node *n;
4387 n = m_clients.find(c.peer_id);
4388 // The client shouldn't already exist
4392 RemoteClient *client = new RemoteClient();
4393 client->peer_id = c.peer_id;
4394 m_clients.insert(client->peer_id, client);
4397 else if(c.type == PEER_REMOVED)
4404 core::map<u16, RemoteClient*>::Node *n;
4405 n = m_clients.find(c.peer_id);
4406 // The client should exist
4410 Mark objects to be not known by the client
4412 RemoteClient *client = n->getValue();
4414 for(core::map<u16, bool>::Iterator
4415 i = client->m_known_objects.getIterator();
4416 i.atEnd()==false; i++)
4419 u16 id = i.getNode()->getKey();
4420 ServerActiveObject* obj = m_env.getActiveObject(id);
4422 if(obj && obj->m_known_by_count > 0)
4423 obj->m_known_by_count--;
4426 // Collect information about leaving in chat
4427 std::wstring message;
4429 std::wstring name = L"unknown";
4430 Player *player = m_env.getPlayer(c.peer_id);
4432 name = narrow_to_wide(player->getName());
4436 message += L" left game";
4438 message += L" (timed out)";
4443 m_env.removePlayer(c.peer_id);
4446 // Set player client disconnected
4448 Player *player = m_env.getPlayer(c.peer_id);
4450 player->peer_id = 0;
4454 delete m_clients[c.peer_id];
4455 m_clients.remove(c.peer_id);
4457 // Send player info to all remaining clients
4460 // Send leave chat message to all remaining clients
4461 BroadcastChatMessage(message);
4470 void Server::handlePeerChanges()
4472 while(m_peer_change_queue.size() > 0)
4474 PeerChange c = m_peer_change_queue.pop_front();
4476 dout_server<<"Server: Handling peer change: "
4477 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4480 handlePeerChange(c);
4484 u64 Server::getPlayerPrivs(Player *player)
4488 std::string playername = player->getName();
4489 // Local player gets all privileges regardless of
4490 // what's set on their account.
4491 if(g_settings.get("name") == playername)
4497 return getPlayerAuthPrivs(playername);
4501 void dedicated_server_loop(Server &server, bool &kill)
4503 DSTACK(__FUNCTION_NAME);
4505 dstream<<DTIME<<std::endl;
4506 dstream<<"========================"<<std::endl;
4507 dstream<<"Running dedicated server"<<std::endl;
4508 dstream<<"========================"<<std::endl;
4511 IntervalLimiter m_profiler_interval;
4515 // This is kind of a hack but can be done like this
4516 // because server.step() is very light
4518 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4523 if(server.getShutdownRequested() || kill)
4525 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4532 float profiler_print_interval =
4533 g_settings.getFloat("profiler_print_interval");
4534 if(profiler_print_interval != 0)
4536 if(m_profiler_interval.step(0.030, profiler_print_interval))
4538 dstream<<"Profiler:"<<std::endl;
4539 g_profiler.print(dstream);
4547 static int counter = 0;
4553 core::list<PlayerInfo> list = server.getPlayerInfo();
4554 core::list<PlayerInfo>::Iterator i;
4555 static u32 sum_old = 0;
4556 u32 sum = PIChecksum(list);
4559 dstream<<DTIME<<"Player info:"<<std::endl;
4560 for(i=list.begin(); i!=list.end(); i++)
4562 i->PrintLine(&dstream);