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();
2100 // Enforce user limit.
2101 // Don't enforce for users that have some admin right
2102 if(m_clients.size() >= g_settings.getU16("max_users") &&
2103 (m_authmanager.getPrivs(playername)
2104 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2105 playername != g_settings.get("name"))
2107 SendAccessDenied(m_con, peer_id, L"Too many users.");
2112 Player *player = emergePlayer(playername, password, peer_id);
2115 // DEBUG: Test serialization
2116 std::ostringstream test_os;
2117 player->serialize(test_os);
2118 dstream<<"Player serialization test: \""<<test_os.str()
2120 std::istringstream test_is(test_os.str());
2121 player->deSerialize(test_is);
2124 // If failed, cancel
2127 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2128 <<": failed to emerge player"<<std::endl;
2133 // If a client is already connected to the player, cancel
2134 if(player->peer_id != 0)
2136 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2137 <<" tried to connect to "
2138 "an already connected player (peer_id="
2139 <<player->peer_id<<")"<<std::endl;
2142 // Set client of player
2143 player->peer_id = peer_id;
2146 // Check if player doesn't exist
2148 throw con::InvalidIncomingDataException
2149 ("Server::ProcessData(): INIT: Player doesn't exist");
2151 /*// update name if it was supplied
2152 if(datasize >= 20+3)
2155 player->updateName((const char*)&data[3]);
2159 Answer with a TOCLIENT_INIT
2162 SharedBuffer<u8> reply(2+1+6+8);
2163 writeU16(&reply[0], TOCLIENT_INIT);
2164 writeU8(&reply[2], deployed);
2165 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2166 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2169 m_con.Send(peer_id, 0, reply, true);
2173 Send complete position information
2175 SendMovePlayer(player);
2180 if(command == TOSERVER_INIT2)
2182 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2183 <<peer->id<<std::endl;
2186 getClient(peer->id)->serialization_version
2187 = getClient(peer->id)->pending_serialization_version;
2190 Send some initialization data
2193 // Send player info to all players
2196 // Send inventory to player
2197 UpdateCrafting(peer->id);
2198 SendInventory(peer->id);
2200 // Send player items to all players
2205 Player *player = m_env.getPlayer(peer_id);
2206 SendPlayerHP(player);
2211 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2212 m_env.getTimeOfDay());
2213 m_con.Send(peer->id, 0, data, true);
2216 // Send information about server to player in chat
2217 SendChatMessage(peer_id, getStatusString());
2219 // Send information about joining in chat
2221 std::wstring name = L"unknown";
2222 Player *player = m_env.getPlayer(peer_id);
2224 name = narrow_to_wide(player->getName());
2226 std::wstring message;
2229 message += L" joined game";
2230 BroadcastChatMessage(message);
2233 // Warnings about protocol version can be issued here
2234 /*if(getClient(peer->id)->net_proto_version == 0)
2236 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2242 if(peer_ser_ver == SER_FMT_VER_INVALID)
2244 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2245 " serialization format invalid or not initialized."
2246 " Skipping incoming command="<<command<<std::endl;
2250 Player *player = m_env.getPlayer(peer_id);
2253 derr_server<<"Server::ProcessData(): Cancelling: "
2254 "No player for peer_id="<<peer_id
2258 if(command == TOSERVER_PLAYERPOS)
2260 if(datasize < 2+12+12+4+4)
2264 v3s32 ps = readV3S32(&data[start+2]);
2265 v3s32 ss = readV3S32(&data[start+2+12]);
2266 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2267 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2268 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2269 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2270 pitch = wrapDegrees(pitch);
2271 yaw = wrapDegrees(yaw);
2272 player->setPosition(position);
2273 player->setSpeed(speed);
2274 player->setPitch(pitch);
2275 player->setYaw(yaw);
2277 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2278 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2279 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2281 else if(command == TOSERVER_GOTBLOCKS)
2294 u16 count = data[2];
2295 for(u16 i=0; i<count; i++)
2297 if((s16)datasize < 2+1+(i+1)*6)
2298 throw con::InvalidIncomingDataException
2299 ("GOTBLOCKS length is too short");
2300 v3s16 p = readV3S16(&data[2+1+i*6]);
2301 /*dstream<<"Server: GOTBLOCKS ("
2302 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2303 RemoteClient *client = getClient(peer_id);
2304 client->GotBlock(p);
2307 else if(command == TOSERVER_DELETEDBLOCKS)
2320 u16 count = data[2];
2321 for(u16 i=0; i<count; i++)
2323 if((s16)datasize < 2+1+(i+1)*6)
2324 throw con::InvalidIncomingDataException
2325 ("DELETEDBLOCKS length is too short");
2326 v3s16 p = readV3S16(&data[2+1+i*6]);
2327 /*dstream<<"Server: DELETEDBLOCKS ("
2328 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2329 RemoteClient *client = getClient(peer_id);
2330 client->SetBlockNotSent(p);
2333 else if(command == TOSERVER_CLICK_OBJECT)
2338 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2343 [2] u8 button (0=left, 1=right)
2348 u8 button = readU8(&data[2]);
2350 p.X = readS16(&data[3]);
2351 p.Y = readS16(&data[5]);
2352 p.Z = readS16(&data[7]);
2353 s16 id = readS16(&data[9]);
2354 //u16 item_i = readU16(&data[11]);
2356 MapBlock *block = NULL;
2359 block = m_env.getMap().getBlockNoCreate(p);
2361 catch(InvalidPositionException &e)
2363 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2367 MapBlockObject *obj = block->getObject(id);
2371 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2375 //TODO: Check that object is reasonably close
2380 InventoryList *ilist = player->inventory.getList("main");
2381 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2384 // Skip if inventory has no free space
2385 if(ilist->getUsedSlots() == ilist->getSize())
2387 dout_server<<"Player inventory has no free space"<<std::endl;
2392 Create the inventory item
2394 InventoryItem *item = NULL;
2395 // If it is an item-object, take the item from it
2396 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2398 item = ((ItemObject*)obj)->createInventoryItem();
2400 // Else create an item of the object
2403 item = new MapBlockObjectItem
2404 (obj->getInventoryString());
2407 // Add to inventory and send inventory
2408 ilist->addItem(item);
2409 UpdateCrafting(player->peer_id);
2410 SendInventory(player->peer_id);
2413 // Remove from block
2414 block->removeObject(id);
2417 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2422 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2428 [2] u8 button (0=left, 1=right)
2432 u8 button = readU8(&data[2]);
2433 u16 id = readS16(&data[3]);
2434 u16 item_i = readU16(&data[11]);
2436 ServerActiveObject *obj = m_env.getActiveObject(id);
2440 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2445 // Skip if object has been removed
2449 //TODO: Check that object is reasonably close
2451 // Left click, pick object up (usually)
2455 Try creating inventory item
2457 InventoryItem *item = obj->createPickedUpItem();
2461 InventoryList *ilist = player->inventory.getList("main");
2464 if(g_settings.getBool("creative_mode") == false)
2466 // Skip if inventory has no free space
2467 if(ilist->roomForItem(item) == false)
2469 dout_server<<"Player inventory has no free space"<<std::endl;
2473 // Add to inventory and send inventory
2474 ilist->addItem(item);
2475 UpdateCrafting(player->peer_id);
2476 SendInventory(player->peer_id);
2479 // Remove object from environment
2480 obj->m_removed = true;
2486 Item cannot be picked up. Punch it instead.
2489 ToolItem *titem = NULL;
2490 std::string toolname = "";
2492 InventoryList *mlist = player->inventory.getList("main");
2495 InventoryItem *item = mlist->getItem(item_i);
2496 if(item && (std::string)item->getName() == "ToolItem")
2498 titem = (ToolItem*)item;
2499 toolname = titem->getToolName();
2503 v3f playerpos = player->getPosition();
2504 v3f objpos = obj->getBasePosition();
2505 v3f dir = (objpos - playerpos).normalize();
2507 u16 wear = obj->punch(toolname, dir);
2511 bool weared_out = titem->addWear(wear);
2513 mlist->deleteItem(item_i);
2514 SendInventory(player->peer_id);
2518 // Right click, do something with object
2521 // Track hp changes super-crappily
2522 u16 oldhp = player->hp;
2525 obj->rightClick(player);
2528 if(player->hp != oldhp)
2530 SendPlayerHP(player);
2534 else if(command == TOSERVER_GROUND_ACTION)
2542 [3] v3s16 nodepos_undersurface
2543 [9] v3s16 nodepos_abovesurface
2548 2: stop digging (all parameters ignored)
2549 3: digging completed
2551 u8 action = readU8(&data[2]);
2553 p_under.X = readS16(&data[3]);
2554 p_under.Y = readS16(&data[5]);
2555 p_under.Z = readS16(&data[7]);
2557 p_over.X = readS16(&data[9]);
2558 p_over.Y = readS16(&data[11]);
2559 p_over.Z = readS16(&data[13]);
2560 u16 item_i = readU16(&data[15]);
2562 //TODO: Check that target is reasonably close
2570 NOTE: This can be used in the future to check if
2571 somebody is cheating, by checking the timing.
2578 else if(action == 2)
2581 RemoteClient *client = getClient(peer->id);
2582 JMutexAutoLock digmutex(client->m_dig_mutex);
2583 client->m_dig_tool_item = -1;
2588 3: Digging completed
2590 else if(action == 3)
2592 // Mandatory parameter; actually used for nothing
2593 core::map<v3s16, MapBlock*> modified_blocks;
2595 content_t material = CONTENT_IGNORE;
2596 u8 mineral = MINERAL_NONE;
2598 bool cannot_remove_node = false;
2602 MapNode n = m_env.getMap().getNode(p_under);
2604 mineral = n.getMineral();
2605 // Get material at position
2606 material = n.getContent();
2607 // If not yet cancelled
2608 if(cannot_remove_node == false)
2610 // If it's not diggable, do nothing
2611 if(content_diggable(material) == false)
2613 derr_server<<"Server: Not finishing digging: "
2614 <<"Node not diggable"
2616 cannot_remove_node = true;
2619 // If not yet cancelled
2620 if(cannot_remove_node == false)
2622 // Get node metadata
2623 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2624 if(meta && meta->nodeRemovalDisabled() == true)
2626 derr_server<<"Server: Not finishing digging: "
2627 <<"Node metadata disables removal"
2629 cannot_remove_node = true;
2633 catch(InvalidPositionException &e)
2635 derr_server<<"Server: Not finishing digging: Node not found."
2636 <<" Adding block to emerge queue."
2638 m_emerge_queue.addBlock(peer_id,
2639 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2640 cannot_remove_node = true;
2643 // Make sure the player is allowed to do it
2644 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2646 dstream<<"Player "<<player->getName()<<" cannot remove node"
2647 <<" because privileges are "<<getPlayerPrivs(player)
2649 cannot_remove_node = true;
2653 If node can't be removed, set block to be re-sent to
2656 if(cannot_remove_node)
2658 derr_server<<"Server: Not finishing digging."<<std::endl;
2660 // Client probably has wrong data.
2661 // Set block not sent, so that client will get
2663 dstream<<"Client "<<peer_id<<" tried to dig "
2664 <<"node; but node cannot be removed."
2665 <<" setting MapBlock not sent."<<std::endl;
2666 RemoteClient *client = getClient(peer_id);
2667 v3s16 blockpos = getNodeBlockPos(p_under);
2668 client->SetBlockNotSent(blockpos);
2674 Send the removal to all close-by players.
2675 - If other player is close, send REMOVENODE
2676 - Otherwise set blocks not sent
2678 core::list<u16> far_players;
2679 sendRemoveNode(p_under, peer_id, &far_players, 30);
2682 Update and send inventory
2685 if(g_settings.getBool("creative_mode") == false)
2690 InventoryList *mlist = player->inventory.getList("main");
2693 InventoryItem *item = mlist->getItem(item_i);
2694 if(item && (std::string)item->getName() == "ToolItem")
2696 ToolItem *titem = (ToolItem*)item;
2697 std::string toolname = titem->getToolName();
2699 // Get digging properties for material and tool
2700 DiggingProperties prop =
2701 getDiggingProperties(material, toolname);
2703 if(prop.diggable == false)
2705 derr_server<<"Server: WARNING: Player digged"
2706 <<" with impossible material + tool"
2707 <<" combination"<<std::endl;
2710 bool weared_out = titem->addWear(prop.wear);
2714 mlist->deleteItem(item_i);
2720 Add dug item to inventory
2723 InventoryItem *item = NULL;
2725 if(mineral != MINERAL_NONE)
2726 item = getDiggedMineralItem(mineral);
2731 std::string &dug_s = content_features(material).dug_item;
2734 std::istringstream is(dug_s, std::ios::binary);
2735 item = InventoryItem::deSerialize(is);
2741 // Add a item to inventory
2742 player->inventory.addItem("main", item);
2745 UpdateCrafting(player->peer_id);
2746 SendInventory(player->peer_id);
2752 (this takes some time so it is done after the quick stuff)
2755 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2757 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2760 Set blocks not sent to far players
2762 for(core::list<u16>::Iterator
2763 i = far_players.begin();
2764 i != far_players.end(); i++)
2767 RemoteClient *client = getClient(peer_id);
2770 client->SetBlocksNotSent(modified_blocks);
2777 else if(action == 1)
2780 InventoryList *ilist = player->inventory.getList("main");
2785 InventoryItem *item = ilist->getItem(item_i);
2787 // If there is no item, it is not possible to add it anywhere
2792 Handle material items
2794 if(std::string("MaterialItem") == item->getName())
2797 // Don't add a node if this is not a free space
2798 MapNode n2 = m_env.getMap().getNode(p_over);
2799 bool no_enough_privs =
2800 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2802 dstream<<"Player "<<player->getName()<<" cannot add node"
2803 <<" because privileges are "<<getPlayerPrivs(player)
2806 if(content_features(n2).buildable_to == false
2809 // Client probably has wrong data.
2810 // Set block not sent, so that client will get
2812 dstream<<"Client "<<peer_id<<" tried to place"
2813 <<" node in invalid position; setting"
2814 <<" MapBlock not sent."<<std::endl;
2815 RemoteClient *client = getClient(peer_id);
2816 v3s16 blockpos = getNodeBlockPos(p_over);
2817 client->SetBlockNotSent(blockpos);
2821 catch(InvalidPositionException &e)
2823 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2824 <<" Adding block to emerge queue."
2826 m_emerge_queue.addBlock(peer_id,
2827 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2831 // Reset build time counter
2832 getClient(peer->id)->m_time_from_building = 0.0;
2835 MaterialItem *mitem = (MaterialItem*)item;
2837 n.setContent(mitem->getMaterial());
2839 // Calculate direction for wall mounted stuff
2840 if(content_features(n).wall_mounted)
2841 n.param2 = packDir(p_under - p_over);
2843 // Calculate the direction for furnaces and chests and stuff
2844 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2846 v3f playerpos = player->getPosition();
2847 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2848 blockpos = blockpos.normalize();
2850 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2864 Send to all close-by players
2866 core::list<u16> far_players;
2867 sendAddNode(p_over, n, 0, &far_players, 30);
2872 InventoryList *ilist = player->inventory.getList("main");
2873 if(g_settings.getBool("creative_mode") == false && ilist)
2875 // Remove from inventory and send inventory
2876 if(mitem->getCount() == 1)
2877 ilist->deleteItem(item_i);
2881 UpdateCrafting(peer_id);
2882 SendInventory(peer_id);
2888 This takes some time so it is done after the quick stuff
2890 core::map<v3s16, MapBlock*> modified_blocks;
2892 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2894 std::string p_name = std::string(player->getName());
2895 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2898 Set blocks not sent to far players
2900 for(core::list<u16>::Iterator
2901 i = far_players.begin();
2902 i != far_players.end(); i++)
2905 RemoteClient *client = getClient(peer_id);
2908 client->SetBlocksNotSent(modified_blocks);
2912 Calculate special events
2915 /*if(n.d == CONTENT_MESE)
2918 for(s16 z=-1; z<=1; z++)
2919 for(s16 y=-1; y<=1; y++)
2920 for(s16 x=-1; x<=1; x++)
2927 Place other item (not a block)
2931 v3s16 blockpos = getNodeBlockPos(p_over);
2934 Check that the block is loaded so that the item
2935 can properly be added to the static list too
2937 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2940 derr_server<<"Error while placing object: "
2941 "block not found"<<std::endl;
2946 If in creative mode, item dropping is disabled unless
2947 player has build privileges
2949 if(g_settings.getBool("creative_mode") &&
2950 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2952 derr_server<<"Not allowing player to drop item: "
2953 "creative mode and no build privs"<<std::endl;
2957 dout_server<<"Placing a miscellaneous item on map"
2960 // Calculate a position for it
2961 v3f pos = intToFloat(p_over, BS);
2963 pos.Y -= BS*0.25; // let it drop a bit
2965 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2966 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2971 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2975 derr_server<<"WARNING: item resulted in NULL object, "
2976 <<"not placing onto map"
2981 // Add the object to the environment
2982 m_env.addActiveObject(obj);
2984 dout_server<<"Placed object"<<std::endl;
2986 if(g_settings.getBool("creative_mode") == false)
2988 // Delete the right amount of items from the slot
2989 u16 dropcount = item->getDropCount();
2991 // Delete item if all gone
2992 if(item->getCount() <= dropcount)
2994 if(item->getCount() < dropcount)
2995 dstream<<"WARNING: Server: dropped more items"
2996 <<" than the slot contains"<<std::endl;
2998 InventoryList *ilist = player->inventory.getList("main");
3000 // Remove from inventory and send inventory
3001 ilist->deleteItem(item_i);
3003 // Else decrement it
3005 item->remove(dropcount);
3008 UpdateCrafting(peer_id);
3009 SendInventory(peer_id);
3017 Catch invalid actions
3021 derr_server<<"WARNING: Server: Invalid action "
3022 <<action<<std::endl;
3026 else if(command == TOSERVER_RELEASE)
3035 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3038 else if(command == TOSERVER_SIGNTEXT)
3040 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3049 std::string datastring((char*)&data[2], datasize-2);
3050 std::istringstream is(datastring, std::ios_base::binary);
3053 is.read((char*)buf, 6);
3054 v3s16 blockpos = readV3S16(buf);
3055 is.read((char*)buf, 2);
3056 s16 id = readS16(buf);
3057 is.read((char*)buf, 2);
3058 u16 textlen = readU16(buf);
3060 for(u16 i=0; i<textlen; i++)
3062 is.read((char*)buf, 1);
3063 text += (char)buf[0];
3066 MapBlock *block = NULL;
3069 block = m_env.getMap().getBlockNoCreate(blockpos);
3071 catch(InvalidPositionException &e)
3073 derr_server<<"Error while setting sign text: "
3074 "block not found"<<std::endl;
3078 MapBlockObject *obj = block->getObject(id);
3081 derr_server<<"Error while setting sign text: "
3082 "object not found"<<std::endl;
3086 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3088 derr_server<<"Error while setting sign text: "
3089 "object is not a sign"<<std::endl;
3093 ((SignObject*)obj)->setText(text);
3095 obj->getBlock()->setChangedFlag();
3097 else if(command == TOSERVER_SIGNNODETEXT)
3099 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3107 std::string datastring((char*)&data[2], datasize-2);
3108 std::istringstream is(datastring, std::ios_base::binary);
3111 is.read((char*)buf, 6);
3112 v3s16 p = readV3S16(buf);
3113 is.read((char*)buf, 2);
3114 u16 textlen = readU16(buf);
3116 for(u16 i=0; i<textlen; i++)
3118 is.read((char*)buf, 1);
3119 text += (char)buf[0];
3122 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3125 if(meta->typeId() != CONTENT_SIGN_WALL)
3127 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3128 signmeta->setText(text);
3130 v3s16 blockpos = getNodeBlockPos(p);
3131 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3134 block->setChangedFlag();
3137 for(core::map<u16, RemoteClient*>::Iterator
3138 i = m_clients.getIterator();
3139 i.atEnd()==false; i++)
3141 RemoteClient *client = i.getNode()->getValue();
3142 client->SetBlockNotSent(blockpos);
3145 else if(command == TOSERVER_INVENTORY_ACTION)
3147 /*// Ignore inventory changes if in creative mode
3148 if(g_settings.getBool("creative_mode") == true)
3150 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3154 // Strip command and create a stream
3155 std::string datastring((char*)&data[2], datasize-2);
3156 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3157 std::istringstream is(datastring, std::ios_base::binary);
3159 InventoryAction *a = InventoryAction::deSerialize(is);
3164 c.current_player = player;
3167 Handle craftresult specially if not in creative mode
3169 bool disable_action = false;
3170 if(a->getType() == IACTION_MOVE
3171 && g_settings.getBool("creative_mode") == false)
3173 IMoveAction *ma = (IMoveAction*)a;
3174 if(ma->to_inv == "current_player" &&
3175 ma->from_inv == "current_player")
3177 InventoryList *rlist = player->inventory.getList("craftresult");
3179 InventoryList *clist = player->inventory.getList("craft");
3181 InventoryList *mlist = player->inventory.getList("main");
3184 Craftresult is no longer preview if something
3187 if(ma->to_list == "craftresult"
3188 && ma->from_list != "craftresult")
3190 // If it currently is a preview, remove
3192 if(player->craftresult_is_preview)
3194 rlist->deleteItem(0);
3196 player->craftresult_is_preview = false;
3199 Crafting takes place if this condition is true.
3201 if(player->craftresult_is_preview &&
3202 ma->from_list == "craftresult")
3204 player->craftresult_is_preview = false;
3205 clist->decrementMaterials(1);
3208 If the craftresult is placed on itself, move it to
3209 main inventory instead of doing the action
3211 if(ma->to_list == "craftresult"
3212 && ma->from_list == "craftresult")
3214 disable_action = true;
3216 InventoryItem *item1 = rlist->changeItem(0, NULL);
3217 mlist->addItem(item1);
3220 // Disallow moving items if not allowed to build
3221 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3225 // if it's a locking chest, only allow the owner or server admins to move items
3226 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3228 Strfnd fn(ma->from_inv);
3229 std::string id0 = fn.next(":");
3230 if(id0 == "nodemeta")
3233 p.X = stoi(fn.next(","));
3234 p.Y = stoi(fn.next(","));
3235 p.Z = stoi(fn.next(","));
3236 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3237 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3238 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3239 if (lcm->getOwner() != player->getName())
3244 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3246 Strfnd fn(ma->to_inv);
3247 std::string id0 = fn.next(":");
3248 if(id0 == "nodemeta")
3251 p.X = stoi(fn.next(","));
3252 p.Y = stoi(fn.next(","));
3253 p.Z = stoi(fn.next(","));
3254 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3255 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3256 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3257 if (lcm->getOwner() != player->getName())
3264 if(disable_action == false)
3266 // Feed action to player inventory
3274 UpdateCrafting(player->peer_id);
3275 SendInventory(player->peer_id);
3280 dstream<<"TOSERVER_INVENTORY_ACTION: "
3281 <<"InventoryAction::deSerialize() returned NULL"
3285 else if(command == TOSERVER_CHAT_MESSAGE)
3293 std::string datastring((char*)&data[2], datasize-2);
3294 std::istringstream is(datastring, std::ios_base::binary);
3297 is.read((char*)buf, 2);
3298 u16 len = readU16(buf);
3300 std::wstring message;
3301 for(u16 i=0; i<len; i++)
3303 is.read((char*)buf, 2);
3304 message += (wchar_t)readU16(buf);
3307 // Get player name of this client
3308 std::wstring name = narrow_to_wide(player->getName());
3310 // Line to send to players
3312 // Whether to send to the player that sent the line
3313 bool send_to_sender = false;
3314 // Whether to send to other players
3315 bool send_to_others = false;
3317 // Local player gets all privileges regardless of
3318 // what's set on their account.
3319 u64 privs = getPlayerPrivs(player);
3322 if(message[0] == L'/')
3324 size_t strip_size = 1;
3325 if (message[1] == L'#') // support old-style commans
3327 message = message.substr(strip_size);
3329 WStrfnd f1(message);
3330 f1.next(L" "); // Skip over /#whatever
3331 std::wstring paramstring = f1.next(L"");
3333 ServerCommandContext *ctx = new ServerCommandContext(
3334 str_split(message, L' '),
3341 std::wstring reply(processServerCommand(ctx));
3342 send_to_sender = ctx->flags & SEND_TO_SENDER;
3343 send_to_others = ctx->flags & SEND_TO_OTHERS;
3345 if (ctx->flags & SEND_NO_PREFIX)
3348 line += L"Server: " + reply;
3355 if(privs & PRIV_SHOUT)
3361 send_to_others = true;
3365 line += L"Server: You are not allowed to shout";
3366 send_to_sender = true;
3372 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3375 Send the message to clients
3377 for(core::map<u16, RemoteClient*>::Iterator
3378 i = m_clients.getIterator();
3379 i.atEnd() == false; i++)
3381 // Get client and check that it is valid
3382 RemoteClient *client = i.getNode()->getValue();
3383 assert(client->peer_id == i.getNode()->getKey());
3384 if(client->serialization_version == SER_FMT_VER_INVALID)
3388 bool sender_selected = (peer_id == client->peer_id);
3389 if(sender_selected == true && send_to_sender == false)
3391 if(sender_selected == false && send_to_others == false)
3394 SendChatMessage(client->peer_id, line);
3398 else if(command == TOSERVER_DAMAGE)
3400 if(g_settings.getBool("enable_damage"))
3402 std::string datastring((char*)&data[2], datasize-2);
3403 std::istringstream is(datastring, std::ios_base::binary);
3404 u8 damage = readU8(is);
3405 if(player->hp > damage)
3407 player->hp -= damage;
3413 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3416 v3f pos = findSpawnPos(m_env.getServerMap());
3417 player->setPosition(pos);
3419 SendMovePlayer(player);
3420 SendPlayerHP(player);
3422 //TODO: Throw items around
3426 SendPlayerHP(player);
3428 else if(command == TOSERVER_PASSWORD)
3431 [0] u16 TOSERVER_PASSWORD
3432 [2] u8[28] old password
3433 [30] u8[28] new password
3436 if(datasize != 2+PASSWORD_SIZE*2)
3438 /*char password[PASSWORD_SIZE];
3439 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3440 password[i] = data[2+i];
3441 password[PASSWORD_SIZE-1] = 0;*/
3443 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3451 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3453 char c = data[2+PASSWORD_SIZE+i];
3459 dstream<<"Server: Client requests a password change from "
3460 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3462 std::string playername = player->getName();
3464 if(m_authmanager.exists(playername) == false)
3466 dstream<<"Server: playername not found in authmanager"<<std::endl;
3467 // Wrong old password supplied!!
3468 SendChatMessage(peer_id, L"playername not found in authmanager");
3472 std::string checkpwd = m_authmanager.getPassword(playername);
3474 if(oldpwd != checkpwd)
3476 dstream<<"Server: invalid old password"<<std::endl;
3477 // Wrong old password supplied!!
3478 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3482 m_authmanager.setPassword(playername, newpwd);
3484 dstream<<"Server: password change successful for "<<playername
3486 SendChatMessage(peer_id, L"Password change successful");
3488 else if (command == TOSERVER_PLAYERITEM)
3493 u16 item = readU16(&data[2]);
3494 player->wieldItem(item);
3495 SendWieldedItem(player);
3499 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3500 "unknown command "<<command<<std::endl;
3504 catch(SendFailedException &e)
3506 derr_server<<"Server::ProcessData(): SendFailedException: "
3512 void Server::onMapEditEvent(MapEditEvent *event)
3514 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3515 if(m_ignore_map_edit_events)
3517 MapEditEvent *e = event->clone();
3518 m_unsent_map_edit_queue.push_back(e);
3521 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3523 if(id == "current_player")
3525 assert(c->current_player);
3526 return &(c->current_player->inventory);
3530 std::string id0 = fn.next(":");
3532 if(id0 == "nodemeta")
3535 p.X = stoi(fn.next(","));
3536 p.Y = stoi(fn.next(","));
3537 p.Z = stoi(fn.next(","));
3538 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3540 return meta->getInventory();
3541 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3542 <<"no metadata found"<<std::endl;
3546 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3549 void Server::inventoryModified(InventoryContext *c, std::string id)
3551 if(id == "current_player")
3553 assert(c->current_player);
3555 UpdateCrafting(c->current_player->peer_id);
3556 SendInventory(c->current_player->peer_id);
3561 std::string id0 = fn.next(":");
3563 if(id0 == "nodemeta")
3566 p.X = stoi(fn.next(","));
3567 p.Y = stoi(fn.next(","));
3568 p.Z = stoi(fn.next(","));
3569 v3s16 blockpos = getNodeBlockPos(p);
3571 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3573 meta->inventoryModified();
3575 for(core::map<u16, RemoteClient*>::Iterator
3576 i = m_clients.getIterator();
3577 i.atEnd()==false; i++)
3579 RemoteClient *client = i.getNode()->getValue();
3580 client->SetBlockNotSent(blockpos);
3586 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3589 core::list<PlayerInfo> Server::getPlayerInfo()
3591 DSTACK(__FUNCTION_NAME);
3592 JMutexAutoLock envlock(m_env_mutex);
3593 JMutexAutoLock conlock(m_con_mutex);
3595 core::list<PlayerInfo> list;
3597 core::list<Player*> players = m_env.getPlayers();
3599 core::list<Player*>::Iterator i;
3600 for(i = players.begin();
3601 i != players.end(); i++)
3605 Player *player = *i;
3608 con::Peer *peer = m_con.GetPeer(player->peer_id);
3609 // Copy info from peer to info struct
3611 info.address = peer->address;
3612 info.avg_rtt = peer->avg_rtt;
3614 catch(con::PeerNotFoundException &e)
3616 // Set dummy peer info
3618 info.address = Address(0,0,0,0,0);
3622 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3623 info.position = player->getPosition();
3625 list.push_back(info);
3632 void Server::peerAdded(con::Peer *peer)
3634 DSTACK(__FUNCTION_NAME);
3635 dout_server<<"Server::peerAdded(): peer->id="
3636 <<peer->id<<std::endl;
3639 c.type = PEER_ADDED;
3640 c.peer_id = peer->id;
3642 m_peer_change_queue.push_back(c);
3645 void Server::deletingPeer(con::Peer *peer, bool timeout)
3647 DSTACK(__FUNCTION_NAME);
3648 dout_server<<"Server::deletingPeer(): peer->id="
3649 <<peer->id<<", timeout="<<timeout<<std::endl;
3652 c.type = PEER_REMOVED;
3653 c.peer_id = peer->id;
3654 c.timeout = timeout;
3655 m_peer_change_queue.push_back(c);
3662 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3664 DSTACK(__FUNCTION_NAME);
3665 std::ostringstream os(std::ios_base::binary);
3667 writeU16(os, TOCLIENT_HP);
3671 std::string s = os.str();
3672 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3674 con.Send(peer_id, 0, data, true);
3677 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3678 const std::wstring &reason)
3680 DSTACK(__FUNCTION_NAME);
3681 std::ostringstream os(std::ios_base::binary);
3683 writeU16(os, TOCLIENT_ACCESS_DENIED);
3684 os<<serializeWideString(reason);
3687 std::string s = os.str();
3688 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3690 con.Send(peer_id, 0, data, true);
3694 Non-static send methods
3697 void Server::SendObjectData(float dtime)
3699 DSTACK(__FUNCTION_NAME);
3701 core::map<v3s16, bool> stepped_blocks;
3703 for(core::map<u16, RemoteClient*>::Iterator
3704 i = m_clients.getIterator();
3705 i.atEnd() == false; i++)
3707 u16 peer_id = i.getNode()->getKey();
3708 RemoteClient *client = i.getNode()->getValue();
3709 assert(client->peer_id == peer_id);
3711 if(client->serialization_version == SER_FMT_VER_INVALID)
3714 client->SendObjectData(this, dtime, stepped_blocks);
3718 void Server::SendPlayerInfos()
3720 DSTACK(__FUNCTION_NAME);
3722 //JMutexAutoLock envlock(m_env_mutex);
3724 // Get connected players
3725 core::list<Player*> players = m_env.getPlayers(true);
3727 u32 player_count = players.getSize();
3728 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3730 SharedBuffer<u8> data(datasize);
3731 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3734 core::list<Player*>::Iterator i;
3735 for(i = players.begin();
3736 i != players.end(); i++)
3738 Player *player = *i;
3740 /*dstream<<"Server sending player info for player with "
3741 "peer_id="<<player->peer_id<<std::endl;*/
3743 writeU16(&data[start], player->peer_id);
3744 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3745 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3746 start += 2+PLAYERNAME_SIZE;
3749 //JMutexAutoLock conlock(m_con_mutex);
3752 m_con.SendToAll(0, data, true);
3755 void Server::SendInventory(u16 peer_id)
3757 DSTACK(__FUNCTION_NAME);
3759 Player* player = m_env.getPlayer(peer_id);
3766 std::ostringstream os;
3767 //os.imbue(std::locale("C"));
3769 player->inventory.serialize(os);
3771 std::string s = os.str();
3773 SharedBuffer<u8> data(s.size()+2);
3774 writeU16(&data[0], TOCLIENT_INVENTORY);
3775 memcpy(&data[2], s.c_str(), s.size());
3778 m_con.Send(peer_id, 0, data, true);
3781 std::string getWieldedItemString(const Player *player)
3783 const InventoryItem *item = player->getWieldItem();
3785 return std::string("");
3786 std::ostringstream os(std::ios_base::binary);
3787 item->serialize(os);
3791 void Server::SendWieldedItem(const Player* player)
3793 DSTACK(__FUNCTION_NAME);
3797 std::ostringstream os(std::ios_base::binary);
3799 writeU16(os, TOCLIENT_PLAYERITEM);
3801 writeU16(os, player->peer_id);
3802 os<<serializeString(getWieldedItemString(player));
3805 std::string s = os.str();
3806 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3808 m_con.SendToAll(0, data, true);
3811 void Server::SendPlayerItems()
3813 DSTACK(__FUNCTION_NAME);
3815 std::ostringstream os(std::ios_base::binary);
3816 core::list<Player *> players = m_env.getPlayers(true);
3818 writeU16(os, TOCLIENT_PLAYERITEM);
3819 writeU16(os, players.size());
3820 core::list<Player *>::Iterator i;
3821 for(i = players.begin(); i != players.end(); ++i)
3824 writeU16(os, p->peer_id);
3825 os<<serializeString(getWieldedItemString(p));
3829 std::string s = os.str();
3830 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3832 m_con.SendToAll(0, data, true);
3835 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3837 DSTACK(__FUNCTION_NAME);
3839 std::ostringstream os(std::ios_base::binary);
3843 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3844 os.write((char*)buf, 2);
3847 writeU16(buf, message.size());
3848 os.write((char*)buf, 2);
3851 for(u32 i=0; i<message.size(); i++)
3855 os.write((char*)buf, 2);
3859 std::string s = os.str();
3860 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3862 m_con.Send(peer_id, 0, data, true);
3865 void Server::BroadcastChatMessage(const std::wstring &message)
3867 for(core::map<u16, RemoteClient*>::Iterator
3868 i = m_clients.getIterator();
3869 i.atEnd() == false; i++)
3871 // Get client and check that it is valid
3872 RemoteClient *client = i.getNode()->getValue();
3873 assert(client->peer_id == i.getNode()->getKey());
3874 if(client->serialization_version == SER_FMT_VER_INVALID)
3877 SendChatMessage(client->peer_id, message);
3881 void Server::SendPlayerHP(Player *player)
3883 SendHP(m_con, player->peer_id, player->hp);
3886 void Server::SendMovePlayer(Player *player)
3888 DSTACK(__FUNCTION_NAME);
3889 std::ostringstream os(std::ios_base::binary);
3891 writeU16(os, TOCLIENT_MOVE_PLAYER);
3892 writeV3F1000(os, player->getPosition());
3893 writeF1000(os, player->getPitch());
3894 writeF1000(os, player->getYaw());
3897 v3f pos = player->getPosition();
3898 f32 pitch = player->getPitch();
3899 f32 yaw = player->getYaw();
3900 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3901 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3908 std::string s = os.str();
3909 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3911 m_con.Send(player->peer_id, 0, data, true);
3914 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3915 core::list<u16> *far_players, float far_d_nodes)
3917 float maxd = far_d_nodes*BS;
3918 v3f p_f = intToFloat(p, BS);
3922 SharedBuffer<u8> reply(replysize);
3923 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3924 writeS16(&reply[2], p.X);
3925 writeS16(&reply[4], p.Y);
3926 writeS16(&reply[6], p.Z);
3928 for(core::map<u16, RemoteClient*>::Iterator
3929 i = m_clients.getIterator();
3930 i.atEnd() == false; i++)
3932 // Get client and check that it is valid
3933 RemoteClient *client = i.getNode()->getValue();
3934 assert(client->peer_id == i.getNode()->getKey());
3935 if(client->serialization_version == SER_FMT_VER_INVALID)
3938 // Don't send if it's the same one
3939 if(client->peer_id == ignore_id)
3945 Player *player = m_env.getPlayer(client->peer_id);
3948 // If player is far away, only set modified blocks not sent
3949 v3f player_pos = player->getPosition();
3950 if(player_pos.getDistanceFrom(p_f) > maxd)
3952 far_players->push_back(client->peer_id);
3959 m_con.Send(client->peer_id, 0, reply, true);
3963 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3964 core::list<u16> *far_players, float far_d_nodes)
3966 float maxd = far_d_nodes*BS;
3967 v3f p_f = intToFloat(p, BS);
3969 for(core::map<u16, RemoteClient*>::Iterator
3970 i = m_clients.getIterator();
3971 i.atEnd() == false; i++)
3973 // Get client and check that it is valid
3974 RemoteClient *client = i.getNode()->getValue();
3975 assert(client->peer_id == i.getNode()->getKey());
3976 if(client->serialization_version == SER_FMT_VER_INVALID)
3979 // Don't send if it's the same one
3980 if(client->peer_id == ignore_id)
3986 Player *player = m_env.getPlayer(client->peer_id);
3989 // If player is far away, only set modified blocks not sent
3990 v3f player_pos = player->getPosition();
3991 if(player_pos.getDistanceFrom(p_f) > maxd)
3993 far_players->push_back(client->peer_id);
4000 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4001 SharedBuffer<u8> reply(replysize);
4002 writeU16(&reply[0], TOCLIENT_ADDNODE);
4003 writeS16(&reply[2], p.X);
4004 writeS16(&reply[4], p.Y);
4005 writeS16(&reply[6], p.Z);
4006 n.serialize(&reply[8], client->serialization_version);
4009 m_con.Send(client->peer_id, 0, reply, true);
4013 void Server::setBlockNotSent(v3s16 p)
4015 for(core::map<u16, RemoteClient*>::Iterator
4016 i = m_clients.getIterator();
4017 i.atEnd()==false; i++)
4019 RemoteClient *client = i.getNode()->getValue();
4020 client->SetBlockNotSent(p);
4024 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4026 DSTACK(__FUNCTION_NAME);
4028 v3s16 p = block->getPos();
4032 bool completely_air = true;
4033 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4034 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4035 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4037 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4039 completely_air = false;
4040 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4045 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4047 dstream<<"[completely air] ";
4052 Create a packet with the block in the right format
4055 std::ostringstream os(std::ios_base::binary);
4056 block->serialize(os, ver);
4057 std::string s = os.str();
4058 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4060 u32 replysize = 8 + blockdata.getSize();
4061 SharedBuffer<u8> reply(replysize);
4062 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4063 writeS16(&reply[2], p.X);
4064 writeS16(&reply[4], p.Y);
4065 writeS16(&reply[6], p.Z);
4066 memcpy(&reply[8], *blockdata, blockdata.getSize());
4068 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4069 <<": \tpacket size: "<<replysize<<std::endl;*/
4074 m_con.Send(peer_id, 1, reply, true);
4077 void Server::SendBlocks(float dtime)
4079 DSTACK(__FUNCTION_NAME);
4081 JMutexAutoLock envlock(m_env_mutex);
4082 JMutexAutoLock conlock(m_con_mutex);
4084 //TimeTaker timer("Server::SendBlocks");
4086 core::array<PrioritySortedBlockTransfer> queue;
4088 s32 total_sending = 0;
4091 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
4093 for(core::map<u16, RemoteClient*>::Iterator
4094 i = m_clients.getIterator();
4095 i.atEnd() == false; i++)
4097 RemoteClient *client = i.getNode()->getValue();
4098 assert(client->peer_id == i.getNode()->getKey());
4100 total_sending += client->SendingCount();
4102 if(client->serialization_version == SER_FMT_VER_INVALID)
4105 client->GetNextBlocks(this, dtime, queue);
4110 // Lowest priority number comes first.
4111 // Lowest is most important.
4114 for(u32 i=0; i<queue.size(); i++)
4116 //TODO: Calculate limit dynamically
4117 if(total_sending >= g_settings.getS32
4118 ("max_simultaneous_block_sends_server_total"))
4121 PrioritySortedBlockTransfer q = queue[i];
4123 MapBlock *block = NULL;
4126 block = m_env.getMap().getBlockNoCreate(q.pos);
4128 catch(InvalidPositionException &e)
4133 RemoteClient *client = getClient(q.peer_id);
4135 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4137 client->SentBlock(q.pos);
4147 void Server::UpdateCrafting(u16 peer_id)
4149 DSTACK(__FUNCTION_NAME);
4151 Player* player = m_env.getPlayer(peer_id);
4155 Calculate crafting stuff
4157 if(g_settings.getBool("creative_mode") == false)
4159 InventoryList *clist = player->inventory.getList("craft");
4160 InventoryList *rlist = player->inventory.getList("craftresult");
4162 if(rlist && rlist->getUsedSlots() == 0)
4163 player->craftresult_is_preview = true;
4165 if(rlist && player->craftresult_is_preview)
4167 rlist->clearItems();
4169 if(clist && rlist && player->craftresult_is_preview)
4171 InventoryItem *items[9];
4172 for(u16 i=0; i<9; i++)
4174 items[i] = clist->getItem(i);
4177 // Get result of crafting grid
4178 InventoryItem *result = craft_get_result(items);
4180 rlist->addItem(result);
4183 } // if creative_mode == false
4186 RemoteClient* Server::getClient(u16 peer_id)
4188 DSTACK(__FUNCTION_NAME);
4189 //JMutexAutoLock lock(m_con_mutex);
4190 core::map<u16, RemoteClient*>::Node *n;
4191 n = m_clients.find(peer_id);
4192 // A client should exist for all peers
4194 return n->getValue();
4197 std::wstring Server::getStatusString()
4199 std::wostringstream os(std::ios_base::binary);
4202 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4204 os<<L", uptime="<<m_uptime.get();
4205 // Information about clients
4207 for(core::map<u16, RemoteClient*>::Iterator
4208 i = m_clients.getIterator();
4209 i.atEnd() == false; i++)
4211 // Get client and check that it is valid
4212 RemoteClient *client = i.getNode()->getValue();
4213 assert(client->peer_id == i.getNode()->getKey());
4214 if(client->serialization_version == SER_FMT_VER_INVALID)
4217 Player *player = m_env.getPlayer(client->peer_id);
4218 // Get name of player
4219 std::wstring name = L"unknown";
4221 name = narrow_to_wide(player->getName());
4222 // Add name to information string
4226 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4227 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4228 if(g_settings.get("motd") != "")
4229 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4233 v3f findSpawnPos(ServerMap &map)
4235 //return v3f(50,50,50)*BS;
4238 s16 groundheight = 0;
4241 nodepos = v2s16(0,0);
4246 // Try to find a good place a few times
4247 for(s32 i=0; i<1000; i++)
4250 // We're going to try to throw the player to this position
4251 nodepos = v2s16(-range + (myrand()%(range*2)),
4252 -range + (myrand()%(range*2)));
4253 v2s16 sectorpos = getNodeSectorPos(nodepos);
4254 // Get sector (NOTE: Don't get because it's slow)
4255 //m_env.getMap().emergeSector(sectorpos);
4256 // Get ground height at point (fallbacks to heightmap function)
4257 groundheight = map.findGroundLevel(nodepos);
4258 // Don't go underwater
4259 if(groundheight < WATER_LEVEL)
4261 //dstream<<"-> Underwater"<<std::endl;
4264 // Don't go to high places
4265 if(groundheight > WATER_LEVEL + 4)
4267 //dstream<<"-> Underwater"<<std::endl;
4271 // Found a good place
4272 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4277 // If no suitable place was not found, go above water at least.
4278 if(groundheight < WATER_LEVEL)
4279 groundheight = WATER_LEVEL;
4281 return intToFloat(v3s16(
4288 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4291 Try to get an existing player
4293 Player *player = m_env.getPlayer(name);
4296 // If player is already connected, cancel
4297 if(player->peer_id != 0)
4299 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4304 player->peer_id = peer_id;
4306 // Reset inventory to creative if in creative mode
4307 if(g_settings.getBool("creative_mode"))
4309 // Warning: double code below
4310 // Backup actual inventory
4311 player->inventory_backup = new Inventory();
4312 *(player->inventory_backup) = player->inventory;
4313 // Set creative inventory
4314 craft_set_creative_inventory(player);
4321 If player with the wanted peer_id already exists, cancel.
4323 if(m_env.getPlayer(peer_id) != NULL)
4325 dstream<<"emergePlayer(): Player with wrong name but same"
4326 " peer_id already exists"<<std::endl;
4334 player = new ServerRemotePlayer();
4335 //player->peer_id = c.peer_id;
4336 //player->peer_id = PEER_ID_INEXISTENT;
4337 player->peer_id = peer_id;
4338 player->updateName(name);
4339 m_authmanager.add(name);
4340 m_authmanager.setPassword(name, password);
4341 m_authmanager.setPrivs(name,
4342 stringToPrivs(g_settings.get("default_privs")));
4348 dstream<<"Server: Finding spawn place for player \""
4349 <<player->getName()<<"\""<<std::endl;
4351 v3f pos = findSpawnPos(m_env.getServerMap());
4353 player->setPosition(pos);
4356 Add player to environment
4359 m_env.addPlayer(player);
4362 Add stuff to inventory
4365 if(g_settings.getBool("creative_mode"))
4367 // Warning: double code above
4368 // Backup actual inventory
4369 player->inventory_backup = new Inventory();
4370 *(player->inventory_backup) = player->inventory;
4371 // Set creative inventory
4372 craft_set_creative_inventory(player);
4374 else if(g_settings.getBool("give_initial_stuff"))
4376 craft_give_initial_stuff(player);
4381 } // create new player
4384 void Server::handlePeerChange(PeerChange &c)
4386 JMutexAutoLock envlock(m_env_mutex);
4387 JMutexAutoLock conlock(m_con_mutex);
4389 if(c.type == PEER_ADDED)
4396 core::map<u16, RemoteClient*>::Node *n;
4397 n = m_clients.find(c.peer_id);
4398 // The client shouldn't already exist
4402 RemoteClient *client = new RemoteClient();
4403 client->peer_id = c.peer_id;
4404 m_clients.insert(client->peer_id, client);
4407 else if(c.type == PEER_REMOVED)
4414 core::map<u16, RemoteClient*>::Node *n;
4415 n = m_clients.find(c.peer_id);
4416 // The client should exist
4420 Mark objects to be not known by the client
4422 RemoteClient *client = n->getValue();
4424 for(core::map<u16, bool>::Iterator
4425 i = client->m_known_objects.getIterator();
4426 i.atEnd()==false; i++)
4429 u16 id = i.getNode()->getKey();
4430 ServerActiveObject* obj = m_env.getActiveObject(id);
4432 if(obj && obj->m_known_by_count > 0)
4433 obj->m_known_by_count--;
4436 // Collect information about leaving in chat
4437 std::wstring message;
4439 Player *player = m_env.getPlayer(c.peer_id);
4442 std::wstring name = narrow_to_wide(player->getName());
4445 message += L" left game";
4447 message += L" (timed out)";
4453 m_env.removePlayer(c.peer_id);
4456 // Set player client disconnected
4458 Player *player = m_env.getPlayer(c.peer_id);
4460 player->peer_id = 0;
4464 delete m_clients[c.peer_id];
4465 m_clients.remove(c.peer_id);
4467 // Send player info to all remaining clients
4470 // Send leave chat message to all remaining clients
4471 BroadcastChatMessage(message);
4480 void Server::handlePeerChanges()
4482 while(m_peer_change_queue.size() > 0)
4484 PeerChange c = m_peer_change_queue.pop_front();
4486 dout_server<<"Server: Handling peer change: "
4487 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4490 handlePeerChange(c);
4494 u64 Server::getPlayerPrivs(Player *player)
4498 std::string playername = player->getName();
4499 // Local player gets all privileges regardless of
4500 // what's set on their account.
4501 if(g_settings.get("name") == playername)
4507 return getPlayerAuthPrivs(playername);
4511 void dedicated_server_loop(Server &server, bool &kill)
4513 DSTACK(__FUNCTION_NAME);
4515 dstream<<DTIME<<std::endl;
4516 dstream<<"========================"<<std::endl;
4517 dstream<<"Running dedicated server"<<std::endl;
4518 dstream<<"========================"<<std::endl;
4521 IntervalLimiter m_profiler_interval;
4525 // This is kind of a hack but can be done like this
4526 // because server.step() is very light
4528 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4533 if(server.getShutdownRequested() || kill)
4535 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4542 float profiler_print_interval =
4543 g_settings.getFloat("profiler_print_interval");
4544 if(profiler_print_interval != 0)
4546 if(m_profiler_interval.step(0.030, profiler_print_interval))
4548 dstream<<"Profiler:"<<std::endl;
4549 g_profiler.print(dstream);
4557 static int counter = 0;
4563 core::list<PlayerInfo> list = server.getPlayerInfo();
4564 core::list<PlayerInfo>::Iterator i;
4565 static u32 sum_old = 0;
4566 u32 sum = PIChecksum(list);
4569 dstream<<DTIME<<"Player info:"<<std::endl;
4570 for(i=list.begin(); i!=list.end(); i++)
4572 i->PrintLine(&dstream);