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
786 std::ostringstream os(std::ios_base::binary);
790 writeU16(buf, TOCLIENT_OBJECTDATA);
791 os.write((char*)buf, 2);
794 Get and write player data
797 // Get connected players
798 core::list<Player*> players = server->m_env.getPlayers(true);
800 // Write player count
801 u16 playercount = players.size();
802 writeU16(buf, playercount);
803 os.write((char*)buf, 2);
805 core::list<Player*>::Iterator i;
806 for(i = players.begin();
807 i != players.end(); i++)
811 v3f pf = player->getPosition();
812 v3f sf = player->getSpeed();
814 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
815 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
816 s32 pitch_i (player->getPitch() * 100);
817 s32 yaw_i (player->getYaw() * 100);
819 writeU16(buf, player->peer_id);
820 os.write((char*)buf, 2);
821 writeV3S32(buf, position_i);
822 os.write((char*)buf, 12);
823 writeV3S32(buf, speed_i);
824 os.write((char*)buf, 12);
825 writeS32(buf, pitch_i);
826 os.write((char*)buf, 4);
827 writeS32(buf, yaw_i);
828 os.write((char*)buf, 4);
832 Get and write object data
838 For making players to be able to build to their nearby
839 environment (building is not possible on blocks that are not
842 - Add blocks to emerge queue if they are not found
844 SUGGESTION: These could be ignored from the backside of the player
847 Player *player = server->m_env.getPlayer(peer_id);
851 v3f playerpos = player->getPosition();
852 v3f playerspeed = player->getSpeed();
854 v3s16 center_nodepos = floatToInt(playerpos, BS);
855 v3s16 center = getNodeBlockPos(center_nodepos);
857 s16 d_max = g_settings.getS16("active_object_range");
859 // Number of blocks whose objects were written to bos
862 std::ostringstream bos(std::ios_base::binary);
864 for(s16 d = 0; d <= d_max; d++)
866 core::list<v3s16> list;
867 getFacePositions(list, d);
869 core::list<v3s16>::Iterator li;
870 for(li=list.begin(); li!=list.end(); li++)
872 v3s16 p = *li + center;
875 Ignore blocks that haven't been sent to the client
878 if(m_blocks_sent.find(p) == NULL)
882 // Try stepping block and add it to a send queue
887 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
890 Step block if not in stepped_blocks and add to stepped_blocks.
892 if(stepped_blocks.find(p) == NULL)
894 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
895 stepped_blocks.insert(p, true);
896 block->setChangedFlag();
899 // Skip block if there are no objects
900 if(block->getObjectCount() == 0)
909 bos.write((char*)buf, 6);
912 //block->serializeObjects(bos, serialization_version); // DEPRECATED
919 Stop collecting objects if data is already too big
921 // Sum of player and object data sizes
922 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
923 // break out if data too big
924 if(sum > MAX_OBJECTDATA_SIZE)
926 goto skip_subsequent;
930 catch(InvalidPositionException &e)
933 // Add it to the emerge queue and trigger the thread.
934 // Fetch the block only if it is on disk.
936 // Grab and increment counter
937 /*SharedPtr<JMutexAutoLock> lock
938 (m_num_blocks_in_emerge_queue.getLock());
939 m_num_blocks_in_emerge_queue.m_value++;*/
941 // Add to queue as an anonymous fetch from disk
942 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
943 server->m_emerge_queue.addBlock(0, p, flags);
944 server->m_emergethread.trigger();
952 writeU16(buf, blockcount);
953 os.write((char*)buf, 2);
955 // Write block objects
962 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
965 std::string s = os.str();
966 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
967 // Send as unreliable
968 server->m_con.Send(peer_id, 0, data, false);
971 void RemoteClient::GotBlock(v3s16 p)
973 if(m_blocks_sending.find(p) != NULL)
974 m_blocks_sending.remove(p);
977 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
978 " m_blocks_sending"<<std::endl;*/
979 m_excess_gotblocks++;
981 m_blocks_sent.insert(p, true);
984 void RemoteClient::SentBlock(v3s16 p)
986 if(m_blocks_sending.find(p) == NULL)
987 m_blocks_sending.insert(p, 0.0);
989 dstream<<"RemoteClient::SentBlock(): Sent block"
990 " already in m_blocks_sending"<<std::endl;
993 void RemoteClient::SetBlockNotSent(v3s16 p)
995 m_nearest_unsent_d = 0;
997 if(m_blocks_sending.find(p) != NULL)
998 m_blocks_sending.remove(p);
999 if(m_blocks_sent.find(p) != NULL)
1000 m_blocks_sent.remove(p);
1003 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1005 m_nearest_unsent_d = 0;
1007 for(core::map<v3s16, MapBlock*>::Iterator
1008 i = blocks.getIterator();
1009 i.atEnd()==false; i++)
1011 v3s16 p = i.getNode()->getKey();
1013 if(m_blocks_sending.find(p) != NULL)
1014 m_blocks_sending.remove(p);
1015 if(m_blocks_sent.find(p) != NULL)
1016 m_blocks_sent.remove(p);
1024 PlayerInfo::PlayerInfo()
1030 void PlayerInfo::PrintLine(std::ostream *s)
1033 (*s)<<"\""<<name<<"\" ("
1034 <<(position.X/10)<<","<<(position.Y/10)
1035 <<","<<(position.Z/10)<<") ";
1037 (*s)<<" avg_rtt="<<avg_rtt;
1041 u32 PIChecksum(core::list<PlayerInfo> &l)
1043 core::list<PlayerInfo>::Iterator i;
1046 for(i=l.begin(); i!=l.end(); i++)
1048 checksum += a * (i->id+1);
1049 checksum ^= 0x435aafcd;
1060 std::string mapsavedir,
1061 std::string configpath
1063 m_env(new ServerMap(mapsavedir), this),
1064 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1065 m_authmanager(mapsavedir+"/auth.txt"),
1067 m_emergethread(this),
1069 m_time_of_day_send_timer(0),
1071 m_mapsavedir(mapsavedir),
1072 m_configpath(configpath),
1073 m_shutdown_requested(false),
1074 m_ignore_map_edit_events(false),
1075 m_ignore_map_edit_events_peer_id(0)
1077 m_liquid_transform_timer = 0.0;
1078 m_print_info_timer = 0.0;
1079 m_objectdata_timer = 0.0;
1080 m_emergethread_trigger_timer = 0.0;
1081 m_savemap_timer = 0.0;
1085 m_step_dtime_mutex.Init();
1088 // Register us to receive map edit events
1089 m_env.getMap().addEventReceiver(this);
1091 // If file exists, load environment metadata
1092 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1094 dstream<<"Server: Loading environment metadata"<<std::endl;
1095 m_env.loadMeta(m_mapsavedir);
1099 dstream<<"Server: Loading players"<<std::endl;
1100 m_env.deSerializePlayers(m_mapsavedir);
1105 dstream<<"Server::~Server()"<<std::endl;
1108 Send shutdown message
1111 JMutexAutoLock conlock(m_con_mutex);
1113 std::wstring line = L"*** Server shutting down";
1116 Send the message to clients
1118 for(core::map<u16, RemoteClient*>::Iterator
1119 i = m_clients.getIterator();
1120 i.atEnd() == false; i++)
1122 // Get client and check that it is valid
1123 RemoteClient *client = i.getNode()->getValue();
1124 assert(client->peer_id == i.getNode()->getKey());
1125 if(client->serialization_version == SER_FMT_VER_INVALID)
1129 SendChatMessage(client->peer_id, line);
1131 catch(con::PeerNotFoundException &e)
1139 dstream<<"Server: Saving players"<<std::endl;
1140 m_env.serializePlayers(m_mapsavedir);
1143 Save environment metadata
1145 dstream<<"Server: Saving environment metadata"<<std::endl;
1146 m_env.saveMeta(m_mapsavedir);
1157 JMutexAutoLock clientslock(m_con_mutex);
1159 for(core::map<u16, RemoteClient*>::Iterator
1160 i = m_clients.getIterator();
1161 i.atEnd() == false; i++)
1164 // NOTE: These are removed by env destructor
1166 u16 peer_id = i.getNode()->getKey();
1167 JMutexAutoLock envlock(m_env_mutex);
1168 m_env.removePlayer(peer_id);
1172 delete i.getNode()->getValue();
1177 void Server::start(unsigned short port)
1179 DSTACK(__FUNCTION_NAME);
1180 // Stop thread if already running
1183 // Initialize connection
1184 m_con.setTimeoutMs(30);
1188 m_thread.setRun(true);
1191 dout_server<<"Server: Started on port "<<port<<std::endl;
1196 DSTACK(__FUNCTION_NAME);
1198 // Stop threads (set run=false first so both start stopping)
1199 m_thread.setRun(false);
1200 m_emergethread.setRun(false);
1202 m_emergethread.stop();
1204 dout_server<<"Server: Threads stopped"<<std::endl;
1207 void Server::step(float dtime)
1209 DSTACK(__FUNCTION_NAME);
1214 JMutexAutoLock lock(m_step_dtime_mutex);
1215 m_step_dtime += dtime;
1219 void Server::AsyncRunStep()
1221 DSTACK(__FUNCTION_NAME);
1225 JMutexAutoLock lock1(m_step_dtime_mutex);
1226 dtime = m_step_dtime;
1230 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1231 "blocks to clients");
1232 // Send blocks to clients
1239 //dstream<<"Server steps "<<dtime<<std::endl;
1240 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1243 JMutexAutoLock lock1(m_step_dtime_mutex);
1244 m_step_dtime -= dtime;
1251 m_uptime.set(m_uptime.get() + dtime);
1255 Update m_time_of_day and overall game time
1258 JMutexAutoLock envlock(m_env_mutex);
1260 m_time_counter += dtime;
1261 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1262 u32 units = (u32)(m_time_counter*speed);
1263 m_time_counter -= (f32)units / speed;
1265 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1267 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1270 Send to clients at constant intervals
1273 m_time_of_day_send_timer -= dtime;
1274 if(m_time_of_day_send_timer < 0.0)
1276 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1278 //JMutexAutoLock envlock(m_env_mutex);
1279 JMutexAutoLock conlock(m_con_mutex);
1281 for(core::map<u16, RemoteClient*>::Iterator
1282 i = m_clients.getIterator();
1283 i.atEnd() == false; i++)
1285 RemoteClient *client = i.getNode()->getValue();
1286 //Player *player = m_env.getPlayer(client->peer_id);
1288 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1289 m_env.getTimeOfDay());
1291 m_con.Send(client->peer_id, 0, data, true);
1297 // Process connection's timeouts
1298 JMutexAutoLock lock2(m_con_mutex);
1299 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1300 m_con.RunTimeouts(dtime);
1304 // This has to be called so that the client list gets synced
1305 // with the peer list of the connection
1306 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1307 handlePeerChanges();
1311 JMutexAutoLock lock(m_env_mutex);
1313 ScopeProfiler sp(&g_profiler, "Server: environment step");
1317 const float map_timer_and_unload_dtime = 5.15;
1318 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1320 JMutexAutoLock lock(m_env_mutex);
1321 // Run Map's timers and unload unused data
1322 ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
1323 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1324 g_settings.getFloat("server_unload_unused_data_timeout"));
1334 m_liquid_transform_timer += dtime;
1335 if(m_liquid_transform_timer >= 1.00)
1337 m_liquid_transform_timer -= 1.00;
1339 JMutexAutoLock lock(m_env_mutex);
1341 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1343 core::map<v3s16, MapBlock*> modified_blocks;
1344 m_env.getMap().transformLiquids(modified_blocks);
1349 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1350 ServerMap &map = ((ServerMap&)m_env.getMap());
1351 map.updateLighting(modified_blocks, lighting_modified_blocks);
1353 // Add blocks modified by lighting to modified_blocks
1354 for(core::map<v3s16, MapBlock*>::Iterator
1355 i = lighting_modified_blocks.getIterator();
1356 i.atEnd() == false; i++)
1358 MapBlock *block = i.getNode()->getValue();
1359 modified_blocks.insert(block->getPos(), block);
1363 Set the modified blocks unsent for all the clients
1366 JMutexAutoLock lock2(m_con_mutex);
1368 for(core::map<u16, RemoteClient*>::Iterator
1369 i = m_clients.getIterator();
1370 i.atEnd() == false; i++)
1372 RemoteClient *client = i.getNode()->getValue();
1374 if(modified_blocks.size() > 0)
1376 // Remove block from sent history
1377 client->SetBlocksNotSent(modified_blocks);
1382 // Periodically print some info
1384 float &counter = m_print_info_timer;
1390 JMutexAutoLock lock2(m_con_mutex);
1392 for(core::map<u16, RemoteClient*>::Iterator
1393 i = m_clients.getIterator();
1394 i.atEnd() == false; i++)
1396 //u16 peer_id = i.getNode()->getKey();
1397 RemoteClient *client = i.getNode()->getValue();
1398 Player *player = m_env.getPlayer(client->peer_id);
1401 std::cout<<player->getName()<<"\t";
1402 client->PrintInfo(std::cout);
1407 //if(g_settings.getBool("enable_experimental"))
1411 Check added and deleted active objects
1414 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1415 JMutexAutoLock envlock(m_env_mutex);
1416 JMutexAutoLock conlock(m_con_mutex);
1418 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1420 // Radius inside which objects are active
1423 for(core::map<u16, RemoteClient*>::Iterator
1424 i = m_clients.getIterator();
1425 i.atEnd() == false; i++)
1427 RemoteClient *client = i.getNode()->getValue();
1428 Player *player = m_env.getPlayer(client->peer_id);
1431 // This can happen if the client timeouts somehow
1432 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1434 <<" has no associated player"<<std::endl;*/
1437 v3s16 pos = floatToInt(player->getPosition(), BS);
1439 core::map<u16, bool> removed_objects;
1440 core::map<u16, bool> added_objects;
1441 m_env.getRemovedActiveObjects(pos, radius,
1442 client->m_known_objects, removed_objects);
1443 m_env.getAddedActiveObjects(pos, radius,
1444 client->m_known_objects, added_objects);
1446 // Ignore if nothing happened
1447 if(removed_objects.size() == 0 && added_objects.size() == 0)
1449 //dstream<<"INFO: active objects: none changed"<<std::endl;
1453 std::string data_buffer;
1457 // Handle removed objects
1458 writeU16((u8*)buf, removed_objects.size());
1459 data_buffer.append(buf, 2);
1460 for(core::map<u16, bool>::Iterator
1461 i = removed_objects.getIterator();
1462 i.atEnd()==false; i++)
1465 u16 id = i.getNode()->getKey();
1466 ServerActiveObject* obj = m_env.getActiveObject(id);
1468 // Add to data buffer for sending
1469 writeU16((u8*)buf, i.getNode()->getKey());
1470 data_buffer.append(buf, 2);
1472 // Remove from known objects
1473 client->m_known_objects.remove(i.getNode()->getKey());
1475 if(obj && obj->m_known_by_count > 0)
1476 obj->m_known_by_count--;
1479 // Handle added objects
1480 writeU16((u8*)buf, added_objects.size());
1481 data_buffer.append(buf, 2);
1482 for(core::map<u16, bool>::Iterator
1483 i = added_objects.getIterator();
1484 i.atEnd()==false; i++)
1487 u16 id = i.getNode()->getKey();
1488 ServerActiveObject* obj = m_env.getActiveObject(id);
1491 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1493 dstream<<"WARNING: "<<__FUNCTION_NAME
1494 <<": NULL object"<<std::endl;
1496 type = obj->getType();
1498 // Add to data buffer for sending
1499 writeU16((u8*)buf, id);
1500 data_buffer.append(buf, 2);
1501 writeU8((u8*)buf, type);
1502 data_buffer.append(buf, 1);
1505 data_buffer.append(serializeLongString(
1506 obj->getClientInitializationData()));
1508 data_buffer.append(serializeLongString(""));
1510 // Add to known objects
1511 client->m_known_objects.insert(i.getNode()->getKey(), false);
1514 obj->m_known_by_count++;
1518 SharedBuffer<u8> reply(2 + data_buffer.size());
1519 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1520 memcpy((char*)&reply[2], data_buffer.c_str(),
1521 data_buffer.size());
1523 m_con.Send(client->peer_id, 0, reply, true);
1525 dstream<<"INFO: Server: Sent object remove/add: "
1526 <<removed_objects.size()<<" removed, "
1527 <<added_objects.size()<<" added, "
1528 <<"packet size is "<<reply.getSize()<<std::endl;
1533 Collect a list of all the objects known by the clients
1534 and report it back to the environment.
1537 core::map<u16, bool> all_known_objects;
1539 for(core::map<u16, RemoteClient*>::Iterator
1540 i = m_clients.getIterator();
1541 i.atEnd() == false; i++)
1543 RemoteClient *client = i.getNode()->getValue();
1544 // Go through all known objects of client
1545 for(core::map<u16, bool>::Iterator
1546 i = client->m_known_objects.getIterator();
1547 i.atEnd()==false; i++)
1549 u16 id = i.getNode()->getKey();
1550 all_known_objects[id] = true;
1554 m_env.setKnownActiveObjects(whatever);
1560 Send object messages
1563 JMutexAutoLock envlock(m_env_mutex);
1564 JMutexAutoLock conlock(m_con_mutex);
1566 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1569 // Value = data sent by object
1570 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1572 // Get active object messages from environment
1575 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1579 core::list<ActiveObjectMessage>* message_list = NULL;
1580 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1581 n = buffered_messages.find(aom.id);
1584 message_list = new core::list<ActiveObjectMessage>;
1585 buffered_messages.insert(aom.id, message_list);
1589 message_list = n->getValue();
1591 message_list->push_back(aom);
1594 // Route data to every client
1595 for(core::map<u16, RemoteClient*>::Iterator
1596 i = m_clients.getIterator();
1597 i.atEnd()==false; i++)
1599 RemoteClient *client = i.getNode()->getValue();
1600 std::string reliable_data;
1601 std::string unreliable_data;
1602 // Go through all objects in message buffer
1603 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1604 j = buffered_messages.getIterator();
1605 j.atEnd()==false; j++)
1607 // If object is not known by client, skip it
1608 u16 id = j.getNode()->getKey();
1609 if(client->m_known_objects.find(id) == NULL)
1611 // Get message list of object
1612 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1613 // Go through every message
1614 for(core::list<ActiveObjectMessage>::Iterator
1615 k = list->begin(); k != list->end(); k++)
1617 // Compose the full new data with header
1618 ActiveObjectMessage aom = *k;
1619 std::string new_data;
1622 writeU16((u8*)&buf[0], aom.id);
1623 new_data.append(buf, 2);
1625 new_data += serializeString(aom.datastring);
1626 // Add data to buffer
1628 reliable_data += new_data;
1630 unreliable_data += new_data;
1634 reliable_data and unreliable_data are now ready.
1637 if(reliable_data.size() > 0)
1639 SharedBuffer<u8> reply(2 + reliable_data.size());
1640 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1641 memcpy((char*)&reply[2], reliable_data.c_str(),
1642 reliable_data.size());
1644 m_con.Send(client->peer_id, 0, reply, true);
1646 if(unreliable_data.size() > 0)
1648 SharedBuffer<u8> reply(2 + unreliable_data.size());
1649 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1650 memcpy((char*)&reply[2], unreliable_data.c_str(),
1651 unreliable_data.size());
1652 // Send as unreliable
1653 m_con.Send(client->peer_id, 0, reply, false);
1656 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1658 dstream<<"INFO: Server: Size of object message data: "
1659 <<"reliable: "<<reliable_data.size()
1660 <<", unreliable: "<<unreliable_data.size()
1665 // Clear buffered_messages
1666 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1667 i = buffered_messages.getIterator();
1668 i.atEnd()==false; i++)
1670 delete i.getNode()->getValue();
1674 } // enable_experimental
1677 Send queued-for-sending map edit events.
1680 // Don't send too many at a time
1683 // Single change sending is disabled if queue size is not small
1684 bool disable_single_change_sending = false;
1685 if(m_unsent_map_edit_queue.size() >= 4)
1686 disable_single_change_sending = true;
1688 bool got_any_events = false;
1690 // We'll log the amount of each
1693 while(m_unsent_map_edit_queue.size() != 0)
1695 got_any_events = true;
1697 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1699 // Players far away from the change are stored here.
1700 // Instead of sending the changes, MapBlocks are set not sent
1702 core::list<u16> far_players;
1704 if(event->type == MEET_ADDNODE)
1706 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1707 prof.add("MEET_ADDNODE", 1);
1708 if(disable_single_change_sending)
1709 sendAddNode(event->p, event->n, event->already_known_by_peer,
1712 sendAddNode(event->p, event->n, event->already_known_by_peer,
1715 else if(event->type == MEET_REMOVENODE)
1717 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1718 prof.add("MEET_REMOVENODE", 1);
1719 if(disable_single_change_sending)
1720 sendRemoveNode(event->p, event->already_known_by_peer,
1723 sendRemoveNode(event->p, event->already_known_by_peer,
1726 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1728 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1729 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1730 setBlockNotSent(event->p);
1732 else if(event->type == MEET_OTHER)
1734 prof.add("MEET_OTHER", 1);
1735 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1740 prof.add("unknown", 1);
1741 dstream<<"WARNING: Server: Unknown MapEditEvent "
1742 <<((u32)event->type)<<std::endl;
1746 Set blocks not sent to far players
1748 if(far_players.size() > 0)
1750 // Convert list format to that wanted by SetBlocksNotSent
1751 core::map<v3s16, MapBlock*> modified_blocks2;
1752 for(core::map<v3s16, bool>::Iterator
1753 i = event->modified_blocks.getIterator();
1754 i.atEnd()==false; i++)
1756 v3s16 p = i.getNode()->getKey();
1757 modified_blocks2.insert(p,
1758 m_env.getMap().getBlockNoCreateNoEx(p));
1760 // Set blocks not sent
1761 for(core::list<u16>::Iterator
1762 i = far_players.begin();
1763 i != far_players.end(); i++)
1766 RemoteClient *client = getClient(peer_id);
1769 client->SetBlocksNotSent(modified_blocks2);
1775 /*// Don't send too many at a time
1777 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1783 dstream<<"Server: MapEditEvents:"<<std::endl;
1784 prof.print(dstream);
1790 Send object positions
1791 TODO: Get rid of MapBlockObjects
1794 float &counter = m_objectdata_timer;
1796 if(counter >= g_settings.getFloat("objectdata_interval"))
1798 JMutexAutoLock lock1(m_env_mutex);
1799 JMutexAutoLock lock2(m_con_mutex);
1801 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1803 SendObjectData(counter);
1810 Trigger emergethread (it somehow gets to a non-triggered but
1811 bysy state sometimes)
1814 float &counter = m_emergethread_trigger_timer;
1820 m_emergethread.trigger();
1824 // Save map, players and auth stuff
1826 float &counter = m_savemap_timer;
1828 if(counter >= g_settings.getFloat("server_map_save_interval"))
1832 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1835 if(m_authmanager.isModified())
1836 m_authmanager.save();
1839 JMutexAutoLock lock(m_env_mutex);
1841 /*// Unload unused data (delete from memory)
1842 m_env.getMap().unloadUnusedData(
1843 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1845 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1846 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1849 // Save only changed parts
1850 m_env.getMap().save(true);
1852 /*if(deleted_count > 0)
1854 dout_server<<"Server: Unloaded "<<deleted_count
1855 <<" blocks from memory"<<std::endl;
1859 m_env.serializePlayers(m_mapsavedir);
1861 // Save environment metadata
1862 m_env.saveMeta(m_mapsavedir);
1867 void Server::Receive()
1869 DSTACK(__FUNCTION_NAME);
1870 u32 data_maxsize = 10000;
1871 Buffer<u8> data(data_maxsize);
1876 JMutexAutoLock conlock(m_con_mutex);
1877 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1880 // This has to be called so that the client list gets synced
1881 // with the peer list of the connection
1882 handlePeerChanges();
1884 ProcessData(*data, datasize, peer_id);
1886 catch(con::InvalidIncomingDataException &e)
1888 derr_server<<"Server::Receive(): "
1889 "InvalidIncomingDataException: what()="
1890 <<e.what()<<std::endl;
1892 catch(con::PeerNotFoundException &e)
1894 //NOTE: This is not needed anymore
1896 // The peer has been disconnected.
1897 // Find the associated player and remove it.
1899 /*JMutexAutoLock envlock(m_env_mutex);
1901 dout_server<<"ServerThread: peer_id="<<peer_id
1902 <<" has apparently closed connection. "
1903 <<"Removing player."<<std::endl;
1905 m_env.removePlayer(peer_id);*/
1909 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1911 DSTACK(__FUNCTION_NAME);
1912 // Environment is locked first.
1913 JMutexAutoLock envlock(m_env_mutex);
1914 JMutexAutoLock conlock(m_con_mutex);
1918 peer = m_con.GetPeer(peer_id);
1920 catch(con::PeerNotFoundException &e)
1922 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1923 <<peer_id<<" not found"<<std::endl;
1927 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1935 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1937 if(command == TOSERVER_INIT)
1939 // [0] u16 TOSERVER_INIT
1940 // [2] u8 SER_FMT_VER_HIGHEST
1941 // [3] u8[20] player_name
1942 // [23] u8[28] password <--- can be sent without this, from old versions
1944 if(datasize < 2+1+PLAYERNAME_SIZE)
1947 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1948 <<peer->id<<std::endl;
1950 // First byte after command is maximum supported
1951 // serialization version
1952 u8 client_max = data[2];
1953 u8 our_max = SER_FMT_VER_HIGHEST;
1954 // Use the highest version supported by both
1955 u8 deployed = core::min_(client_max, our_max);
1956 // If it's lower than the lowest supported, give up.
1957 if(deployed < SER_FMT_VER_LOWEST)
1958 deployed = SER_FMT_VER_INVALID;
1960 //peer->serialization_version = deployed;
1961 getClient(peer->id)->pending_serialization_version = deployed;
1963 if(deployed == SER_FMT_VER_INVALID)
1965 derr_server<<DTIME<<"Server: Cannot negotiate "
1966 "serialization version with peer "
1967 <<peer_id<<std::endl;
1968 SendAccessDenied(m_con, peer_id,
1969 L"Your client is too old (map format)");
1974 Read and check network protocol version
1977 u16 net_proto_version = 0;
1978 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1980 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1983 getClient(peer->id)->net_proto_version = net_proto_version;
1985 if(net_proto_version == 0)
1987 SendAccessDenied(m_con, peer_id,
1988 L"Your client is too old. Please upgrade.");
1997 char playername[PLAYERNAME_SIZE];
1998 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2000 playername[i] = data[3+i];
2002 playername[PLAYERNAME_SIZE-1] = 0;
2004 if(playername[0]=='\0')
2006 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2007 SendAccessDenied(m_con, peer_id,
2012 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2014 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2015 SendAccessDenied(m_con, peer_id,
2016 L"Name contains unallowed characters");
2021 char password[PASSWORD_SIZE];
2022 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2024 // old version - assume blank password
2029 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2031 password[i] = data[23+i];
2033 password[PASSWORD_SIZE-1] = 0;
2036 std::string checkpwd;
2037 if(m_authmanager.exists(playername))
2039 checkpwd = m_authmanager.getPassword(playername);
2043 checkpwd = g_settings.get("default_password");
2046 /*dstream<<"Server: Client gave password '"<<password
2047 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2049 if(password != checkpwd && m_authmanager.exists(playername))
2051 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2052 <<": supplied invalid password for "
2053 <<playername<<std::endl;
2054 SendAccessDenied(m_con, peer_id, L"Invalid password");
2058 // Add player to auth manager
2059 if(m_authmanager.exists(playername) == false)
2061 derr_server<<DTIME<<"Server: adding player "<<playername
2062 <<" to auth manager"<<std::endl;
2063 m_authmanager.add(playername);
2064 m_authmanager.setPassword(playername, checkpwd);
2065 m_authmanager.setPrivs(playername,
2066 stringToPrivs(g_settings.get("default_privs")));
2067 m_authmanager.save();
2071 Player *player = emergePlayer(playername, password, peer_id);
2075 // DEBUG: Test serialization
2076 std::ostringstream test_os;
2077 player->serialize(test_os);
2078 dstream<<"Player serialization test: \""<<test_os.str()
2080 std::istringstream test_is(test_os.str());
2081 player->deSerialize(test_is);
2084 // If failed, cancel
2087 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2088 <<": failed to emerge player"<<std::endl;
2093 // If a client is already connected to the player, cancel
2094 if(player->peer_id != 0)
2096 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2097 <<" tried to connect to "
2098 "an already connected player (peer_id="
2099 <<player->peer_id<<")"<<std::endl;
2102 // Set client of player
2103 player->peer_id = peer_id;
2106 // Check if player doesn't exist
2108 throw con::InvalidIncomingDataException
2109 ("Server::ProcessData(): INIT: Player doesn't exist");
2111 /*// update name if it was supplied
2112 if(datasize >= 20+3)
2115 player->updateName((const char*)&data[3]);
2119 Answer with a TOCLIENT_INIT
2122 SharedBuffer<u8> reply(2+1+6+8);
2123 writeU16(&reply[0], TOCLIENT_INIT);
2124 writeU8(&reply[2], deployed);
2125 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2126 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2129 m_con.Send(peer_id, 0, reply, true);
2133 Send complete position information
2135 SendMovePlayer(player);
2140 if(command == TOSERVER_INIT2)
2142 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2143 <<peer->id<<std::endl;
2146 getClient(peer->id)->serialization_version
2147 = getClient(peer->id)->pending_serialization_version;
2150 Send some initialization data
2153 // Send player info to all players
2156 // Send inventory to player
2157 UpdateCrafting(peer->id);
2158 SendInventory(peer->id);
2160 // Send player items to all players
2165 Player *player = m_env.getPlayer(peer_id);
2166 SendPlayerHP(player);
2171 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2172 m_env.getTimeOfDay());
2173 m_con.Send(peer->id, 0, data, true);
2176 // Send information about server to player in chat
2177 SendChatMessage(peer_id, getStatusString());
2179 // Send information about joining in chat
2181 std::wstring name = L"unknown";
2182 Player *player = m_env.getPlayer(peer_id);
2184 name = narrow_to_wide(player->getName());
2186 std::wstring message;
2189 message += L" joined game";
2190 BroadcastChatMessage(message);
2193 // Warnings about protocol version can be issued here
2194 /*if(getClient(peer->id)->net_proto_version == 0)
2196 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2202 if(peer_ser_ver == SER_FMT_VER_INVALID)
2204 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2205 " serialization format invalid or not initialized."
2206 " Skipping incoming command="<<command<<std::endl;
2210 Player *player = m_env.getPlayer(peer_id);
2213 derr_server<<"Server::ProcessData(): Cancelling: "
2214 "No player for peer_id="<<peer_id
2218 if(command == TOSERVER_PLAYERPOS)
2220 if(datasize < 2+12+12+4+4)
2224 v3s32 ps = readV3S32(&data[start+2]);
2225 v3s32 ss = readV3S32(&data[start+2+12]);
2226 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2227 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2228 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2229 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2230 pitch = wrapDegrees(pitch);
2231 yaw = wrapDegrees(yaw);
2232 player->setPosition(position);
2233 player->setSpeed(speed);
2234 player->setPitch(pitch);
2235 player->setYaw(yaw);
2237 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2238 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2239 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2241 else if(command == TOSERVER_GOTBLOCKS)
2254 u16 count = data[2];
2255 for(u16 i=0; i<count; i++)
2257 if((s16)datasize < 2+1+(i+1)*6)
2258 throw con::InvalidIncomingDataException
2259 ("GOTBLOCKS length is too short");
2260 v3s16 p = readV3S16(&data[2+1+i*6]);
2261 /*dstream<<"Server: GOTBLOCKS ("
2262 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2263 RemoteClient *client = getClient(peer_id);
2264 client->GotBlock(p);
2267 else if(command == TOSERVER_DELETEDBLOCKS)
2280 u16 count = data[2];
2281 for(u16 i=0; i<count; i++)
2283 if((s16)datasize < 2+1+(i+1)*6)
2284 throw con::InvalidIncomingDataException
2285 ("DELETEDBLOCKS length is too short");
2286 v3s16 p = readV3S16(&data[2+1+i*6]);
2287 /*dstream<<"Server: DELETEDBLOCKS ("
2288 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2289 RemoteClient *client = getClient(peer_id);
2290 client->SetBlockNotSent(p);
2293 else if(command == TOSERVER_CLICK_OBJECT)
2298 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2303 [2] u8 button (0=left, 1=right)
2308 u8 button = readU8(&data[2]);
2310 p.X = readS16(&data[3]);
2311 p.Y = readS16(&data[5]);
2312 p.Z = readS16(&data[7]);
2313 s16 id = readS16(&data[9]);
2314 //u16 item_i = readU16(&data[11]);
2316 MapBlock *block = NULL;
2319 block = m_env.getMap().getBlockNoCreate(p);
2321 catch(InvalidPositionException &e)
2323 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2327 MapBlockObject *obj = block->getObject(id);
2331 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2335 //TODO: Check that object is reasonably close
2340 InventoryList *ilist = player->inventory.getList("main");
2341 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2344 // Skip if inventory has no free space
2345 if(ilist->getUsedSlots() == ilist->getSize())
2347 dout_server<<"Player inventory has no free space"<<std::endl;
2352 Create the inventory item
2354 InventoryItem *item = NULL;
2355 // If it is an item-object, take the item from it
2356 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2358 item = ((ItemObject*)obj)->createInventoryItem();
2360 // Else create an item of the object
2363 item = new MapBlockObjectItem
2364 (obj->getInventoryString());
2367 // Add to inventory and send inventory
2368 ilist->addItem(item);
2369 UpdateCrafting(player->peer_id);
2370 SendInventory(player->peer_id);
2373 // Remove from block
2374 block->removeObject(id);
2377 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2382 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2388 [2] u8 button (0=left, 1=right)
2392 u8 button = readU8(&data[2]);
2393 u16 id = readS16(&data[3]);
2394 u16 item_i = readU16(&data[11]);
2396 ServerActiveObject *obj = m_env.getActiveObject(id);
2400 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2405 // Skip if object has been removed
2409 //TODO: Check that object is reasonably close
2411 // Left click, pick object up (usually)
2415 Try creating inventory item
2417 InventoryItem *item = obj->createPickedUpItem();
2421 InventoryList *ilist = player->inventory.getList("main");
2424 if(g_settings.getBool("creative_mode") == false)
2426 // Skip if inventory has no free space
2427 if(ilist->getUsedSlots() == ilist->getSize())
2429 dout_server<<"Player inventory has no free space"<<std::endl;
2433 // Add to inventory and send inventory
2434 ilist->addItem(item);
2435 UpdateCrafting(player->peer_id);
2436 SendInventory(player->peer_id);
2439 // Remove object from environment
2440 obj->m_removed = true;
2446 Item cannot be picked up. Punch it instead.
2449 ToolItem *titem = NULL;
2450 std::string toolname = "";
2452 InventoryList *mlist = player->inventory.getList("main");
2455 InventoryItem *item = mlist->getItem(item_i);
2456 if(item && (std::string)item->getName() == "ToolItem")
2458 titem = (ToolItem*)item;
2459 toolname = titem->getToolName();
2463 v3f playerpos = player->getPosition();
2464 v3f objpos = obj->getBasePosition();
2465 v3f dir = (objpos - playerpos).normalize();
2467 u16 wear = obj->punch(toolname, dir);
2471 bool weared_out = titem->addWear(wear);
2473 mlist->deleteItem(item_i);
2474 SendInventory(player->peer_id);
2478 // Right click, do something with object
2481 // Track hp changes super-crappily
2482 u16 oldhp = player->hp;
2485 obj->rightClick(player);
2488 if(player->hp != oldhp)
2490 SendPlayerHP(player);
2494 else if(command == TOSERVER_GROUND_ACTION)
2502 [3] v3s16 nodepos_undersurface
2503 [9] v3s16 nodepos_abovesurface
2508 2: stop digging (all parameters ignored)
2509 3: digging completed
2511 u8 action = readU8(&data[2]);
2513 p_under.X = readS16(&data[3]);
2514 p_under.Y = readS16(&data[5]);
2515 p_under.Z = readS16(&data[7]);
2517 p_over.X = readS16(&data[9]);
2518 p_over.Y = readS16(&data[11]);
2519 p_over.Z = readS16(&data[13]);
2520 u16 item_i = readU16(&data[15]);
2522 //TODO: Check that target is reasonably close
2530 NOTE: This can be used in the future to check if
2531 somebody is cheating, by checking the timing.
2538 else if(action == 2)
2541 RemoteClient *client = getClient(peer->id);
2542 JMutexAutoLock digmutex(client->m_dig_mutex);
2543 client->m_dig_tool_item = -1;
2548 3: Digging completed
2550 else if(action == 3)
2552 // Mandatory parameter; actually used for nothing
2553 core::map<v3s16, MapBlock*> modified_blocks;
2555 content_t material = CONTENT_IGNORE;
2556 u8 mineral = MINERAL_NONE;
2558 bool cannot_remove_node = false;
2562 MapNode n = m_env.getMap().getNode(p_under);
2564 mineral = n.getMineral();
2565 // Get material at position
2566 material = n.getContent();
2567 // If not yet cancelled
2568 if(cannot_remove_node == false)
2570 // If it's not diggable, do nothing
2571 if(content_diggable(material) == false)
2573 derr_server<<"Server: Not finishing digging: "
2574 <<"Node not diggable"
2576 cannot_remove_node = true;
2579 // If not yet cancelled
2580 if(cannot_remove_node == false)
2582 // Get node metadata
2583 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2584 if(meta && meta->nodeRemovalDisabled() == true)
2586 derr_server<<"Server: Not finishing digging: "
2587 <<"Node metadata disables removal"
2589 cannot_remove_node = true;
2593 catch(InvalidPositionException &e)
2595 derr_server<<"Server: Not finishing digging: Node not found."
2596 <<" Adding block to emerge queue."
2598 m_emerge_queue.addBlock(peer_id,
2599 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2600 cannot_remove_node = true;
2603 // Make sure the player is allowed to do it
2604 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2606 dstream<<"Player "<<player->getName()<<" cannot remove node"
2607 <<" because privileges are "<<getPlayerPrivs(player)
2609 cannot_remove_node = true;
2613 If node can't be removed, set block to be re-sent to
2616 if(cannot_remove_node)
2618 derr_server<<"Server: Not finishing digging."<<std::endl;
2620 // Client probably has wrong data.
2621 // Set block not sent, so that client will get
2623 dstream<<"Client "<<peer_id<<" tried to dig "
2624 <<"node; but node cannot be removed."
2625 <<" setting MapBlock not sent."<<std::endl;
2626 RemoteClient *client = getClient(peer_id);
2627 v3s16 blockpos = getNodeBlockPos(p_under);
2628 client->SetBlockNotSent(blockpos);
2634 Send the removal to all close-by players.
2635 - If other player is close, send REMOVENODE
2636 - Otherwise set blocks not sent
2638 core::list<u16> far_players;
2639 sendRemoveNode(p_under, peer_id, &far_players, 30);
2642 Update and send inventory
2645 if(g_settings.getBool("creative_mode") == false)
2650 InventoryList *mlist = player->inventory.getList("main");
2653 InventoryItem *item = mlist->getItem(item_i);
2654 if(item && (std::string)item->getName() == "ToolItem")
2656 ToolItem *titem = (ToolItem*)item;
2657 std::string toolname = titem->getToolName();
2659 // Get digging properties for material and tool
2660 DiggingProperties prop =
2661 getDiggingProperties(material, toolname);
2663 if(prop.diggable == false)
2665 derr_server<<"Server: WARNING: Player digged"
2666 <<" with impossible material + tool"
2667 <<" combination"<<std::endl;
2670 bool weared_out = titem->addWear(prop.wear);
2674 mlist->deleteItem(item_i);
2680 Add dug item to inventory
2683 InventoryItem *item = NULL;
2685 if(mineral != MINERAL_NONE)
2686 item = getDiggedMineralItem(mineral);
2691 std::string &dug_s = content_features(material).dug_item;
2694 std::istringstream is(dug_s, std::ios::binary);
2695 item = InventoryItem::deSerialize(is);
2701 // Add a item to inventory
2702 player->inventory.addItem("main", item);
2705 UpdateCrafting(player->peer_id);
2706 SendInventory(player->peer_id);
2712 (this takes some time so it is done after the quick stuff)
2715 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2717 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2720 Set blocks not sent to far players
2722 for(core::list<u16>::Iterator
2723 i = far_players.begin();
2724 i != far_players.end(); i++)
2727 RemoteClient *client = getClient(peer_id);
2730 client->SetBlocksNotSent(modified_blocks);
2737 else if(action == 1)
2740 InventoryList *ilist = player->inventory.getList("main");
2745 InventoryItem *item = ilist->getItem(item_i);
2747 // If there is no item, it is not possible to add it anywhere
2752 Handle material items
2754 if(std::string("MaterialItem") == item->getName())
2757 // Don't add a node if this is not a free space
2758 MapNode n2 = m_env.getMap().getNode(p_over);
2759 bool no_enough_privs =
2760 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2762 dstream<<"Player "<<player->getName()<<" cannot add node"
2763 <<" because privileges are "<<getPlayerPrivs(player)
2766 if(content_features(n2).buildable_to == false
2769 // Client probably has wrong data.
2770 // Set block not sent, so that client will get
2772 dstream<<"Client "<<peer_id<<" tried to place"
2773 <<" node in invalid position; setting"
2774 <<" MapBlock not sent."<<std::endl;
2775 RemoteClient *client = getClient(peer_id);
2776 v3s16 blockpos = getNodeBlockPos(p_over);
2777 client->SetBlockNotSent(blockpos);
2781 catch(InvalidPositionException &e)
2783 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2784 <<" Adding block to emerge queue."
2786 m_emerge_queue.addBlock(peer_id,
2787 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2791 // Reset build time counter
2792 getClient(peer->id)->m_time_from_building = 0.0;
2795 MaterialItem *mitem = (MaterialItem*)item;
2797 n.setContent(mitem->getMaterial());
2799 // Calculate direction for wall mounted stuff
2800 if(content_features(n).wall_mounted)
2801 n.param2 = packDir(p_under - p_over);
2803 // Calculate the direction for furnaces and chests and stuff
2804 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2806 v3f playerpos = player->getPosition();
2807 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2808 blockpos = blockpos.normalize();
2810 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2824 Send to all close-by players
2826 core::list<u16> far_players;
2827 sendAddNode(p_over, n, 0, &far_players, 30);
2832 InventoryList *ilist = player->inventory.getList("main");
2833 if(g_settings.getBool("creative_mode") == false && ilist)
2835 // Remove from inventory and send inventory
2836 if(mitem->getCount() == 1)
2837 ilist->deleteItem(item_i);
2841 UpdateCrafting(peer_id);
2842 SendInventory(peer_id);
2848 This takes some time so it is done after the quick stuff
2850 core::map<v3s16, MapBlock*> modified_blocks;
2852 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2854 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2857 Set blocks not sent to far players
2859 for(core::list<u16>::Iterator
2860 i = far_players.begin();
2861 i != far_players.end(); i++)
2864 RemoteClient *client = getClient(peer_id);
2867 client->SetBlocksNotSent(modified_blocks);
2871 Calculate special events
2874 /*if(n.d == CONTENT_MESE)
2877 for(s16 z=-1; z<=1; z++)
2878 for(s16 y=-1; y<=1; y++)
2879 for(s16 x=-1; x<=1; x++)
2886 Place other item (not a block)
2890 v3s16 blockpos = getNodeBlockPos(p_over);
2893 Check that the block is loaded so that the item
2894 can properly be added to the static list too
2896 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2899 derr_server<<"Error while placing object: "
2900 "block not found"<<std::endl;
2904 dout_server<<"Placing a miscellaneous item on map"
2907 // Calculate a position for it
2908 v3f pos = intToFloat(p_over, BS);
2910 pos.Y -= BS*0.25; // let it drop a bit
2912 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2913 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2918 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2922 derr_server<<"WARNING: item resulted in NULL object, "
2923 <<"not placing onto map"
2928 // Add the object to the environment
2929 m_env.addActiveObject(obj);
2931 dout_server<<"Placed object"<<std::endl;
2933 if(g_settings.getBool("creative_mode") == false)
2935 // Delete the right amount of items from the slot
2936 u16 dropcount = item->getDropCount();
2938 // Delete item if all gone
2939 if(item->getCount() <= dropcount)
2941 if(item->getCount() < dropcount)
2942 dstream<<"WARNING: Server: dropped more items"
2943 <<" than the slot contains"<<std::endl;
2945 InventoryList *ilist = player->inventory.getList("main");
2947 // Remove from inventory and send inventory
2948 ilist->deleteItem(item_i);
2950 // Else decrement it
2952 item->remove(dropcount);
2955 UpdateCrafting(peer_id);
2956 SendInventory(peer_id);
2964 Catch invalid actions
2968 derr_server<<"WARNING: Server: Invalid action "
2969 <<action<<std::endl;
2973 else if(command == TOSERVER_RELEASE)
2982 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2985 else if(command == TOSERVER_SIGNTEXT)
2987 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2996 std::string datastring((char*)&data[2], datasize-2);
2997 std::istringstream is(datastring, std::ios_base::binary);
3000 is.read((char*)buf, 6);
3001 v3s16 blockpos = readV3S16(buf);
3002 is.read((char*)buf, 2);
3003 s16 id = readS16(buf);
3004 is.read((char*)buf, 2);
3005 u16 textlen = readU16(buf);
3007 for(u16 i=0; i<textlen; i++)
3009 is.read((char*)buf, 1);
3010 text += (char)buf[0];
3013 MapBlock *block = NULL;
3016 block = m_env.getMap().getBlockNoCreate(blockpos);
3018 catch(InvalidPositionException &e)
3020 derr_server<<"Error while setting sign text: "
3021 "block not found"<<std::endl;
3025 MapBlockObject *obj = block->getObject(id);
3028 derr_server<<"Error while setting sign text: "
3029 "object not found"<<std::endl;
3033 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3035 derr_server<<"Error while setting sign text: "
3036 "object is not a sign"<<std::endl;
3040 ((SignObject*)obj)->setText(text);
3042 obj->getBlock()->setChangedFlag();
3044 else if(command == TOSERVER_SIGNNODETEXT)
3046 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3054 std::string datastring((char*)&data[2], datasize-2);
3055 std::istringstream is(datastring, std::ios_base::binary);
3058 is.read((char*)buf, 6);
3059 v3s16 p = readV3S16(buf);
3060 is.read((char*)buf, 2);
3061 u16 textlen = readU16(buf);
3063 for(u16 i=0; i<textlen; i++)
3065 is.read((char*)buf, 1);
3066 text += (char)buf[0];
3069 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3072 if(meta->typeId() != CONTENT_SIGN_WALL)
3074 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3075 signmeta->setText(text);
3077 v3s16 blockpos = getNodeBlockPos(p);
3078 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3081 block->setChangedFlag();
3084 for(core::map<u16, RemoteClient*>::Iterator
3085 i = m_clients.getIterator();
3086 i.atEnd()==false; i++)
3088 RemoteClient *client = i.getNode()->getValue();
3089 client->SetBlockNotSent(blockpos);
3092 else if(command == TOSERVER_INVENTORY_ACTION)
3094 /*// Ignore inventory changes if in creative mode
3095 if(g_settings.getBool("creative_mode") == true)
3097 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3101 // Strip command and create a stream
3102 std::string datastring((char*)&data[2], datasize-2);
3103 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3104 std::istringstream is(datastring, std::ios_base::binary);
3106 InventoryAction *a = InventoryAction::deSerialize(is);
3111 c.current_player = player;
3114 Handle craftresult specially if not in creative mode
3116 bool disable_action = false;
3117 if(a->getType() == IACTION_MOVE
3118 && g_settings.getBool("creative_mode") == false)
3120 IMoveAction *ma = (IMoveAction*)a;
3121 if(ma->to_inv == "current_player" &&
3122 ma->from_inv == "current_player")
3124 InventoryList *rlist = player->inventory.getList("craftresult");
3126 InventoryList *clist = player->inventory.getList("craft");
3128 InventoryList *mlist = player->inventory.getList("main");
3131 Craftresult is no longer preview if something
3134 if(ma->to_list == "craftresult"
3135 && ma->from_list != "craftresult")
3137 // If it currently is a preview, remove
3139 if(player->craftresult_is_preview)
3141 rlist->deleteItem(0);
3143 player->craftresult_is_preview = false;
3146 Crafting takes place if this condition is true.
3148 if(player->craftresult_is_preview &&
3149 ma->from_list == "craftresult")
3151 player->craftresult_is_preview = false;
3152 clist->decrementMaterials(1);
3155 If the craftresult is placed on itself, move it to
3156 main inventory instead of doing the action
3158 if(ma->to_list == "craftresult"
3159 && ma->from_list == "craftresult")
3161 disable_action = true;
3163 InventoryItem *item1 = rlist->changeItem(0, NULL);
3164 mlist->addItem(item1);
3169 if(disable_action == false)
3171 // Feed action to player inventory
3179 UpdateCrafting(player->peer_id);
3180 SendInventory(player->peer_id);
3185 dstream<<"TOSERVER_INVENTORY_ACTION: "
3186 <<"InventoryAction::deSerialize() returned NULL"
3190 else if(command == TOSERVER_CHAT_MESSAGE)
3198 std::string datastring((char*)&data[2], datasize-2);
3199 std::istringstream is(datastring, std::ios_base::binary);
3202 is.read((char*)buf, 2);
3203 u16 len = readU16(buf);
3205 std::wstring message;
3206 for(u16 i=0; i<len; i++)
3208 is.read((char*)buf, 2);
3209 message += (wchar_t)readU16(buf);
3212 // Get player name of this client
3213 std::wstring name = narrow_to_wide(player->getName());
3215 // Line to send to players
3217 // Whether to send to the player that sent the line
3218 bool send_to_sender = false;
3219 // Whether to send to other players
3220 bool send_to_others = false;
3222 // Local player gets all privileges regardless of
3223 // what's set on their account.
3224 u64 privs = getPlayerPrivs(player);
3227 std::wstring commandprefix = L"/#";
3228 if(message.substr(0, commandprefix.size()) == commandprefix)
3230 line += L"Server: ";
3232 message = message.substr(commandprefix.size());
3234 WStrfnd f1(message);
3235 f1.next(L" "); // Skip over /#whatever
3236 std::wstring paramstring = f1.next(L"");
3238 ServerCommandContext *ctx = new ServerCommandContext(
3239 str_split(message, L' '),
3246 line += processServerCommand(ctx);
3247 send_to_sender = ctx->flags & 1;
3248 send_to_others = ctx->flags & 2;
3254 if(privs & PRIV_SHOUT)
3260 send_to_others = true;
3264 line += L"Server: You are not allowed to shout";
3265 send_to_sender = true;
3271 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3274 Send the message to clients
3276 for(core::map<u16, RemoteClient*>::Iterator
3277 i = m_clients.getIterator();
3278 i.atEnd() == false; i++)
3280 // Get client and check that it is valid
3281 RemoteClient *client = i.getNode()->getValue();
3282 assert(client->peer_id == i.getNode()->getKey());
3283 if(client->serialization_version == SER_FMT_VER_INVALID)
3287 bool sender_selected = (peer_id == client->peer_id);
3288 if(sender_selected == true && send_to_sender == false)
3290 if(sender_selected == false && send_to_others == false)
3293 SendChatMessage(client->peer_id, line);
3297 else if(command == TOSERVER_DAMAGE)
3299 if(g_settings.getBool("enable_damage"))
3301 std::string datastring((char*)&data[2], datasize-2);
3302 std::istringstream is(datastring, std::ios_base::binary);
3303 u8 damage = readU8(is);
3304 if(player->hp > damage)
3306 player->hp -= damage;
3312 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3315 v3f pos = findSpawnPos(m_env.getServerMap());
3316 player->setPosition(pos);
3318 SendMovePlayer(player);
3319 SendPlayerHP(player);
3321 //TODO: Throw items around
3325 SendPlayerHP(player);
3327 else if(command == TOSERVER_PASSWORD)
3330 [0] u16 TOSERVER_PASSWORD
3331 [2] u8[28] old password
3332 [30] u8[28] new password
3335 if(datasize != 2+PASSWORD_SIZE*2)
3337 /*char password[PASSWORD_SIZE];
3338 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3339 password[i] = data[2+i];
3340 password[PASSWORD_SIZE-1] = 0;*/
3342 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3350 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3352 char c = data[2+PASSWORD_SIZE+i];
3358 dstream<<"Server: Client requests a password change from "
3359 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3361 std::string playername = player->getName();
3363 if(m_authmanager.exists(playername) == false)
3365 dstream<<"Server: playername not found in authmanager"<<std::endl;
3366 // Wrong old password supplied!!
3367 SendChatMessage(peer_id, L"playername not found in authmanager");
3371 std::string checkpwd = m_authmanager.getPassword(playername);
3373 if(oldpwd != checkpwd)
3375 dstream<<"Server: invalid old password"<<std::endl;
3376 // Wrong old password supplied!!
3377 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3381 m_authmanager.setPassword(playername, newpwd);
3383 dstream<<"Server: password change successful for "<<playername
3385 SendChatMessage(peer_id, L"Password change successful");
3387 else if (command == TOSERVER_PLAYERITEM)
3392 u16 item = readU16(&data[2]);
3393 player->wieldItem(item);
3394 SendWieldedItem(player);
3398 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3399 "unknown command "<<command<<std::endl;
3403 catch(SendFailedException &e)
3405 derr_server<<"Server::ProcessData(): SendFailedException: "
3411 void Server::onMapEditEvent(MapEditEvent *event)
3413 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3414 if(m_ignore_map_edit_events)
3416 MapEditEvent *e = event->clone();
3417 m_unsent_map_edit_queue.push_back(e);
3420 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3422 if(id == "current_player")
3424 assert(c->current_player);
3425 return &(c->current_player->inventory);
3429 std::string id0 = fn.next(":");
3431 if(id0 == "nodemeta")
3434 p.X = stoi(fn.next(","));
3435 p.Y = stoi(fn.next(","));
3436 p.Z = stoi(fn.next(","));
3437 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3439 return meta->getInventory();
3440 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3441 <<"no metadata found"<<std::endl;
3445 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3448 void Server::inventoryModified(InventoryContext *c, std::string id)
3450 if(id == "current_player")
3452 assert(c->current_player);
3454 UpdateCrafting(c->current_player->peer_id);
3455 SendInventory(c->current_player->peer_id);
3460 std::string id0 = fn.next(":");
3462 if(id0 == "nodemeta")
3465 p.X = stoi(fn.next(","));
3466 p.Y = stoi(fn.next(","));
3467 p.Z = stoi(fn.next(","));
3468 v3s16 blockpos = getNodeBlockPos(p);
3470 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3472 meta->inventoryModified();
3474 for(core::map<u16, RemoteClient*>::Iterator
3475 i = m_clients.getIterator();
3476 i.atEnd()==false; i++)
3478 RemoteClient *client = i.getNode()->getValue();
3479 client->SetBlockNotSent(blockpos);
3485 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3488 core::list<PlayerInfo> Server::getPlayerInfo()
3490 DSTACK(__FUNCTION_NAME);
3491 JMutexAutoLock envlock(m_env_mutex);
3492 JMutexAutoLock conlock(m_con_mutex);
3494 core::list<PlayerInfo> list;
3496 core::list<Player*> players = m_env.getPlayers();
3498 core::list<Player*>::Iterator i;
3499 for(i = players.begin();
3500 i != players.end(); i++)
3504 Player *player = *i;
3507 con::Peer *peer = m_con.GetPeer(player->peer_id);
3508 // Copy info from peer to info struct
3510 info.address = peer->address;
3511 info.avg_rtt = peer->avg_rtt;
3513 catch(con::PeerNotFoundException &e)
3515 // Set dummy peer info
3517 info.address = Address(0,0,0,0,0);
3521 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3522 info.position = player->getPosition();
3524 list.push_back(info);
3531 void Server::peerAdded(con::Peer *peer)
3533 DSTACK(__FUNCTION_NAME);
3534 dout_server<<"Server::peerAdded(): peer->id="
3535 <<peer->id<<std::endl;
3538 c.type = PEER_ADDED;
3539 c.peer_id = peer->id;
3541 m_peer_change_queue.push_back(c);
3544 void Server::deletingPeer(con::Peer *peer, bool timeout)
3546 DSTACK(__FUNCTION_NAME);
3547 dout_server<<"Server::deletingPeer(): peer->id="
3548 <<peer->id<<", timeout="<<timeout<<std::endl;
3551 c.type = PEER_REMOVED;
3552 c.peer_id = peer->id;
3553 c.timeout = timeout;
3554 m_peer_change_queue.push_back(c);
3561 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3563 DSTACK(__FUNCTION_NAME);
3564 std::ostringstream os(std::ios_base::binary);
3566 writeU16(os, TOCLIENT_HP);
3570 std::string s = os.str();
3571 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3573 con.Send(peer_id, 0, data, true);
3576 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3577 const std::wstring &reason)
3579 DSTACK(__FUNCTION_NAME);
3580 std::ostringstream os(std::ios_base::binary);
3582 writeU16(os, TOCLIENT_ACCESS_DENIED);
3583 os<<serializeWideString(reason);
3586 std::string s = os.str();
3587 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3589 con.Send(peer_id, 0, data, true);
3593 Non-static send methods
3596 void Server::SendObjectData(float dtime)
3598 DSTACK(__FUNCTION_NAME);
3600 core::map<v3s16, bool> stepped_blocks;
3602 for(core::map<u16, RemoteClient*>::Iterator
3603 i = m_clients.getIterator();
3604 i.atEnd() == false; i++)
3606 u16 peer_id = i.getNode()->getKey();
3607 RemoteClient *client = i.getNode()->getValue();
3608 assert(client->peer_id == peer_id);
3610 if(client->serialization_version == SER_FMT_VER_INVALID)
3613 client->SendObjectData(this, dtime, stepped_blocks);
3617 void Server::SendPlayerInfos()
3619 DSTACK(__FUNCTION_NAME);
3621 //JMutexAutoLock envlock(m_env_mutex);
3623 // Get connected players
3624 core::list<Player*> players = m_env.getPlayers(true);
3626 u32 player_count = players.getSize();
3627 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3629 SharedBuffer<u8> data(datasize);
3630 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3633 core::list<Player*>::Iterator i;
3634 for(i = players.begin();
3635 i != players.end(); i++)
3637 Player *player = *i;
3639 /*dstream<<"Server sending player info for player with "
3640 "peer_id="<<player->peer_id<<std::endl;*/
3642 writeU16(&data[start], player->peer_id);
3643 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3644 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3645 start += 2+PLAYERNAME_SIZE;
3648 //JMutexAutoLock conlock(m_con_mutex);
3651 m_con.SendToAll(0, data, true);
3654 void Server::SendInventory(u16 peer_id)
3656 DSTACK(__FUNCTION_NAME);
3658 Player* player = m_env.getPlayer(peer_id);
3665 std::ostringstream os;
3666 //os.imbue(std::locale("C"));
3668 player->inventory.serialize(os);
3670 std::string s = os.str();
3672 SharedBuffer<u8> data(s.size()+2);
3673 writeU16(&data[0], TOCLIENT_INVENTORY);
3674 memcpy(&data[2], s.c_str(), s.size());
3677 m_con.Send(peer_id, 0, data, true);
3680 std::string getWieldedItemString(const Player *player)
3682 const InventoryItem *item = player->getWieldItem();
3684 return std::string("");
3685 std::ostringstream os(std::ios_base::binary);
3686 item->serialize(os);
3690 void Server::SendWieldedItem(const Player* player)
3692 DSTACK(__FUNCTION_NAME);
3696 std::ostringstream os(std::ios_base::binary);
3698 writeU16(os, TOCLIENT_PLAYERITEM);
3700 writeU16(os, player->peer_id);
3701 os<<serializeString(getWieldedItemString(player));
3704 std::string s = os.str();
3705 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3707 m_con.SendToAll(0, data, true);
3710 void Server::SendPlayerItems()
3712 DSTACK(__FUNCTION_NAME);
3714 std::ostringstream os(std::ios_base::binary);
3715 core::list<Player *> players = m_env.getPlayers(true);
3717 writeU16(os, TOCLIENT_PLAYERITEM);
3718 writeU16(os, players.size());
3719 core::list<Player *>::Iterator i;
3720 for(i = players.begin(); i != players.end(); ++i)
3723 writeU16(os, p->peer_id);
3724 os<<serializeString(getWieldedItemString(p));
3728 std::string s = os.str();
3729 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3731 m_con.SendToAll(0, data, true);
3734 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3736 DSTACK(__FUNCTION_NAME);
3738 std::ostringstream os(std::ios_base::binary);
3742 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3743 os.write((char*)buf, 2);
3746 writeU16(buf, message.size());
3747 os.write((char*)buf, 2);
3750 for(u32 i=0; i<message.size(); i++)
3754 os.write((char*)buf, 2);
3758 std::string s = os.str();
3759 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3761 m_con.Send(peer_id, 0, data, true);
3764 void Server::BroadcastChatMessage(const std::wstring &message)
3766 for(core::map<u16, RemoteClient*>::Iterator
3767 i = m_clients.getIterator();
3768 i.atEnd() == false; i++)
3770 // Get client and check that it is valid
3771 RemoteClient *client = i.getNode()->getValue();
3772 assert(client->peer_id == i.getNode()->getKey());
3773 if(client->serialization_version == SER_FMT_VER_INVALID)
3776 SendChatMessage(client->peer_id, message);
3780 void Server::SendPlayerHP(Player *player)
3782 SendHP(m_con, player->peer_id, player->hp);
3785 void Server::SendMovePlayer(Player *player)
3787 DSTACK(__FUNCTION_NAME);
3788 std::ostringstream os(std::ios_base::binary);
3790 writeU16(os, TOCLIENT_MOVE_PLAYER);
3791 writeV3F1000(os, player->getPosition());
3792 writeF1000(os, player->getPitch());
3793 writeF1000(os, player->getYaw());
3796 v3f pos = player->getPosition();
3797 f32 pitch = player->getPitch();
3798 f32 yaw = player->getYaw();
3799 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3800 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3807 std::string s = os.str();
3808 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3810 m_con.Send(player->peer_id, 0, data, true);
3813 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3814 core::list<u16> *far_players, float far_d_nodes)
3816 float maxd = far_d_nodes*BS;
3817 v3f p_f = intToFloat(p, BS);
3821 SharedBuffer<u8> reply(replysize);
3822 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3823 writeS16(&reply[2], p.X);
3824 writeS16(&reply[4], p.Y);
3825 writeS16(&reply[6], p.Z);
3827 for(core::map<u16, RemoteClient*>::Iterator
3828 i = m_clients.getIterator();
3829 i.atEnd() == false; i++)
3831 // Get client and check that it is valid
3832 RemoteClient *client = i.getNode()->getValue();
3833 assert(client->peer_id == i.getNode()->getKey());
3834 if(client->serialization_version == SER_FMT_VER_INVALID)
3837 // Don't send if it's the same one
3838 if(client->peer_id == ignore_id)
3844 Player *player = m_env.getPlayer(client->peer_id);
3847 // If player is far away, only set modified blocks not sent
3848 v3f player_pos = player->getPosition();
3849 if(player_pos.getDistanceFrom(p_f) > maxd)
3851 far_players->push_back(client->peer_id);
3858 m_con.Send(client->peer_id, 0, reply, true);
3862 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3863 core::list<u16> *far_players, float far_d_nodes)
3865 float maxd = far_d_nodes*BS;
3866 v3f p_f = intToFloat(p, BS);
3868 for(core::map<u16, RemoteClient*>::Iterator
3869 i = m_clients.getIterator();
3870 i.atEnd() == false; i++)
3872 // Get client and check that it is valid
3873 RemoteClient *client = i.getNode()->getValue();
3874 assert(client->peer_id == i.getNode()->getKey());
3875 if(client->serialization_version == SER_FMT_VER_INVALID)
3878 // Don't send if it's the same one
3879 if(client->peer_id == ignore_id)
3885 Player *player = m_env.getPlayer(client->peer_id);
3888 // If player is far away, only set modified blocks not sent
3889 v3f player_pos = player->getPosition();
3890 if(player_pos.getDistanceFrom(p_f) > maxd)
3892 far_players->push_back(client->peer_id);
3899 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3900 SharedBuffer<u8> reply(replysize);
3901 writeU16(&reply[0], TOCLIENT_ADDNODE);
3902 writeS16(&reply[2], p.X);
3903 writeS16(&reply[4], p.Y);
3904 writeS16(&reply[6], p.Z);
3905 n.serialize(&reply[8], client->serialization_version);
3908 m_con.Send(client->peer_id, 0, reply, true);
3912 void Server::setBlockNotSent(v3s16 p)
3914 for(core::map<u16, RemoteClient*>::Iterator
3915 i = m_clients.getIterator();
3916 i.atEnd()==false; i++)
3918 RemoteClient *client = i.getNode()->getValue();
3919 client->SetBlockNotSent(p);
3923 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3925 DSTACK(__FUNCTION_NAME);
3927 v3s16 p = block->getPos();
3931 bool completely_air = true;
3932 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3933 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3934 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3936 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3938 completely_air = false;
3939 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3944 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3946 dstream<<"[completely air] ";
3951 Create a packet with the block in the right format
3954 std::ostringstream os(std::ios_base::binary);
3955 block->serialize(os, ver);
3956 std::string s = os.str();
3957 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3959 u32 replysize = 8 + blockdata.getSize();
3960 SharedBuffer<u8> reply(replysize);
3961 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3962 writeS16(&reply[2], p.X);
3963 writeS16(&reply[4], p.Y);
3964 writeS16(&reply[6], p.Z);
3965 memcpy(&reply[8], *blockdata, blockdata.getSize());
3967 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3968 <<": \tpacket size: "<<replysize<<std::endl;*/
3973 m_con.Send(peer_id, 1, reply, true);
3976 void Server::SendBlocks(float dtime)
3978 DSTACK(__FUNCTION_NAME);
3980 JMutexAutoLock envlock(m_env_mutex);
3981 JMutexAutoLock conlock(m_con_mutex);
3983 //TimeTaker timer("Server::SendBlocks");
3985 core::array<PrioritySortedBlockTransfer> queue;
3987 s32 total_sending = 0;
3990 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3992 for(core::map<u16, RemoteClient*>::Iterator
3993 i = m_clients.getIterator();
3994 i.atEnd() == false; i++)
3996 RemoteClient *client = i.getNode()->getValue();
3997 assert(client->peer_id == i.getNode()->getKey());
3999 total_sending += client->SendingCount();
4001 if(client->serialization_version == SER_FMT_VER_INVALID)
4004 client->GetNextBlocks(this, dtime, queue);
4009 // Lowest priority number comes first.
4010 // Lowest is most important.
4013 for(u32 i=0; i<queue.size(); i++)
4015 //TODO: Calculate limit dynamically
4016 if(total_sending >= g_settings.getS32
4017 ("max_simultaneous_block_sends_server_total"))
4020 PrioritySortedBlockTransfer q = queue[i];
4022 MapBlock *block = NULL;
4025 block = m_env.getMap().getBlockNoCreate(q.pos);
4027 catch(InvalidPositionException &e)
4032 RemoteClient *client = getClient(q.peer_id);
4034 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4036 client->SentBlock(q.pos);
4046 void Server::UpdateCrafting(u16 peer_id)
4048 DSTACK(__FUNCTION_NAME);
4050 Player* player = m_env.getPlayer(peer_id);
4054 Calculate crafting stuff
4056 if(g_settings.getBool("creative_mode") == false)
4058 InventoryList *clist = player->inventory.getList("craft");
4059 InventoryList *rlist = player->inventory.getList("craftresult");
4061 if(rlist->getUsedSlots() == 0)
4062 player->craftresult_is_preview = true;
4064 if(rlist && player->craftresult_is_preview)
4066 rlist->clearItems();
4068 if(clist && rlist && player->craftresult_is_preview)
4070 InventoryItem *items[9];
4071 for(u16 i=0; i<9; i++)
4073 items[i] = clist->getItem(i);
4076 // Get result of crafting grid
4077 InventoryItem *result = craft_get_result(items);
4079 rlist->addItem(result);
4082 } // if creative_mode == false
4085 RemoteClient* Server::getClient(u16 peer_id)
4087 DSTACK(__FUNCTION_NAME);
4088 //JMutexAutoLock lock(m_con_mutex);
4089 core::map<u16, RemoteClient*>::Node *n;
4090 n = m_clients.find(peer_id);
4091 // A client should exist for all peers
4093 return n->getValue();
4096 std::wstring Server::getStatusString()
4098 std::wostringstream os(std::ios_base::binary);
4101 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4103 os<<L", uptime="<<m_uptime.get();
4104 // Information about clients
4106 for(core::map<u16, RemoteClient*>::Iterator
4107 i = m_clients.getIterator();
4108 i.atEnd() == false; i++)
4110 // Get client and check that it is valid
4111 RemoteClient *client = i.getNode()->getValue();
4112 assert(client->peer_id == i.getNode()->getKey());
4113 if(client->serialization_version == SER_FMT_VER_INVALID)
4116 Player *player = m_env.getPlayer(client->peer_id);
4117 // Get name of player
4118 std::wstring name = L"unknown";
4120 name = narrow_to_wide(player->getName());
4121 // Add name to information string
4125 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4126 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4127 if(g_settings.get("motd") != "")
4128 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4132 v3f findSpawnPos(ServerMap &map)
4134 //return v3f(50,50,50)*BS;
4137 s16 groundheight = 0;
4140 nodepos = v2s16(0,0);
4145 // Try to find a good place a few times
4146 for(s32 i=0; i<1000; i++)
4149 // We're going to try to throw the player to this position
4150 nodepos = v2s16(-range + (myrand()%(range*2)),
4151 -range + (myrand()%(range*2)));
4152 v2s16 sectorpos = getNodeSectorPos(nodepos);
4153 // Get sector (NOTE: Don't get because it's slow)
4154 //m_env.getMap().emergeSector(sectorpos);
4155 // Get ground height at point (fallbacks to heightmap function)
4156 groundheight = map.findGroundLevel(nodepos);
4157 // Don't go underwater
4158 if(groundheight < WATER_LEVEL)
4160 //dstream<<"-> Underwater"<<std::endl;
4163 // Don't go to high places
4164 if(groundheight > WATER_LEVEL + 4)
4166 //dstream<<"-> Underwater"<<std::endl;
4170 // Found a good place
4171 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4176 // If no suitable place was not found, go above water at least.
4177 if(groundheight < WATER_LEVEL)
4178 groundheight = WATER_LEVEL;
4180 return intToFloat(v3s16(
4187 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4190 Try to get an existing player
4192 Player *player = m_env.getPlayer(name);
4195 // If player is already connected, cancel
4196 if(player->peer_id != 0)
4198 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4203 player->peer_id = peer_id;
4205 // Reset inventory to creative if in creative mode
4206 if(g_settings.getBool("creative_mode"))
4208 // Warning: double code below
4209 // Backup actual inventory
4210 player->inventory_backup = new Inventory();
4211 *(player->inventory_backup) = player->inventory;
4212 // Set creative inventory
4213 craft_set_creative_inventory(player);
4220 If player with the wanted peer_id already exists, cancel.
4222 if(m_env.getPlayer(peer_id) != NULL)
4224 dstream<<"emergePlayer(): Player with wrong name but same"
4225 " peer_id already exists"<<std::endl;
4233 player = new ServerRemotePlayer();
4234 //player->peer_id = c.peer_id;
4235 //player->peer_id = PEER_ID_INEXISTENT;
4236 player->peer_id = peer_id;
4237 player->updateName(name);
4238 m_authmanager.add(name);
4239 m_authmanager.setPassword(name, password);
4240 m_authmanager.setPrivs(name,
4241 stringToPrivs(g_settings.get("default_privs")));
4247 dstream<<"Server: Finding spawn place for player \""
4248 <<player->getName()<<"\""<<std::endl;
4250 v3f pos = findSpawnPos(m_env.getServerMap());
4252 player->setPosition(pos);
4255 Add player to environment
4258 m_env.addPlayer(player);
4261 Add stuff to inventory
4264 if(g_settings.getBool("creative_mode"))
4266 // Warning: double code above
4267 // Backup actual inventory
4268 player->inventory_backup = new Inventory();
4269 *(player->inventory_backup) = player->inventory;
4270 // Set creative inventory
4271 craft_set_creative_inventory(player);
4273 else if(g_settings.getBool("give_initial_stuff"))
4275 craft_give_initial_stuff(player);
4280 } // create new player
4283 void Server::handlePeerChange(PeerChange &c)
4285 JMutexAutoLock envlock(m_env_mutex);
4286 JMutexAutoLock conlock(m_con_mutex);
4288 if(c.type == PEER_ADDED)
4295 core::map<u16, RemoteClient*>::Node *n;
4296 n = m_clients.find(c.peer_id);
4297 // The client shouldn't already exist
4301 RemoteClient *client = new RemoteClient();
4302 client->peer_id = c.peer_id;
4303 m_clients.insert(client->peer_id, client);
4306 else if(c.type == PEER_REMOVED)
4313 core::map<u16, RemoteClient*>::Node *n;
4314 n = m_clients.find(c.peer_id);
4315 // The client should exist
4319 Mark objects to be not known by the client
4321 RemoteClient *client = n->getValue();
4323 for(core::map<u16, bool>::Iterator
4324 i = client->m_known_objects.getIterator();
4325 i.atEnd()==false; i++)
4328 u16 id = i.getNode()->getKey();
4329 ServerActiveObject* obj = m_env.getActiveObject(id);
4331 if(obj && obj->m_known_by_count > 0)
4332 obj->m_known_by_count--;
4335 // Collect information about leaving in chat
4336 std::wstring message;
4338 std::wstring name = L"unknown";
4339 Player *player = m_env.getPlayer(c.peer_id);
4341 name = narrow_to_wide(player->getName());
4345 message += L" left game";
4347 message += L" (timed out)";
4352 m_env.removePlayer(c.peer_id);
4355 // Set player client disconnected
4357 Player *player = m_env.getPlayer(c.peer_id);
4359 player->peer_id = 0;
4363 delete m_clients[c.peer_id];
4364 m_clients.remove(c.peer_id);
4366 // Send player info to all remaining clients
4369 // Send leave chat message to all remaining clients
4370 BroadcastChatMessage(message);
4379 void Server::handlePeerChanges()
4381 while(m_peer_change_queue.size() > 0)
4383 PeerChange c = m_peer_change_queue.pop_front();
4385 dout_server<<"Server: Handling peer change: "
4386 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4389 handlePeerChange(c);
4393 u64 Server::getPlayerPrivs(Player *player)
4397 std::string playername = player->getName();
4398 // Local player gets all privileges regardless of
4399 // what's set on their account.
4400 if(g_settings.get("name") == playername)
4406 return getPlayerAuthPrivs(playername);
4410 void dedicated_server_loop(Server &server, bool &kill)
4412 DSTACK(__FUNCTION_NAME);
4414 dstream<<DTIME<<std::endl;
4415 dstream<<"========================"<<std::endl;
4416 dstream<<"Running dedicated server"<<std::endl;
4417 dstream<<"========================"<<std::endl;
4420 IntervalLimiter m_profiler_interval;
4424 // This is kind of a hack but can be done like this
4425 // because server.step() is very light
4427 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4432 if(server.getShutdownRequested() || kill)
4434 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4441 float profiler_print_interval =
4442 g_settings.getFloat("profiler_print_interval");
4443 if(profiler_print_interval != 0)
4445 if(m_profiler_interval.step(0.030, profiler_print_interval))
4447 dstream<<"Profiler:"<<std::endl;
4448 g_profiler.print(dstream);
4456 static int counter = 0;
4462 core::list<PlayerInfo> list = server.getPlayerInfo();
4463 core::list<PlayerInfo>::Iterator i;
4464 static u32 sum_old = 0;
4465 u32 sum = PIChecksum(list);
4468 dstream<<DTIME<<"Player info:"<<std::endl;
4469 for(i=list.begin(); i!=list.end(); i++)
4471 i->PrintLine(&dstream);