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
585 FIXME This only works if the client uses a small enough
586 FOV setting. The default of 72 degrees is fine.
589 float camera_fov = (72.0*PI/180) * 4./3.;
590 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
596 Don't send already sent blocks
599 if(m_blocks_sent.find(p) != NULL)
606 Check if map has this block
608 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
610 bool surely_not_found_on_disk = false;
611 bool block_is_invalid = false;
614 // Reset usage timer, this block will be of use in the future.
615 block->resetUsageTimer();
617 // Block is dummy if data doesn't exist.
618 // It means it has been not found from disk and not generated
621 surely_not_found_on_disk = true;
624 // Block is valid if lighting is up-to-date and data exists
625 if(block->isValid() == false)
627 block_is_invalid = true;
630 /*if(block->isFullyGenerated() == false)
632 block_is_invalid = true;
637 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
638 v2s16 chunkpos = map->sector_to_chunk(p2d);
639 if(map->chunkNonVolatile(chunkpos) == false)
640 block_is_invalid = true;
642 if(block->isGenerated() == false)
643 block_is_invalid = true;
646 If block is not close, don't send it unless it is near
649 Block is near ground level if night-time mesh
650 differs from day-time mesh.
654 if(block->dayNightDiffed() == false)
661 If block has been marked to not exist on disk (dummy)
662 and generating new ones is not wanted, skip block.
664 if(generate == false && surely_not_found_on_disk == true)
671 Record the lowest d from which a block has been
672 found being not sent and possibly to exist
674 if(no_blocks_found_for_sending)
677 new_nearest_unsent_d = d;
680 no_blocks_found_for_sending = false;
683 Add inexistent block to emerge queue.
685 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
687 //TODO: Get value from somewhere
688 // Allow only one block in emerge queue
689 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
690 // Allow two blocks in queue per client
691 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
693 //dstream<<"Adding block to emerge queue"<<std::endl;
695 // Add it to the emerge queue and trigger the thread
698 if(generate == false)
699 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
701 server->m_emerge_queue.addBlock(peer_id, p, flags);
702 server->m_emergethread.trigger();
710 Add block to send queue
713 PrioritySortedBlockTransfer q((float)d, p, peer_id);
717 num_blocks_selected += 1;
718 sending_something = true;
723 //dstream<<"Stopped at "<<d<<std::endl;
725 if(no_blocks_found_for_sending)
727 if(queue_is_full == false)
728 new_nearest_unsent_d = d;
731 if(new_nearest_unsent_d != -1)
732 m_nearest_unsent_d = new_nearest_unsent_d;
734 if(sending_something == false)
736 m_nothing_to_send_counter++;
737 if((s16)m_nothing_to_send_counter >=
738 g_settings.getS16("max_block_send_distance"))
740 // Pause time in seconds
741 m_nothing_to_send_pause_timer = 1.0;
742 /*dstream<<"nothing to send to "
743 <<server->getPlayerName(peer_id)
744 <<" (d="<<d<<")"<<std::endl;*/
749 m_nothing_to_send_counter = 0;
752 /*timer_result = timer.stop(true);
753 if(timer_result != 0)
754 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
757 void RemoteClient::SendObjectData(
760 core::map<v3s16, bool> &stepped_blocks
763 DSTACK(__FUNCTION_NAME);
765 // Can't send anything without knowing version
766 if(serialization_version == SER_FMT_VER_INVALID)
768 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
774 Send a TOCLIENT_OBJECTDATA packet.
778 u16 number of player positions
790 std::ostringstream os(std::ios_base::binary);
794 writeU16(buf, TOCLIENT_OBJECTDATA);
795 os.write((char*)buf, 2);
798 Get and write player data
801 // Get connected players
802 core::list<Player*> players = server->m_env.getPlayers(true);
804 // Write player count
805 u16 playercount = players.size();
806 writeU16(buf, playercount);
807 os.write((char*)buf, 2);
809 core::list<Player*>::Iterator i;
810 for(i = players.begin();
811 i != players.end(); i++)
815 v3f pf = player->getPosition();
816 v3f sf = player->getSpeed();
818 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
819 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
820 s32 pitch_i (player->getPitch() * 100);
821 s32 yaw_i (player->getYaw() * 100);
823 writeU16(buf, player->peer_id);
824 os.write((char*)buf, 2);
825 writeV3S32(buf, position_i);
826 os.write((char*)buf, 12);
827 writeV3S32(buf, speed_i);
828 os.write((char*)buf, 12);
829 writeS32(buf, pitch_i);
830 os.write((char*)buf, 4);
831 writeS32(buf, yaw_i);
832 os.write((char*)buf, 4);
836 Get and write object data
842 For making players to be able to build to their nearby
843 environment (building is not possible on blocks that are not
846 - Add blocks to emerge queue if they are not found
848 SUGGESTION: These could be ignored from the backside of the player
851 Player *player = server->m_env.getPlayer(peer_id);
855 v3f playerpos = player->getPosition();
856 v3f playerspeed = player->getSpeed();
858 v3s16 center_nodepos = floatToInt(playerpos, BS);
859 v3s16 center = getNodeBlockPos(center_nodepos);
861 s16 d_max = g_settings.getS16("active_object_range");
863 // Number of blocks whose objects were written to bos
866 std::ostringstream bos(std::ios_base::binary);
868 for(s16 d = 0; d <= d_max; d++)
870 core::list<v3s16> list;
871 getFacePositions(list, d);
873 core::list<v3s16>::Iterator li;
874 for(li=list.begin(); li!=list.end(); li++)
876 v3s16 p = *li + center;
879 Ignore blocks that haven't been sent to the client
882 if(m_blocks_sent.find(p) == NULL)
886 // Try stepping block and add it to a send queue
891 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
894 Step block if not in stepped_blocks and add to stepped_blocks.
896 if(stepped_blocks.find(p) == NULL)
898 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
899 stepped_blocks.insert(p, true);
900 block->setChangedFlag();
903 // Skip block if there are no objects
904 if(block->getObjectCount() == 0)
913 bos.write((char*)buf, 6);
916 //block->serializeObjects(bos, serialization_version); // DEPRECATED
923 Stop collecting objects if data is already too big
925 // Sum of player and object data sizes
926 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
927 // break out if data too big
928 if(sum > MAX_OBJECTDATA_SIZE)
930 goto skip_subsequent;
934 catch(InvalidPositionException &e)
937 // Add it to the emerge queue and trigger the thread.
938 // Fetch the block only if it is on disk.
940 // Grab and increment counter
941 /*SharedPtr<JMutexAutoLock> lock
942 (m_num_blocks_in_emerge_queue.getLock());
943 m_num_blocks_in_emerge_queue.m_value++;*/
945 // Add to queue as an anonymous fetch from disk
946 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
947 server->m_emerge_queue.addBlock(0, p, flags);
948 server->m_emergethread.trigger();
956 writeU16(buf, blockcount);
957 os.write((char*)buf, 2);
959 // Write block objects
966 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
969 std::string s = os.str();
970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
971 // Send as unreliable
972 server->m_con.Send(peer_id, 0, data, false);
975 void RemoteClient::GotBlock(v3s16 p)
977 if(m_blocks_sending.find(p) != NULL)
978 m_blocks_sending.remove(p);
981 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
982 " m_blocks_sending"<<std::endl;*/
983 m_excess_gotblocks++;
985 m_blocks_sent.insert(p, true);
988 void RemoteClient::SentBlock(v3s16 p)
990 if(m_blocks_sending.find(p) == NULL)
991 m_blocks_sending.insert(p, 0.0);
993 dstream<<"RemoteClient::SentBlock(): Sent block"
994 " already in m_blocks_sending"<<std::endl;
997 void RemoteClient::SetBlockNotSent(v3s16 p)
999 m_nearest_unsent_d = 0;
1001 if(m_blocks_sending.find(p) != NULL)
1002 m_blocks_sending.remove(p);
1003 if(m_blocks_sent.find(p) != NULL)
1004 m_blocks_sent.remove(p);
1007 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1009 m_nearest_unsent_d = 0;
1011 for(core::map<v3s16, MapBlock*>::Iterator
1012 i = blocks.getIterator();
1013 i.atEnd()==false; i++)
1015 v3s16 p = i.getNode()->getKey();
1017 if(m_blocks_sending.find(p) != NULL)
1018 m_blocks_sending.remove(p);
1019 if(m_blocks_sent.find(p) != NULL)
1020 m_blocks_sent.remove(p);
1028 PlayerInfo::PlayerInfo()
1034 void PlayerInfo::PrintLine(std::ostream *s)
1037 (*s)<<"\""<<name<<"\" ("
1038 <<(position.X/10)<<","<<(position.Y/10)
1039 <<","<<(position.Z/10)<<") ";
1041 (*s)<<" avg_rtt="<<avg_rtt;
1045 u32 PIChecksum(core::list<PlayerInfo> &l)
1047 core::list<PlayerInfo>::Iterator i;
1050 for(i=l.begin(); i!=l.end(); i++)
1052 checksum += a * (i->id+1);
1053 checksum ^= 0x435aafcd;
1064 std::string mapsavedir,
1065 std::string configpath
1067 m_env(new ServerMap(mapsavedir), this),
1068 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1069 m_authmanager(mapsavedir+"/auth.txt"),
1070 m_banmanager(mapsavedir+"/ipban.txt"),
1072 m_emergethread(this),
1074 m_time_of_day_send_timer(0),
1076 m_mapsavedir(mapsavedir),
1077 m_configpath(configpath),
1078 m_shutdown_requested(false),
1079 m_ignore_map_edit_events(false),
1080 m_ignore_map_edit_events_peer_id(0)
1082 m_liquid_transform_timer = 0.0;
1083 m_print_info_timer = 0.0;
1084 m_objectdata_timer = 0.0;
1085 m_emergethread_trigger_timer = 0.0;
1086 m_savemap_timer = 0.0;
1090 m_step_dtime_mutex.Init();
1093 // Register us to receive map edit events
1094 m_env.getMap().addEventReceiver(this);
1096 // If file exists, load environment metadata
1097 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1099 dstream<<"Server: Loading environment metadata"<<std::endl;
1100 m_env.loadMeta(m_mapsavedir);
1104 dstream<<"Server: Loading players"<<std::endl;
1105 m_env.deSerializePlayers(m_mapsavedir);
1110 dstream<<"Server::~Server()"<<std::endl;
1113 Send shutdown message
1116 JMutexAutoLock conlock(m_con_mutex);
1118 std::wstring line = L"*** Server shutting down";
1121 Send the message to clients
1123 for(core::map<u16, RemoteClient*>::Iterator
1124 i = m_clients.getIterator();
1125 i.atEnd() == false; i++)
1127 // Get client and check that it is valid
1128 RemoteClient *client = i.getNode()->getValue();
1129 assert(client->peer_id == i.getNode()->getKey());
1130 if(client->serialization_version == SER_FMT_VER_INVALID)
1134 SendChatMessage(client->peer_id, line);
1136 catch(con::PeerNotFoundException &e)
1144 dstream<<"Server: Saving players"<<std::endl;
1145 m_env.serializePlayers(m_mapsavedir);
1148 Save environment metadata
1150 dstream<<"Server: Saving environment metadata"<<std::endl;
1151 m_env.saveMeta(m_mapsavedir);
1162 JMutexAutoLock clientslock(m_con_mutex);
1164 for(core::map<u16, RemoteClient*>::Iterator
1165 i = m_clients.getIterator();
1166 i.atEnd() == false; i++)
1169 // NOTE: These are removed by env destructor
1171 u16 peer_id = i.getNode()->getKey();
1172 JMutexAutoLock envlock(m_env_mutex);
1173 m_env.removePlayer(peer_id);
1177 delete i.getNode()->getValue();
1182 void Server::start(unsigned short port)
1184 DSTACK(__FUNCTION_NAME);
1185 // Stop thread if already running
1188 // Initialize connection
1189 m_con.setTimeoutMs(30);
1193 m_thread.setRun(true);
1196 dout_server<<"Server: Started on port "<<port<<std::endl;
1201 DSTACK(__FUNCTION_NAME);
1203 // Stop threads (set run=false first so both start stopping)
1204 m_thread.setRun(false);
1205 m_emergethread.setRun(false);
1207 m_emergethread.stop();
1209 dout_server<<"Server: Threads stopped"<<std::endl;
1212 void Server::step(float dtime)
1214 DSTACK(__FUNCTION_NAME);
1219 JMutexAutoLock lock(m_step_dtime_mutex);
1220 m_step_dtime += dtime;
1224 void Server::AsyncRunStep()
1226 DSTACK(__FUNCTION_NAME);
1230 JMutexAutoLock lock1(m_step_dtime_mutex);
1231 dtime = m_step_dtime;
1235 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1236 "blocks to clients");
1237 // Send blocks to clients
1244 //dstream<<"Server steps "<<dtime<<std::endl;
1245 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1248 JMutexAutoLock lock1(m_step_dtime_mutex);
1249 m_step_dtime -= dtime;
1256 m_uptime.set(m_uptime.get() + dtime);
1260 // Process connection's timeouts
1261 JMutexAutoLock lock2(m_con_mutex);
1262 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1263 m_con.RunTimeouts(dtime);
1267 // This has to be called so that the client list gets synced
1268 // with the peer list of the connection
1269 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1270 handlePeerChanges();
1274 Update m_time_of_day and overall game time
1277 JMutexAutoLock envlock(m_env_mutex);
1279 m_time_counter += dtime;
1280 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1281 u32 units = (u32)(m_time_counter*speed);
1282 m_time_counter -= (f32)units / speed;
1284 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1286 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1289 Send to clients at constant intervals
1292 m_time_of_day_send_timer -= dtime;
1293 if(m_time_of_day_send_timer < 0.0)
1295 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1297 //JMutexAutoLock envlock(m_env_mutex);
1298 JMutexAutoLock conlock(m_con_mutex);
1300 for(core::map<u16, RemoteClient*>::Iterator
1301 i = m_clients.getIterator();
1302 i.atEnd() == false; i++)
1304 RemoteClient *client = i.getNode()->getValue();
1305 //Player *player = m_env.getPlayer(client->peer_id);
1307 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1308 m_env.getTimeOfDay());
1310 m_con.Send(client->peer_id, 0, data, true);
1316 JMutexAutoLock lock(m_env_mutex);
1318 ScopeProfiler sp(&g_profiler, "Server: environment step");
1322 const float map_timer_and_unload_dtime = 5.15;
1323 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1325 JMutexAutoLock lock(m_env_mutex);
1326 // Run Map's timers and unload unused data
1327 ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
1328 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1329 g_settings.getFloat("server_unload_unused_data_timeout"));
1339 m_liquid_transform_timer += dtime;
1340 if(m_liquid_transform_timer >= 1.00)
1342 m_liquid_transform_timer -= 1.00;
1344 JMutexAutoLock lock(m_env_mutex);
1346 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1348 core::map<v3s16, MapBlock*> modified_blocks;
1349 m_env.getMap().transformLiquids(modified_blocks);
1354 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1355 ServerMap &map = ((ServerMap&)m_env.getMap());
1356 map.updateLighting(modified_blocks, lighting_modified_blocks);
1358 // Add blocks modified by lighting to modified_blocks
1359 for(core::map<v3s16, MapBlock*>::Iterator
1360 i = lighting_modified_blocks.getIterator();
1361 i.atEnd() == false; i++)
1363 MapBlock *block = i.getNode()->getValue();
1364 modified_blocks.insert(block->getPos(), block);
1368 Set the modified blocks unsent for all the clients
1371 JMutexAutoLock lock2(m_con_mutex);
1373 for(core::map<u16, RemoteClient*>::Iterator
1374 i = m_clients.getIterator();
1375 i.atEnd() == false; i++)
1377 RemoteClient *client = i.getNode()->getValue();
1379 if(modified_blocks.size() > 0)
1381 // Remove block from sent history
1382 client->SetBlocksNotSent(modified_blocks);
1387 // Periodically print some info
1389 float &counter = m_print_info_timer;
1395 JMutexAutoLock lock2(m_con_mutex);
1397 for(core::map<u16, RemoteClient*>::Iterator
1398 i = m_clients.getIterator();
1399 i.atEnd() == false; i++)
1401 //u16 peer_id = i.getNode()->getKey();
1402 RemoteClient *client = i.getNode()->getValue();
1403 Player *player = m_env.getPlayer(client->peer_id);
1406 std::cout<<player->getName()<<"\t";
1407 client->PrintInfo(std::cout);
1412 //if(g_settings.getBool("enable_experimental"))
1416 Check added and deleted active objects
1419 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1420 JMutexAutoLock envlock(m_env_mutex);
1421 JMutexAutoLock conlock(m_con_mutex);
1423 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1425 // Radius inside which objects are active
1428 for(core::map<u16, RemoteClient*>::Iterator
1429 i = m_clients.getIterator();
1430 i.atEnd() == false; i++)
1432 RemoteClient *client = i.getNode()->getValue();
1433 Player *player = m_env.getPlayer(client->peer_id);
1436 // This can happen if the client timeouts somehow
1437 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1439 <<" has no associated player"<<std::endl;*/
1442 v3s16 pos = floatToInt(player->getPosition(), BS);
1444 core::map<u16, bool> removed_objects;
1445 core::map<u16, bool> added_objects;
1446 m_env.getRemovedActiveObjects(pos, radius,
1447 client->m_known_objects, removed_objects);
1448 m_env.getAddedActiveObjects(pos, radius,
1449 client->m_known_objects, added_objects);
1451 // Ignore if nothing happened
1452 if(removed_objects.size() == 0 && added_objects.size() == 0)
1454 //dstream<<"INFO: active objects: none changed"<<std::endl;
1458 std::string data_buffer;
1462 // Handle removed objects
1463 writeU16((u8*)buf, removed_objects.size());
1464 data_buffer.append(buf, 2);
1465 for(core::map<u16, bool>::Iterator
1466 i = removed_objects.getIterator();
1467 i.atEnd()==false; i++)
1470 u16 id = i.getNode()->getKey();
1471 ServerActiveObject* obj = m_env.getActiveObject(id);
1473 // Add to data buffer for sending
1474 writeU16((u8*)buf, i.getNode()->getKey());
1475 data_buffer.append(buf, 2);
1477 // Remove from known objects
1478 client->m_known_objects.remove(i.getNode()->getKey());
1480 if(obj && obj->m_known_by_count > 0)
1481 obj->m_known_by_count--;
1484 // Handle added objects
1485 writeU16((u8*)buf, added_objects.size());
1486 data_buffer.append(buf, 2);
1487 for(core::map<u16, bool>::Iterator
1488 i = added_objects.getIterator();
1489 i.atEnd()==false; i++)
1492 u16 id = i.getNode()->getKey();
1493 ServerActiveObject* obj = m_env.getActiveObject(id);
1496 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1498 dstream<<"WARNING: "<<__FUNCTION_NAME
1499 <<": NULL object"<<std::endl;
1501 type = obj->getType();
1503 // Add to data buffer for sending
1504 writeU16((u8*)buf, id);
1505 data_buffer.append(buf, 2);
1506 writeU8((u8*)buf, type);
1507 data_buffer.append(buf, 1);
1510 data_buffer.append(serializeLongString(
1511 obj->getClientInitializationData()));
1513 data_buffer.append(serializeLongString(""));
1515 // Add to known objects
1516 client->m_known_objects.insert(i.getNode()->getKey(), false);
1519 obj->m_known_by_count++;
1523 SharedBuffer<u8> reply(2 + data_buffer.size());
1524 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1525 memcpy((char*)&reply[2], data_buffer.c_str(),
1526 data_buffer.size());
1528 m_con.Send(client->peer_id, 0, reply, true);
1530 dstream<<"INFO: Server: Sent object remove/add: "
1531 <<removed_objects.size()<<" removed, "
1532 <<added_objects.size()<<" added, "
1533 <<"packet size is "<<reply.getSize()<<std::endl;
1538 Collect a list of all the objects known by the clients
1539 and report it back to the environment.
1542 core::map<u16, bool> all_known_objects;
1544 for(core::map<u16, RemoteClient*>::Iterator
1545 i = m_clients.getIterator();
1546 i.atEnd() == false; i++)
1548 RemoteClient *client = i.getNode()->getValue();
1549 // Go through all known objects of client
1550 for(core::map<u16, bool>::Iterator
1551 i = client->m_known_objects.getIterator();
1552 i.atEnd()==false; i++)
1554 u16 id = i.getNode()->getKey();
1555 all_known_objects[id] = true;
1559 m_env.setKnownActiveObjects(whatever);
1565 Send object messages
1568 JMutexAutoLock envlock(m_env_mutex);
1569 JMutexAutoLock conlock(m_con_mutex);
1571 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1574 // Value = data sent by object
1575 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1577 // Get active object messages from environment
1580 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1584 core::list<ActiveObjectMessage>* message_list = NULL;
1585 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1586 n = buffered_messages.find(aom.id);
1589 message_list = new core::list<ActiveObjectMessage>;
1590 buffered_messages.insert(aom.id, message_list);
1594 message_list = n->getValue();
1596 message_list->push_back(aom);
1599 // Route data to every client
1600 for(core::map<u16, RemoteClient*>::Iterator
1601 i = m_clients.getIterator();
1602 i.atEnd()==false; i++)
1604 RemoteClient *client = i.getNode()->getValue();
1605 std::string reliable_data;
1606 std::string unreliable_data;
1607 // Go through all objects in message buffer
1608 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1609 j = buffered_messages.getIterator();
1610 j.atEnd()==false; j++)
1612 // If object is not known by client, skip it
1613 u16 id = j.getNode()->getKey();
1614 if(client->m_known_objects.find(id) == NULL)
1616 // Get message list of object
1617 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1618 // Go through every message
1619 for(core::list<ActiveObjectMessage>::Iterator
1620 k = list->begin(); k != list->end(); k++)
1622 // Compose the full new data with header
1623 ActiveObjectMessage aom = *k;
1624 std::string new_data;
1627 writeU16((u8*)&buf[0], aom.id);
1628 new_data.append(buf, 2);
1630 new_data += serializeString(aom.datastring);
1631 // Add data to buffer
1633 reliable_data += new_data;
1635 unreliable_data += new_data;
1639 reliable_data and unreliable_data are now ready.
1642 if(reliable_data.size() > 0)
1644 SharedBuffer<u8> reply(2 + reliable_data.size());
1645 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1646 memcpy((char*)&reply[2], reliable_data.c_str(),
1647 reliable_data.size());
1649 m_con.Send(client->peer_id, 0, reply, true);
1651 if(unreliable_data.size() > 0)
1653 SharedBuffer<u8> reply(2 + unreliable_data.size());
1654 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1655 memcpy((char*)&reply[2], unreliable_data.c_str(),
1656 unreliable_data.size());
1657 // Send as unreliable
1658 m_con.Send(client->peer_id, 0, reply, false);
1661 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1663 dstream<<"INFO: Server: Size of object message data: "
1664 <<"reliable: "<<reliable_data.size()
1665 <<", unreliable: "<<unreliable_data.size()
1670 // Clear buffered_messages
1671 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1672 i = buffered_messages.getIterator();
1673 i.atEnd()==false; i++)
1675 delete i.getNode()->getValue();
1679 } // enable_experimental
1682 Send queued-for-sending map edit events.
1685 // Don't send too many at a time
1688 // Single change sending is disabled if queue size is not small
1689 bool disable_single_change_sending = false;
1690 if(m_unsent_map_edit_queue.size() >= 4)
1691 disable_single_change_sending = true;
1693 bool got_any_events = false;
1695 // We'll log the amount of each
1698 while(m_unsent_map_edit_queue.size() != 0)
1700 got_any_events = true;
1702 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1704 // Players far away from the change are stored here.
1705 // Instead of sending the changes, MapBlocks are set not sent
1707 core::list<u16> far_players;
1709 if(event->type == MEET_ADDNODE)
1711 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1712 prof.add("MEET_ADDNODE", 1);
1713 if(disable_single_change_sending)
1714 sendAddNode(event->p, event->n, event->already_known_by_peer,
1717 sendAddNode(event->p, event->n, event->already_known_by_peer,
1720 else if(event->type == MEET_REMOVENODE)
1722 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1723 prof.add("MEET_REMOVENODE", 1);
1724 if(disable_single_change_sending)
1725 sendRemoveNode(event->p, event->already_known_by_peer,
1728 sendRemoveNode(event->p, event->already_known_by_peer,
1731 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1733 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1734 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1735 setBlockNotSent(event->p);
1737 else if(event->type == MEET_OTHER)
1739 dstream<<"Server: MEET_OTHER"<<std::endl;
1740 prof.add("MEET_OTHER", 1);
1741 for(core::map<v3s16, bool>::Iterator
1742 i = event->modified_blocks.getIterator();
1743 i.atEnd()==false; i++)
1745 v3s16 p = i.getNode()->getKey();
1751 prof.add("unknown", 1);
1752 dstream<<"WARNING: Server: Unknown MapEditEvent "
1753 <<((u32)event->type)<<std::endl;
1757 Set blocks not sent to far players
1759 if(far_players.size() > 0)
1761 // Convert list format to that wanted by SetBlocksNotSent
1762 core::map<v3s16, MapBlock*> modified_blocks2;
1763 for(core::map<v3s16, bool>::Iterator
1764 i = event->modified_blocks.getIterator();
1765 i.atEnd()==false; i++)
1767 v3s16 p = i.getNode()->getKey();
1768 modified_blocks2.insert(p,
1769 m_env.getMap().getBlockNoCreateNoEx(p));
1771 // Set blocks not sent
1772 for(core::list<u16>::Iterator
1773 i = far_players.begin();
1774 i != far_players.end(); i++)
1777 RemoteClient *client = getClient(peer_id);
1780 client->SetBlocksNotSent(modified_blocks2);
1786 /*// Don't send too many at a time
1788 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1794 dstream<<"Server: MapEditEvents:"<<std::endl;
1795 prof.print(dstream);
1801 Send object positions
1802 TODO: Get rid of MapBlockObjects
1805 float &counter = m_objectdata_timer;
1807 if(counter >= g_settings.getFloat("objectdata_interval"))
1809 JMutexAutoLock lock1(m_env_mutex);
1810 JMutexAutoLock lock2(m_con_mutex);
1812 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1814 SendObjectData(counter);
1821 Trigger emergethread (it somehow gets to a non-triggered but
1822 bysy state sometimes)
1825 float &counter = m_emergethread_trigger_timer;
1831 m_emergethread.trigger();
1835 // Save map, players and auth stuff
1837 float &counter = m_savemap_timer;
1839 if(counter >= g_settings.getFloat("server_map_save_interval"))
1843 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1846 if(m_authmanager.isModified())
1847 m_authmanager.save();
1850 if(m_banmanager.isModified())
1851 m_banmanager.save();
1854 JMutexAutoLock lock(m_env_mutex);
1856 /*// Unload unused data (delete from memory)
1857 m_env.getMap().unloadUnusedData(
1858 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1860 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1861 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1864 // Save only changed parts
1865 m_env.getMap().save(true);
1867 /*if(deleted_count > 0)
1869 dout_server<<"Server: Unloaded "<<deleted_count
1870 <<" blocks from memory"<<std::endl;
1874 m_env.serializePlayers(m_mapsavedir);
1876 // Save environment metadata
1877 m_env.saveMeta(m_mapsavedir);
1882 void Server::Receive()
1884 DSTACK(__FUNCTION_NAME);
1885 u32 data_maxsize = 10000;
1886 Buffer<u8> data(data_maxsize);
1891 JMutexAutoLock conlock(m_con_mutex);
1892 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1895 // This has to be called so that the client list gets synced
1896 // with the peer list of the connection
1897 handlePeerChanges();
1899 ProcessData(*data, datasize, peer_id);
1901 catch(con::InvalidIncomingDataException &e)
1903 derr_server<<"Server::Receive(): "
1904 "InvalidIncomingDataException: what()="
1905 <<e.what()<<std::endl;
1907 catch(con::PeerNotFoundException &e)
1909 //NOTE: This is not needed anymore
1911 // The peer has been disconnected.
1912 // Find the associated player and remove it.
1914 /*JMutexAutoLock envlock(m_env_mutex);
1916 dout_server<<"ServerThread: peer_id="<<peer_id
1917 <<" has apparently closed connection. "
1918 <<"Removing player."<<std::endl;
1920 m_env.removePlayer(peer_id);*/
1924 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1926 DSTACK(__FUNCTION_NAME);
1927 // Environment is locked first.
1928 JMutexAutoLock envlock(m_env_mutex);
1929 JMutexAutoLock conlock(m_con_mutex);
1933 peer = m_con.GetPeer(peer_id);
1935 catch(con::PeerNotFoundException &e)
1937 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1938 <<peer_id<<" not found"<<std::endl;
1942 // drop player if is ip is banned
1943 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1944 SendAccessDenied(m_con, peer_id,
1945 L"Your ip is banned. Banned name was "
1946 +narrow_to_wide(m_banmanager.getBanName(
1947 peer->address.serializeString())));
1948 m_con.deletePeer(peer_id, false);
1952 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1960 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1962 if(command == TOSERVER_INIT)
1964 // [0] u16 TOSERVER_INIT
1965 // [2] u8 SER_FMT_VER_HIGHEST
1966 // [3] u8[20] player_name
1967 // [23] u8[28] password <--- can be sent without this, from old versions
1969 if(datasize < 2+1+PLAYERNAME_SIZE)
1972 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1973 <<peer->id<<std::endl;
1975 // First byte after command is maximum supported
1976 // serialization version
1977 u8 client_max = data[2];
1978 u8 our_max = SER_FMT_VER_HIGHEST;
1979 // Use the highest version supported by both
1980 u8 deployed = core::min_(client_max, our_max);
1981 // If it's lower than the lowest supported, give up.
1982 if(deployed < SER_FMT_VER_LOWEST)
1983 deployed = SER_FMT_VER_INVALID;
1985 //peer->serialization_version = deployed;
1986 getClient(peer->id)->pending_serialization_version = deployed;
1988 if(deployed == SER_FMT_VER_INVALID)
1990 derr_server<<DTIME<<"Server: Cannot negotiate "
1991 "serialization version with peer "
1992 <<peer_id<<std::endl;
1993 SendAccessDenied(m_con, peer_id,
1994 L"Your client is too old (map format)");
1999 Read and check network protocol version
2002 u16 net_proto_version = 0;
2003 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2005 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2008 getClient(peer->id)->net_proto_version = net_proto_version;
2010 if(net_proto_version == 0)
2012 SendAccessDenied(m_con, peer_id,
2013 L"Your client is too old. Please upgrade.");
2022 char playername[PLAYERNAME_SIZE];
2023 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2025 playername[i] = data[3+i];
2027 playername[PLAYERNAME_SIZE-1] = 0;
2029 if(playername[0]=='\0')
2031 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2032 SendAccessDenied(m_con, peer_id,
2037 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2039 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2040 SendAccessDenied(m_con, peer_id,
2041 L"Name contains unallowed characters");
2046 char password[PASSWORD_SIZE];
2047 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2049 // old version - assume blank password
2054 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2056 password[i] = data[23+i];
2058 password[PASSWORD_SIZE-1] = 0;
2061 std::string checkpwd;
2062 if(m_authmanager.exists(playername))
2064 checkpwd = m_authmanager.getPassword(playername);
2068 checkpwd = g_settings.get("default_password");
2071 /*dstream<<"Server: Client gave password '"<<password
2072 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2074 if(password != checkpwd && m_authmanager.exists(playername))
2076 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2077 <<": supplied invalid password for "
2078 <<playername<<std::endl;
2079 SendAccessDenied(m_con, peer_id, L"Invalid password");
2083 // Add player to auth manager
2084 if(m_authmanager.exists(playername) == false)
2086 derr_server<<DTIME<<"Server: adding player "<<playername
2087 <<" to auth manager"<<std::endl;
2088 m_authmanager.add(playername);
2089 m_authmanager.setPassword(playername, checkpwd);
2090 m_authmanager.setPrivs(playername,
2091 stringToPrivs(g_settings.get("default_privs")));
2092 m_authmanager.save();
2096 Player *player = emergePlayer(playername, password, peer_id);
2100 // DEBUG: Test serialization
2101 std::ostringstream test_os;
2102 player->serialize(test_os);
2103 dstream<<"Player serialization test: \""<<test_os.str()
2105 std::istringstream test_is(test_os.str());
2106 player->deSerialize(test_is);
2109 // If failed, cancel
2112 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2113 <<": failed to emerge player"<<std::endl;
2118 // If a client is already connected to the player, cancel
2119 if(player->peer_id != 0)
2121 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2122 <<" tried to connect to "
2123 "an already connected player (peer_id="
2124 <<player->peer_id<<")"<<std::endl;
2127 // Set client of player
2128 player->peer_id = peer_id;
2131 // Check if player doesn't exist
2133 throw con::InvalidIncomingDataException
2134 ("Server::ProcessData(): INIT: Player doesn't exist");
2136 /*// update name if it was supplied
2137 if(datasize >= 20+3)
2140 player->updateName((const char*)&data[3]);
2144 Answer with a TOCLIENT_INIT
2147 SharedBuffer<u8> reply(2+1+6+8);
2148 writeU16(&reply[0], TOCLIENT_INIT);
2149 writeU8(&reply[2], deployed);
2150 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2151 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2154 m_con.Send(peer_id, 0, reply, true);
2158 Send complete position information
2160 SendMovePlayer(player);
2165 if(command == TOSERVER_INIT2)
2167 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2168 <<peer->id<<std::endl;
2171 getClient(peer->id)->serialization_version
2172 = getClient(peer->id)->pending_serialization_version;
2175 Send some initialization data
2178 // Send player info to all players
2181 // Send inventory to player
2182 UpdateCrafting(peer->id);
2183 SendInventory(peer->id);
2185 // Send player items to all players
2190 Player *player = m_env.getPlayer(peer_id);
2191 SendPlayerHP(player);
2196 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2197 m_env.getTimeOfDay());
2198 m_con.Send(peer->id, 0, data, true);
2201 // Send information about server to player in chat
2202 SendChatMessage(peer_id, getStatusString());
2204 // Send information about joining in chat
2206 std::wstring name = L"unknown";
2207 Player *player = m_env.getPlayer(peer_id);
2209 name = narrow_to_wide(player->getName());
2211 std::wstring message;
2214 message += L" joined game";
2215 BroadcastChatMessage(message);
2218 // Warnings about protocol version can be issued here
2219 /*if(getClient(peer->id)->net_proto_version == 0)
2221 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2227 if(peer_ser_ver == SER_FMT_VER_INVALID)
2229 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2230 " serialization format invalid or not initialized."
2231 " Skipping incoming command="<<command<<std::endl;
2235 Player *player = m_env.getPlayer(peer_id);
2238 derr_server<<"Server::ProcessData(): Cancelling: "
2239 "No player for peer_id="<<peer_id
2243 if(command == TOSERVER_PLAYERPOS)
2245 if(datasize < 2+12+12+4+4)
2249 v3s32 ps = readV3S32(&data[start+2]);
2250 v3s32 ss = readV3S32(&data[start+2+12]);
2251 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2252 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2253 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2254 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2255 pitch = wrapDegrees(pitch);
2256 yaw = wrapDegrees(yaw);
2257 player->setPosition(position);
2258 player->setSpeed(speed);
2259 player->setPitch(pitch);
2260 player->setYaw(yaw);
2262 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2263 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2264 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2266 else if(command == TOSERVER_GOTBLOCKS)
2279 u16 count = data[2];
2280 for(u16 i=0; i<count; i++)
2282 if((s16)datasize < 2+1+(i+1)*6)
2283 throw con::InvalidIncomingDataException
2284 ("GOTBLOCKS length is too short");
2285 v3s16 p = readV3S16(&data[2+1+i*6]);
2286 /*dstream<<"Server: GOTBLOCKS ("
2287 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2288 RemoteClient *client = getClient(peer_id);
2289 client->GotBlock(p);
2292 else if(command == TOSERVER_DELETEDBLOCKS)
2305 u16 count = data[2];
2306 for(u16 i=0; i<count; i++)
2308 if((s16)datasize < 2+1+(i+1)*6)
2309 throw con::InvalidIncomingDataException
2310 ("DELETEDBLOCKS length is too short");
2311 v3s16 p = readV3S16(&data[2+1+i*6]);
2312 /*dstream<<"Server: DELETEDBLOCKS ("
2313 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2314 RemoteClient *client = getClient(peer_id);
2315 client->SetBlockNotSent(p);
2318 else if(command == TOSERVER_CLICK_OBJECT)
2323 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2328 [2] u8 button (0=left, 1=right)
2333 u8 button = readU8(&data[2]);
2335 p.X = readS16(&data[3]);
2336 p.Y = readS16(&data[5]);
2337 p.Z = readS16(&data[7]);
2338 s16 id = readS16(&data[9]);
2339 //u16 item_i = readU16(&data[11]);
2341 MapBlock *block = NULL;
2344 block = m_env.getMap().getBlockNoCreate(p);
2346 catch(InvalidPositionException &e)
2348 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2352 MapBlockObject *obj = block->getObject(id);
2356 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2360 //TODO: Check that object is reasonably close
2365 InventoryList *ilist = player->inventory.getList("main");
2366 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2369 // Skip if inventory has no free space
2370 if(ilist->getUsedSlots() == ilist->getSize())
2372 dout_server<<"Player inventory has no free space"<<std::endl;
2377 Create the inventory item
2379 InventoryItem *item = NULL;
2380 // If it is an item-object, take the item from it
2381 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2383 item = ((ItemObject*)obj)->createInventoryItem();
2385 // Else create an item of the object
2388 item = new MapBlockObjectItem
2389 (obj->getInventoryString());
2392 // Add to inventory and send inventory
2393 ilist->addItem(item);
2394 UpdateCrafting(player->peer_id);
2395 SendInventory(player->peer_id);
2398 // Remove from block
2399 block->removeObject(id);
2402 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2407 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2413 [2] u8 button (0=left, 1=right)
2417 u8 button = readU8(&data[2]);
2418 u16 id = readS16(&data[3]);
2419 u16 item_i = readU16(&data[11]);
2421 ServerActiveObject *obj = m_env.getActiveObject(id);
2425 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2430 // Skip if object has been removed
2434 //TODO: Check that object is reasonably close
2436 // Left click, pick object up (usually)
2440 Try creating inventory item
2442 InventoryItem *item = obj->createPickedUpItem();
2446 InventoryList *ilist = player->inventory.getList("main");
2449 if(g_settings.getBool("creative_mode") == false)
2451 // Skip if inventory has no free space
2452 if(ilist->roomForItem(item) == false)
2454 dout_server<<"Player inventory has no free space"<<std::endl;
2458 // Add to inventory and send inventory
2459 ilist->addItem(item);
2460 UpdateCrafting(player->peer_id);
2461 SendInventory(player->peer_id);
2464 // Remove object from environment
2465 obj->m_removed = true;
2471 Item cannot be picked up. Punch it instead.
2474 ToolItem *titem = NULL;
2475 std::string toolname = "";
2477 InventoryList *mlist = player->inventory.getList("main");
2480 InventoryItem *item = mlist->getItem(item_i);
2481 if(item && (std::string)item->getName() == "ToolItem")
2483 titem = (ToolItem*)item;
2484 toolname = titem->getToolName();
2488 v3f playerpos = player->getPosition();
2489 v3f objpos = obj->getBasePosition();
2490 v3f dir = (objpos - playerpos).normalize();
2492 u16 wear = obj->punch(toolname, dir);
2496 bool weared_out = titem->addWear(wear);
2498 mlist->deleteItem(item_i);
2499 SendInventory(player->peer_id);
2503 // Right click, do something with object
2506 // Track hp changes super-crappily
2507 u16 oldhp = player->hp;
2510 obj->rightClick(player);
2513 if(player->hp != oldhp)
2515 SendPlayerHP(player);
2519 else if(command == TOSERVER_GROUND_ACTION)
2527 [3] v3s16 nodepos_undersurface
2528 [9] v3s16 nodepos_abovesurface
2533 2: stop digging (all parameters ignored)
2534 3: digging completed
2536 u8 action = readU8(&data[2]);
2538 p_under.X = readS16(&data[3]);
2539 p_under.Y = readS16(&data[5]);
2540 p_under.Z = readS16(&data[7]);
2542 p_over.X = readS16(&data[9]);
2543 p_over.Y = readS16(&data[11]);
2544 p_over.Z = readS16(&data[13]);
2545 u16 item_i = readU16(&data[15]);
2547 //TODO: Check that target is reasonably close
2555 NOTE: This can be used in the future to check if
2556 somebody is cheating, by checking the timing.
2563 else if(action == 2)
2566 RemoteClient *client = getClient(peer->id);
2567 JMutexAutoLock digmutex(client->m_dig_mutex);
2568 client->m_dig_tool_item = -1;
2573 3: Digging completed
2575 else if(action == 3)
2577 // Mandatory parameter; actually used for nothing
2578 core::map<v3s16, MapBlock*> modified_blocks;
2580 content_t material = CONTENT_IGNORE;
2581 u8 mineral = MINERAL_NONE;
2583 bool cannot_remove_node = false;
2587 MapNode n = m_env.getMap().getNode(p_under);
2589 mineral = n.getMineral();
2590 // Get material at position
2591 material = n.getContent();
2592 // If not yet cancelled
2593 if(cannot_remove_node == false)
2595 // If it's not diggable, do nothing
2596 if(content_diggable(material) == false)
2598 derr_server<<"Server: Not finishing digging: "
2599 <<"Node not diggable"
2601 cannot_remove_node = true;
2604 // If not yet cancelled
2605 if(cannot_remove_node == false)
2607 // Get node metadata
2608 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2609 if(meta && meta->nodeRemovalDisabled() == true)
2611 derr_server<<"Server: Not finishing digging: "
2612 <<"Node metadata disables removal"
2614 cannot_remove_node = true;
2618 catch(InvalidPositionException &e)
2620 derr_server<<"Server: Not finishing digging: Node not found."
2621 <<" Adding block to emerge queue."
2623 m_emerge_queue.addBlock(peer_id,
2624 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2625 cannot_remove_node = true;
2628 // Make sure the player is allowed to do it
2629 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2631 dstream<<"Player "<<player->getName()<<" cannot remove node"
2632 <<" because privileges are "<<getPlayerPrivs(player)
2634 cannot_remove_node = true;
2638 If node can't be removed, set block to be re-sent to
2641 if(cannot_remove_node)
2643 derr_server<<"Server: Not finishing digging."<<std::endl;
2645 // Client probably has wrong data.
2646 // Set block not sent, so that client will get
2648 dstream<<"Client "<<peer_id<<" tried to dig "
2649 <<"node; but node cannot be removed."
2650 <<" setting MapBlock not sent."<<std::endl;
2651 RemoteClient *client = getClient(peer_id);
2652 v3s16 blockpos = getNodeBlockPos(p_under);
2653 client->SetBlockNotSent(blockpos);
2659 Send the removal to all close-by players.
2660 - If other player is close, send REMOVENODE
2661 - Otherwise set blocks not sent
2663 core::list<u16> far_players;
2664 sendRemoveNode(p_under, peer_id, &far_players, 30);
2667 Update and send inventory
2670 if(g_settings.getBool("creative_mode") == false)
2675 InventoryList *mlist = player->inventory.getList("main");
2678 InventoryItem *item = mlist->getItem(item_i);
2679 if(item && (std::string)item->getName() == "ToolItem")
2681 ToolItem *titem = (ToolItem*)item;
2682 std::string toolname = titem->getToolName();
2684 // Get digging properties for material and tool
2685 DiggingProperties prop =
2686 getDiggingProperties(material, toolname);
2688 if(prop.diggable == false)
2690 derr_server<<"Server: WARNING: Player digged"
2691 <<" with impossible material + tool"
2692 <<" combination"<<std::endl;
2695 bool weared_out = titem->addWear(prop.wear);
2699 mlist->deleteItem(item_i);
2705 Add dug item to inventory
2708 InventoryItem *item = NULL;
2710 if(mineral != MINERAL_NONE)
2711 item = getDiggedMineralItem(mineral);
2716 std::string &dug_s = content_features(material).dug_item;
2719 std::istringstream is(dug_s, std::ios::binary);
2720 item = InventoryItem::deSerialize(is);
2726 // Add a item to inventory
2727 player->inventory.addItem("main", item);
2730 UpdateCrafting(player->peer_id);
2731 SendInventory(player->peer_id);
2737 (this takes some time so it is done after the quick stuff)
2740 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2742 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2745 Set blocks not sent to far players
2747 for(core::list<u16>::Iterator
2748 i = far_players.begin();
2749 i != far_players.end(); i++)
2752 RemoteClient *client = getClient(peer_id);
2755 client->SetBlocksNotSent(modified_blocks);
2762 else if(action == 1)
2765 InventoryList *ilist = player->inventory.getList("main");
2770 InventoryItem *item = ilist->getItem(item_i);
2772 // If there is no item, it is not possible to add it anywhere
2777 Handle material items
2779 if(std::string("MaterialItem") == item->getName())
2782 // Don't add a node if this is not a free space
2783 MapNode n2 = m_env.getMap().getNode(p_over);
2784 bool no_enough_privs =
2785 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2787 dstream<<"Player "<<player->getName()<<" cannot add node"
2788 <<" because privileges are "<<getPlayerPrivs(player)
2791 if(content_features(n2).buildable_to == false
2794 // Client probably has wrong data.
2795 // Set block not sent, so that client will get
2797 dstream<<"Client "<<peer_id<<" tried to place"
2798 <<" node in invalid position; setting"
2799 <<" MapBlock not sent."<<std::endl;
2800 RemoteClient *client = getClient(peer_id);
2801 v3s16 blockpos = getNodeBlockPos(p_over);
2802 client->SetBlockNotSent(blockpos);
2806 catch(InvalidPositionException &e)
2808 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2809 <<" Adding block to emerge queue."
2811 m_emerge_queue.addBlock(peer_id,
2812 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2816 // Reset build time counter
2817 getClient(peer->id)->m_time_from_building = 0.0;
2820 MaterialItem *mitem = (MaterialItem*)item;
2822 n.setContent(mitem->getMaterial());
2824 // Calculate direction for wall mounted stuff
2825 if(content_features(n).wall_mounted)
2826 n.param2 = packDir(p_under - p_over);
2828 // Calculate the direction for furnaces and chests and stuff
2829 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2831 v3f playerpos = player->getPosition();
2832 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2833 blockpos = blockpos.normalize();
2835 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2849 Send to all close-by players
2851 core::list<u16> far_players;
2852 sendAddNode(p_over, n, 0, &far_players, 30);
2857 InventoryList *ilist = player->inventory.getList("main");
2858 if(g_settings.getBool("creative_mode") == false && ilist)
2860 // Remove from inventory and send inventory
2861 if(mitem->getCount() == 1)
2862 ilist->deleteItem(item_i);
2866 UpdateCrafting(peer_id);
2867 SendInventory(peer_id);
2873 This takes some time so it is done after the quick stuff
2875 core::map<v3s16, MapBlock*> modified_blocks;
2877 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2879 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2882 Set blocks not sent to far players
2884 for(core::list<u16>::Iterator
2885 i = far_players.begin();
2886 i != far_players.end(); i++)
2889 RemoteClient *client = getClient(peer_id);
2892 client->SetBlocksNotSent(modified_blocks);
2896 Calculate special events
2899 /*if(n.d == CONTENT_MESE)
2902 for(s16 z=-1; z<=1; z++)
2903 for(s16 y=-1; y<=1; y++)
2904 for(s16 x=-1; x<=1; x++)
2911 Place other item (not a block)
2915 v3s16 blockpos = getNodeBlockPos(p_over);
2918 Check that the block is loaded so that the item
2919 can properly be added to the static list too
2921 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2924 derr_server<<"Error while placing object: "
2925 "block not found"<<std::endl;
2930 If in creative mode, item dropping is disabled unless
2931 player has build privileges
2933 if(g_settings.getBool("creative_mode") &&
2934 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2936 derr_server<<"Not allowing player to drop item: "
2937 "creative mode and no build privs"<<std::endl;
2941 dout_server<<"Placing a miscellaneous item on map"
2944 // Calculate a position for it
2945 v3f pos = intToFloat(p_over, BS);
2947 pos.Y -= BS*0.25; // let it drop a bit
2949 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2950 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2955 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2959 derr_server<<"WARNING: item resulted in NULL object, "
2960 <<"not placing onto map"
2965 // Add the object to the environment
2966 m_env.addActiveObject(obj);
2968 dout_server<<"Placed object"<<std::endl;
2970 if(g_settings.getBool("creative_mode") == false)
2972 // Delete the right amount of items from the slot
2973 u16 dropcount = item->getDropCount();
2975 // Delete item if all gone
2976 if(item->getCount() <= dropcount)
2978 if(item->getCount() < dropcount)
2979 dstream<<"WARNING: Server: dropped more items"
2980 <<" than the slot contains"<<std::endl;
2982 InventoryList *ilist = player->inventory.getList("main");
2984 // Remove from inventory and send inventory
2985 ilist->deleteItem(item_i);
2987 // Else decrement it
2989 item->remove(dropcount);
2992 UpdateCrafting(peer_id);
2993 SendInventory(peer_id);
3001 Catch invalid actions
3005 derr_server<<"WARNING: Server: Invalid action "
3006 <<action<<std::endl;
3010 else if(command == TOSERVER_RELEASE)
3019 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3022 else if(command == TOSERVER_SIGNTEXT)
3024 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3033 std::string datastring((char*)&data[2], datasize-2);
3034 std::istringstream is(datastring, std::ios_base::binary);
3037 is.read((char*)buf, 6);
3038 v3s16 blockpos = readV3S16(buf);
3039 is.read((char*)buf, 2);
3040 s16 id = readS16(buf);
3041 is.read((char*)buf, 2);
3042 u16 textlen = readU16(buf);
3044 for(u16 i=0; i<textlen; i++)
3046 is.read((char*)buf, 1);
3047 text += (char)buf[0];
3050 MapBlock *block = NULL;
3053 block = m_env.getMap().getBlockNoCreate(blockpos);
3055 catch(InvalidPositionException &e)
3057 derr_server<<"Error while setting sign text: "
3058 "block not found"<<std::endl;
3062 MapBlockObject *obj = block->getObject(id);
3065 derr_server<<"Error while setting sign text: "
3066 "object not found"<<std::endl;
3070 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3072 derr_server<<"Error while setting sign text: "
3073 "object is not a sign"<<std::endl;
3077 ((SignObject*)obj)->setText(text);
3079 obj->getBlock()->setChangedFlag();
3081 else if(command == TOSERVER_SIGNNODETEXT)
3083 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3091 std::string datastring((char*)&data[2], datasize-2);
3092 std::istringstream is(datastring, std::ios_base::binary);
3095 is.read((char*)buf, 6);
3096 v3s16 p = readV3S16(buf);
3097 is.read((char*)buf, 2);
3098 u16 textlen = readU16(buf);
3100 for(u16 i=0; i<textlen; i++)
3102 is.read((char*)buf, 1);
3103 text += (char)buf[0];
3106 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3109 if(meta->typeId() != CONTENT_SIGN_WALL)
3111 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3112 signmeta->setText(text);
3114 v3s16 blockpos = getNodeBlockPos(p);
3115 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3118 block->setChangedFlag();
3121 for(core::map<u16, RemoteClient*>::Iterator
3122 i = m_clients.getIterator();
3123 i.atEnd()==false; i++)
3125 RemoteClient *client = i.getNode()->getValue();
3126 client->SetBlockNotSent(blockpos);
3129 else if(command == TOSERVER_INVENTORY_ACTION)
3131 /*// Ignore inventory changes if in creative mode
3132 if(g_settings.getBool("creative_mode") == true)
3134 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3138 // Strip command and create a stream
3139 std::string datastring((char*)&data[2], datasize-2);
3140 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3141 std::istringstream is(datastring, std::ios_base::binary);
3143 InventoryAction *a = InventoryAction::deSerialize(is);
3148 c.current_player = player;
3151 Handle craftresult specially if not in creative mode
3153 bool disable_action = false;
3154 if(a->getType() == IACTION_MOVE
3155 && g_settings.getBool("creative_mode") == false)
3157 IMoveAction *ma = (IMoveAction*)a;
3158 if(ma->to_inv == "current_player" &&
3159 ma->from_inv == "current_player")
3161 InventoryList *rlist = player->inventory.getList("craftresult");
3163 InventoryList *clist = player->inventory.getList("craft");
3165 InventoryList *mlist = player->inventory.getList("main");
3168 Craftresult is no longer preview if something
3171 if(ma->to_list == "craftresult"
3172 && ma->from_list != "craftresult")
3174 // If it currently is a preview, remove
3176 if(player->craftresult_is_preview)
3178 rlist->deleteItem(0);
3180 player->craftresult_is_preview = false;
3183 Crafting takes place if this condition is true.
3185 if(player->craftresult_is_preview &&
3186 ma->from_list == "craftresult")
3188 player->craftresult_is_preview = false;
3189 clist->decrementMaterials(1);
3192 If the craftresult is placed on itself, move it to
3193 main inventory instead of doing the action
3195 if(ma->to_list == "craftresult"
3196 && ma->from_list == "craftresult")
3198 disable_action = true;
3200 InventoryItem *item1 = rlist->changeItem(0, NULL);
3201 mlist->addItem(item1);
3204 // Disallow moving items if not allowed to build
3205 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3209 if(disable_action == false)
3211 // Feed action to player inventory
3219 UpdateCrafting(player->peer_id);
3220 SendInventory(player->peer_id);
3225 dstream<<"TOSERVER_INVENTORY_ACTION: "
3226 <<"InventoryAction::deSerialize() returned NULL"
3230 else if(command == TOSERVER_CHAT_MESSAGE)
3238 std::string datastring((char*)&data[2], datasize-2);
3239 std::istringstream is(datastring, std::ios_base::binary);
3242 is.read((char*)buf, 2);
3243 u16 len = readU16(buf);
3245 std::wstring message;
3246 for(u16 i=0; i<len; i++)
3248 is.read((char*)buf, 2);
3249 message += (wchar_t)readU16(buf);
3252 // Get player name of this client
3253 std::wstring name = narrow_to_wide(player->getName());
3255 // Line to send to players
3257 // Whether to send to the player that sent the line
3258 bool send_to_sender = false;
3259 // Whether to send to other players
3260 bool send_to_others = false;
3262 // Local player gets all privileges regardless of
3263 // what's set on their account.
3264 u64 privs = getPlayerPrivs(player);
3267 if(message[0] == L'/')
3269 size_t strip_size = 1;
3270 if (message[1] == L'#') // support old-style commans
3272 message = message.substr(strip_size);
3274 WStrfnd f1(message);
3275 f1.next(L" "); // Skip over /#whatever
3276 std::wstring paramstring = f1.next(L"");
3278 ServerCommandContext *ctx = new ServerCommandContext(
3279 str_split(message, L' '),
3286 std::wstring reply(processServerCommand(ctx));
3287 send_to_sender = ctx->flags & SEND_TO_SENDER;
3288 send_to_others = ctx->flags & SEND_TO_OTHERS;
3290 if (ctx->flags & SEND_NO_PREFIX)
3293 line += L"Server: " + reply;
3300 if(privs & PRIV_SHOUT)
3306 send_to_others = true;
3310 line += L"Server: You are not allowed to shout";
3311 send_to_sender = true;
3317 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3320 Send the message to clients
3322 for(core::map<u16, RemoteClient*>::Iterator
3323 i = m_clients.getIterator();
3324 i.atEnd() == false; i++)
3326 // Get client and check that it is valid
3327 RemoteClient *client = i.getNode()->getValue();
3328 assert(client->peer_id == i.getNode()->getKey());
3329 if(client->serialization_version == SER_FMT_VER_INVALID)
3333 bool sender_selected = (peer_id == client->peer_id);
3334 if(sender_selected == true && send_to_sender == false)
3336 if(sender_selected == false && send_to_others == false)
3339 SendChatMessage(client->peer_id, line);
3343 else if(command == TOSERVER_DAMAGE)
3345 if(g_settings.getBool("enable_damage"))
3347 std::string datastring((char*)&data[2], datasize-2);
3348 std::istringstream is(datastring, std::ios_base::binary);
3349 u8 damage = readU8(is);
3350 if(player->hp > damage)
3352 player->hp -= damage;
3358 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3361 v3f pos = findSpawnPos(m_env.getServerMap());
3362 player->setPosition(pos);
3364 SendMovePlayer(player);
3365 SendPlayerHP(player);
3367 //TODO: Throw items around
3371 SendPlayerHP(player);
3373 else if(command == TOSERVER_PASSWORD)
3376 [0] u16 TOSERVER_PASSWORD
3377 [2] u8[28] old password
3378 [30] u8[28] new password
3381 if(datasize != 2+PASSWORD_SIZE*2)
3383 /*char password[PASSWORD_SIZE];
3384 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3385 password[i] = data[2+i];
3386 password[PASSWORD_SIZE-1] = 0;*/
3388 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3396 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3398 char c = data[2+PASSWORD_SIZE+i];
3404 dstream<<"Server: Client requests a password change from "
3405 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3407 std::string playername = player->getName();
3409 if(m_authmanager.exists(playername) == false)
3411 dstream<<"Server: playername not found in authmanager"<<std::endl;
3412 // Wrong old password supplied!!
3413 SendChatMessage(peer_id, L"playername not found in authmanager");
3417 std::string checkpwd = m_authmanager.getPassword(playername);
3419 if(oldpwd != checkpwd)
3421 dstream<<"Server: invalid old password"<<std::endl;
3422 // Wrong old password supplied!!
3423 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3427 m_authmanager.setPassword(playername, newpwd);
3429 dstream<<"Server: password change successful for "<<playername
3431 SendChatMessage(peer_id, L"Password change successful");
3433 else if (command == TOSERVER_PLAYERITEM)
3438 u16 item = readU16(&data[2]);
3439 player->wieldItem(item);
3440 SendWieldedItem(player);
3444 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3445 "unknown command "<<command<<std::endl;
3449 catch(SendFailedException &e)
3451 derr_server<<"Server::ProcessData(): SendFailedException: "
3457 void Server::onMapEditEvent(MapEditEvent *event)
3459 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3460 if(m_ignore_map_edit_events)
3462 MapEditEvent *e = event->clone();
3463 m_unsent_map_edit_queue.push_back(e);
3466 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3468 if(id == "current_player")
3470 assert(c->current_player);
3471 return &(c->current_player->inventory);
3475 std::string id0 = fn.next(":");
3477 if(id0 == "nodemeta")
3480 p.X = stoi(fn.next(","));
3481 p.Y = stoi(fn.next(","));
3482 p.Z = stoi(fn.next(","));
3483 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3485 return meta->getInventory();
3486 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3487 <<"no metadata found"<<std::endl;
3491 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3494 void Server::inventoryModified(InventoryContext *c, std::string id)
3496 if(id == "current_player")
3498 assert(c->current_player);
3500 UpdateCrafting(c->current_player->peer_id);
3501 SendInventory(c->current_player->peer_id);
3506 std::string id0 = fn.next(":");
3508 if(id0 == "nodemeta")
3511 p.X = stoi(fn.next(","));
3512 p.Y = stoi(fn.next(","));
3513 p.Z = stoi(fn.next(","));
3514 v3s16 blockpos = getNodeBlockPos(p);
3516 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3518 meta->inventoryModified();
3520 for(core::map<u16, RemoteClient*>::Iterator
3521 i = m_clients.getIterator();
3522 i.atEnd()==false; i++)
3524 RemoteClient *client = i.getNode()->getValue();
3525 client->SetBlockNotSent(blockpos);
3531 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3534 core::list<PlayerInfo> Server::getPlayerInfo()
3536 DSTACK(__FUNCTION_NAME);
3537 JMutexAutoLock envlock(m_env_mutex);
3538 JMutexAutoLock conlock(m_con_mutex);
3540 core::list<PlayerInfo> list;
3542 core::list<Player*> players = m_env.getPlayers();
3544 core::list<Player*>::Iterator i;
3545 for(i = players.begin();
3546 i != players.end(); i++)
3550 Player *player = *i;
3553 con::Peer *peer = m_con.GetPeer(player->peer_id);
3554 // Copy info from peer to info struct
3556 info.address = peer->address;
3557 info.avg_rtt = peer->avg_rtt;
3559 catch(con::PeerNotFoundException &e)
3561 // Set dummy peer info
3563 info.address = Address(0,0,0,0,0);
3567 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3568 info.position = player->getPosition();
3570 list.push_back(info);
3577 void Server::peerAdded(con::Peer *peer)
3579 DSTACK(__FUNCTION_NAME);
3580 dout_server<<"Server::peerAdded(): peer->id="
3581 <<peer->id<<std::endl;
3584 c.type = PEER_ADDED;
3585 c.peer_id = peer->id;
3587 m_peer_change_queue.push_back(c);
3590 void Server::deletingPeer(con::Peer *peer, bool timeout)
3592 DSTACK(__FUNCTION_NAME);
3593 dout_server<<"Server::deletingPeer(): peer->id="
3594 <<peer->id<<", timeout="<<timeout<<std::endl;
3597 c.type = PEER_REMOVED;
3598 c.peer_id = peer->id;
3599 c.timeout = timeout;
3600 m_peer_change_queue.push_back(c);
3607 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3609 DSTACK(__FUNCTION_NAME);
3610 std::ostringstream os(std::ios_base::binary);
3612 writeU16(os, TOCLIENT_HP);
3616 std::string s = os.str();
3617 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3619 con.Send(peer_id, 0, data, true);
3622 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3623 const std::wstring &reason)
3625 DSTACK(__FUNCTION_NAME);
3626 std::ostringstream os(std::ios_base::binary);
3628 writeU16(os, TOCLIENT_ACCESS_DENIED);
3629 os<<serializeWideString(reason);
3632 std::string s = os.str();
3633 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3635 con.Send(peer_id, 0, data, true);
3639 Non-static send methods
3642 void Server::SendObjectData(float dtime)
3644 DSTACK(__FUNCTION_NAME);
3646 core::map<v3s16, bool> stepped_blocks;
3648 for(core::map<u16, RemoteClient*>::Iterator
3649 i = m_clients.getIterator();
3650 i.atEnd() == false; i++)
3652 u16 peer_id = i.getNode()->getKey();
3653 RemoteClient *client = i.getNode()->getValue();
3654 assert(client->peer_id == peer_id);
3656 if(client->serialization_version == SER_FMT_VER_INVALID)
3659 client->SendObjectData(this, dtime, stepped_blocks);
3663 void Server::SendPlayerInfos()
3665 DSTACK(__FUNCTION_NAME);
3667 //JMutexAutoLock envlock(m_env_mutex);
3669 // Get connected players
3670 core::list<Player*> players = m_env.getPlayers(true);
3672 u32 player_count = players.getSize();
3673 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3675 SharedBuffer<u8> data(datasize);
3676 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3679 core::list<Player*>::Iterator i;
3680 for(i = players.begin();
3681 i != players.end(); i++)
3683 Player *player = *i;
3685 /*dstream<<"Server sending player info for player with "
3686 "peer_id="<<player->peer_id<<std::endl;*/
3688 writeU16(&data[start], player->peer_id);
3689 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3690 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3691 start += 2+PLAYERNAME_SIZE;
3694 //JMutexAutoLock conlock(m_con_mutex);
3697 m_con.SendToAll(0, data, true);
3700 void Server::SendInventory(u16 peer_id)
3702 DSTACK(__FUNCTION_NAME);
3704 Player* player = m_env.getPlayer(peer_id);
3711 std::ostringstream os;
3712 //os.imbue(std::locale("C"));
3714 player->inventory.serialize(os);
3716 std::string s = os.str();
3718 SharedBuffer<u8> data(s.size()+2);
3719 writeU16(&data[0], TOCLIENT_INVENTORY);
3720 memcpy(&data[2], s.c_str(), s.size());
3723 m_con.Send(peer_id, 0, data, true);
3726 std::string getWieldedItemString(const Player *player)
3728 const InventoryItem *item = player->getWieldItem();
3730 return std::string("");
3731 std::ostringstream os(std::ios_base::binary);
3732 item->serialize(os);
3736 void Server::SendWieldedItem(const Player* player)
3738 DSTACK(__FUNCTION_NAME);
3742 std::ostringstream os(std::ios_base::binary);
3744 writeU16(os, TOCLIENT_PLAYERITEM);
3746 writeU16(os, player->peer_id);
3747 os<<serializeString(getWieldedItemString(player));
3750 std::string s = os.str();
3751 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3753 m_con.SendToAll(0, data, true);
3756 void Server::SendPlayerItems()
3758 DSTACK(__FUNCTION_NAME);
3760 std::ostringstream os(std::ios_base::binary);
3761 core::list<Player *> players = m_env.getPlayers(true);
3763 writeU16(os, TOCLIENT_PLAYERITEM);
3764 writeU16(os, players.size());
3765 core::list<Player *>::Iterator i;
3766 for(i = players.begin(); i != players.end(); ++i)
3769 writeU16(os, p->peer_id);
3770 os<<serializeString(getWieldedItemString(p));
3774 std::string s = os.str();
3775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3777 m_con.SendToAll(0, data, true);
3780 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3782 DSTACK(__FUNCTION_NAME);
3784 std::ostringstream os(std::ios_base::binary);
3788 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3789 os.write((char*)buf, 2);
3792 writeU16(buf, message.size());
3793 os.write((char*)buf, 2);
3796 for(u32 i=0; i<message.size(); i++)
3800 os.write((char*)buf, 2);
3804 std::string s = os.str();
3805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3807 m_con.Send(peer_id, 0, data, true);
3810 void Server::BroadcastChatMessage(const std::wstring &message)
3812 for(core::map<u16, RemoteClient*>::Iterator
3813 i = m_clients.getIterator();
3814 i.atEnd() == false; i++)
3816 // Get client and check that it is valid
3817 RemoteClient *client = i.getNode()->getValue();
3818 assert(client->peer_id == i.getNode()->getKey());
3819 if(client->serialization_version == SER_FMT_VER_INVALID)
3822 SendChatMessage(client->peer_id, message);
3826 void Server::SendPlayerHP(Player *player)
3828 SendHP(m_con, player->peer_id, player->hp);
3831 void Server::SendMovePlayer(Player *player)
3833 DSTACK(__FUNCTION_NAME);
3834 std::ostringstream os(std::ios_base::binary);
3836 writeU16(os, TOCLIENT_MOVE_PLAYER);
3837 writeV3F1000(os, player->getPosition());
3838 writeF1000(os, player->getPitch());
3839 writeF1000(os, player->getYaw());
3842 v3f pos = player->getPosition();
3843 f32 pitch = player->getPitch();
3844 f32 yaw = player->getYaw();
3845 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3846 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3853 std::string s = os.str();
3854 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3856 m_con.Send(player->peer_id, 0, data, true);
3859 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3860 core::list<u16> *far_players, float far_d_nodes)
3862 float maxd = far_d_nodes*BS;
3863 v3f p_f = intToFloat(p, BS);
3867 SharedBuffer<u8> reply(replysize);
3868 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3869 writeS16(&reply[2], p.X);
3870 writeS16(&reply[4], p.Y);
3871 writeS16(&reply[6], p.Z);
3873 for(core::map<u16, RemoteClient*>::Iterator
3874 i = m_clients.getIterator();
3875 i.atEnd() == false; i++)
3877 // Get client and check that it is valid
3878 RemoteClient *client = i.getNode()->getValue();
3879 assert(client->peer_id == i.getNode()->getKey());
3880 if(client->serialization_version == SER_FMT_VER_INVALID)
3883 // Don't send if it's the same one
3884 if(client->peer_id == ignore_id)
3890 Player *player = m_env.getPlayer(client->peer_id);
3893 // If player is far away, only set modified blocks not sent
3894 v3f player_pos = player->getPosition();
3895 if(player_pos.getDistanceFrom(p_f) > maxd)
3897 far_players->push_back(client->peer_id);
3904 m_con.Send(client->peer_id, 0, reply, true);
3908 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3909 core::list<u16> *far_players, float far_d_nodes)
3911 float maxd = far_d_nodes*BS;
3912 v3f p_f = intToFloat(p, BS);
3914 for(core::map<u16, RemoteClient*>::Iterator
3915 i = m_clients.getIterator();
3916 i.atEnd() == false; i++)
3918 // Get client and check that it is valid
3919 RemoteClient *client = i.getNode()->getValue();
3920 assert(client->peer_id == i.getNode()->getKey());
3921 if(client->serialization_version == SER_FMT_VER_INVALID)
3924 // Don't send if it's the same one
3925 if(client->peer_id == ignore_id)
3931 Player *player = m_env.getPlayer(client->peer_id);
3934 // If player is far away, only set modified blocks not sent
3935 v3f player_pos = player->getPosition();
3936 if(player_pos.getDistanceFrom(p_f) > maxd)
3938 far_players->push_back(client->peer_id);
3945 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3946 SharedBuffer<u8> reply(replysize);
3947 writeU16(&reply[0], TOCLIENT_ADDNODE);
3948 writeS16(&reply[2], p.X);
3949 writeS16(&reply[4], p.Y);
3950 writeS16(&reply[6], p.Z);
3951 n.serialize(&reply[8], client->serialization_version);
3954 m_con.Send(client->peer_id, 0, reply, true);
3958 void Server::setBlockNotSent(v3s16 p)
3960 for(core::map<u16, RemoteClient*>::Iterator
3961 i = m_clients.getIterator();
3962 i.atEnd()==false; i++)
3964 RemoteClient *client = i.getNode()->getValue();
3965 client->SetBlockNotSent(p);
3969 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3971 DSTACK(__FUNCTION_NAME);
3973 v3s16 p = block->getPos();
3977 bool completely_air = true;
3978 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3979 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3980 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3982 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3984 completely_air = false;
3985 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3990 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3992 dstream<<"[completely air] ";
3997 Create a packet with the block in the right format
4000 std::ostringstream os(std::ios_base::binary);
4001 block->serialize(os, ver);
4002 std::string s = os.str();
4003 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4005 u32 replysize = 8 + blockdata.getSize();
4006 SharedBuffer<u8> reply(replysize);
4007 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4008 writeS16(&reply[2], p.X);
4009 writeS16(&reply[4], p.Y);
4010 writeS16(&reply[6], p.Z);
4011 memcpy(&reply[8], *blockdata, blockdata.getSize());
4013 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4014 <<": \tpacket size: "<<replysize<<std::endl;*/
4019 m_con.Send(peer_id, 1, reply, true);
4022 void Server::SendBlocks(float dtime)
4024 DSTACK(__FUNCTION_NAME);
4026 JMutexAutoLock envlock(m_env_mutex);
4027 JMutexAutoLock conlock(m_con_mutex);
4029 //TimeTaker timer("Server::SendBlocks");
4031 core::array<PrioritySortedBlockTransfer> queue;
4033 s32 total_sending = 0;
4036 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
4038 for(core::map<u16, RemoteClient*>::Iterator
4039 i = m_clients.getIterator();
4040 i.atEnd() == false; i++)
4042 RemoteClient *client = i.getNode()->getValue();
4043 assert(client->peer_id == i.getNode()->getKey());
4045 total_sending += client->SendingCount();
4047 if(client->serialization_version == SER_FMT_VER_INVALID)
4050 client->GetNextBlocks(this, dtime, queue);
4055 // Lowest priority number comes first.
4056 // Lowest is most important.
4059 for(u32 i=0; i<queue.size(); i++)
4061 //TODO: Calculate limit dynamically
4062 if(total_sending >= g_settings.getS32
4063 ("max_simultaneous_block_sends_server_total"))
4066 PrioritySortedBlockTransfer q = queue[i];
4068 MapBlock *block = NULL;
4071 block = m_env.getMap().getBlockNoCreate(q.pos);
4073 catch(InvalidPositionException &e)
4078 RemoteClient *client = getClient(q.peer_id);
4080 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4082 client->SentBlock(q.pos);
4092 void Server::UpdateCrafting(u16 peer_id)
4094 DSTACK(__FUNCTION_NAME);
4096 Player* player = m_env.getPlayer(peer_id);
4100 Calculate crafting stuff
4102 if(g_settings.getBool("creative_mode") == false)
4104 InventoryList *clist = player->inventory.getList("craft");
4105 InventoryList *rlist = player->inventory.getList("craftresult");
4107 if(rlist->getUsedSlots() == 0)
4108 player->craftresult_is_preview = true;
4110 if(rlist && player->craftresult_is_preview)
4112 rlist->clearItems();
4114 if(clist && rlist && player->craftresult_is_preview)
4116 InventoryItem *items[9];
4117 for(u16 i=0; i<9; i++)
4119 items[i] = clist->getItem(i);
4122 // Get result of crafting grid
4123 InventoryItem *result = craft_get_result(items);
4125 rlist->addItem(result);
4128 } // if creative_mode == false
4131 RemoteClient* Server::getClient(u16 peer_id)
4133 DSTACK(__FUNCTION_NAME);
4134 //JMutexAutoLock lock(m_con_mutex);
4135 core::map<u16, RemoteClient*>::Node *n;
4136 n = m_clients.find(peer_id);
4137 // A client should exist for all peers
4139 return n->getValue();
4142 std::wstring Server::getStatusString()
4144 std::wostringstream os(std::ios_base::binary);
4147 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4149 os<<L", uptime="<<m_uptime.get();
4150 // Information about clients
4152 for(core::map<u16, RemoteClient*>::Iterator
4153 i = m_clients.getIterator();
4154 i.atEnd() == false; i++)
4156 // Get client and check that it is valid
4157 RemoteClient *client = i.getNode()->getValue();
4158 assert(client->peer_id == i.getNode()->getKey());
4159 if(client->serialization_version == SER_FMT_VER_INVALID)
4162 Player *player = m_env.getPlayer(client->peer_id);
4163 // Get name of player
4164 std::wstring name = L"unknown";
4166 name = narrow_to_wide(player->getName());
4167 // Add name to information string
4171 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4172 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4173 if(g_settings.get("motd") != "")
4174 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4178 v3f findSpawnPos(ServerMap &map)
4180 //return v3f(50,50,50)*BS;
4183 s16 groundheight = 0;
4186 nodepos = v2s16(0,0);
4191 // Try to find a good place a few times
4192 for(s32 i=0; i<1000; i++)
4195 // We're going to try to throw the player to this position
4196 nodepos = v2s16(-range + (myrand()%(range*2)),
4197 -range + (myrand()%(range*2)));
4198 v2s16 sectorpos = getNodeSectorPos(nodepos);
4199 // Get sector (NOTE: Don't get because it's slow)
4200 //m_env.getMap().emergeSector(sectorpos);
4201 // Get ground height at point (fallbacks to heightmap function)
4202 groundheight = map.findGroundLevel(nodepos);
4203 // Don't go underwater
4204 if(groundheight < WATER_LEVEL)
4206 //dstream<<"-> Underwater"<<std::endl;
4209 // Don't go to high places
4210 if(groundheight > WATER_LEVEL + 4)
4212 //dstream<<"-> Underwater"<<std::endl;
4216 // Found a good place
4217 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4222 // If no suitable place was not found, go above water at least.
4223 if(groundheight < WATER_LEVEL)
4224 groundheight = WATER_LEVEL;
4226 return intToFloat(v3s16(
4233 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4236 Try to get an existing player
4238 Player *player = m_env.getPlayer(name);
4241 // If player is already connected, cancel
4242 if(player->peer_id != 0)
4244 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4249 player->peer_id = peer_id;
4251 // Reset inventory to creative if in creative mode
4252 if(g_settings.getBool("creative_mode"))
4254 // Warning: double code below
4255 // Backup actual inventory
4256 player->inventory_backup = new Inventory();
4257 *(player->inventory_backup) = player->inventory;
4258 // Set creative inventory
4259 craft_set_creative_inventory(player);
4266 If player with the wanted peer_id already exists, cancel.
4268 if(m_env.getPlayer(peer_id) != NULL)
4270 dstream<<"emergePlayer(): Player with wrong name but same"
4271 " peer_id already exists"<<std::endl;
4279 player = new ServerRemotePlayer();
4280 //player->peer_id = c.peer_id;
4281 //player->peer_id = PEER_ID_INEXISTENT;
4282 player->peer_id = peer_id;
4283 player->updateName(name);
4284 m_authmanager.add(name);
4285 m_authmanager.setPassword(name, password);
4286 m_authmanager.setPrivs(name,
4287 stringToPrivs(g_settings.get("default_privs")));
4293 dstream<<"Server: Finding spawn place for player \""
4294 <<player->getName()<<"\""<<std::endl;
4296 v3f pos = findSpawnPos(m_env.getServerMap());
4298 player->setPosition(pos);
4301 Add player to environment
4304 m_env.addPlayer(player);
4307 Add stuff to inventory
4310 if(g_settings.getBool("creative_mode"))
4312 // Warning: double code above
4313 // Backup actual inventory
4314 player->inventory_backup = new Inventory();
4315 *(player->inventory_backup) = player->inventory;
4316 // Set creative inventory
4317 craft_set_creative_inventory(player);
4319 else if(g_settings.getBool("give_initial_stuff"))
4321 craft_give_initial_stuff(player);
4326 } // create new player
4329 void Server::handlePeerChange(PeerChange &c)
4331 JMutexAutoLock envlock(m_env_mutex);
4332 JMutexAutoLock conlock(m_con_mutex);
4334 if(c.type == PEER_ADDED)
4341 core::map<u16, RemoteClient*>::Node *n;
4342 n = m_clients.find(c.peer_id);
4343 // The client shouldn't already exist
4347 RemoteClient *client = new RemoteClient();
4348 client->peer_id = c.peer_id;
4349 m_clients.insert(client->peer_id, client);
4352 else if(c.type == PEER_REMOVED)
4359 core::map<u16, RemoteClient*>::Node *n;
4360 n = m_clients.find(c.peer_id);
4361 // The client should exist
4365 Mark objects to be not known by the client
4367 RemoteClient *client = n->getValue();
4369 for(core::map<u16, bool>::Iterator
4370 i = client->m_known_objects.getIterator();
4371 i.atEnd()==false; i++)
4374 u16 id = i.getNode()->getKey();
4375 ServerActiveObject* obj = m_env.getActiveObject(id);
4377 if(obj && obj->m_known_by_count > 0)
4378 obj->m_known_by_count--;
4381 // Collect information about leaving in chat
4382 std::wstring message;
4384 std::wstring name = L"unknown";
4385 Player *player = m_env.getPlayer(c.peer_id);
4387 name = narrow_to_wide(player->getName());
4391 message += L" left game";
4393 message += L" (timed out)";
4398 m_env.removePlayer(c.peer_id);
4401 // Set player client disconnected
4403 Player *player = m_env.getPlayer(c.peer_id);
4405 player->peer_id = 0;
4409 delete m_clients[c.peer_id];
4410 m_clients.remove(c.peer_id);
4412 // Send player info to all remaining clients
4415 // Send leave chat message to all remaining clients
4416 BroadcastChatMessage(message);
4425 void Server::handlePeerChanges()
4427 while(m_peer_change_queue.size() > 0)
4429 PeerChange c = m_peer_change_queue.pop_front();
4431 dout_server<<"Server: Handling peer change: "
4432 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4435 handlePeerChange(c);
4439 u64 Server::getPlayerPrivs(Player *player)
4443 std::string playername = player->getName();
4444 // Local player gets all privileges regardless of
4445 // what's set on their account.
4446 if(g_settings.get("name") == playername)
4452 return getPlayerAuthPrivs(playername);
4456 void dedicated_server_loop(Server &server, bool &kill)
4458 DSTACK(__FUNCTION_NAME);
4460 dstream<<DTIME<<std::endl;
4461 dstream<<"========================"<<std::endl;
4462 dstream<<"Running dedicated server"<<std::endl;
4463 dstream<<"========================"<<std::endl;
4466 IntervalLimiter m_profiler_interval;
4470 // This is kind of a hack but can be done like this
4471 // because server.step() is very light
4473 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4478 if(server.getShutdownRequested() || kill)
4480 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4487 float profiler_print_interval =
4488 g_settings.getFloat("profiler_print_interval");
4489 if(profiler_print_interval != 0)
4491 if(m_profiler_interval.step(0.030, profiler_print_interval))
4493 dstream<<"Profiler:"<<std::endl;
4494 g_profiler.print(dstream);
4502 static int counter = 0;
4508 core::list<PlayerInfo> list = server.getPlayerInfo();
4509 core::list<PlayerInfo>::Iterator i;
4510 static u32 sum_old = 0;
4511 u32 sum = PIChecksum(list);
4514 dstream<<DTIME<<"Player info:"<<std::endl;
4515 for(i=list.begin(); i!=list.end(); i++)
4517 i->PrintLine(&dstream);