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"
43 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
47 class MapEditEventIgnorer
50 MapEditEventIgnorer(bool *flag):
59 ~MapEditEventIgnorer()
72 void * ServerThread::Thread()
76 log_register_thread("ServerThread");
78 DSTACK(__FUNCTION_NAME);
80 BEGIN_DEBUG_EXCEPTION_HANDLER
85 //TimeTaker timer("AsyncRunStep() + Receive()");
88 //TimeTaker timer("AsyncRunStep()");
89 m_server->AsyncRunStep();
92 //infostream<<"Running m_server->Receive()"<<std::endl;
95 catch(con::NoIncomingDataException &e)
98 catch(con::PeerNotFoundException &e)
100 infostream<<"Server: PeerNotFoundException"<<std::endl;
104 END_DEBUG_EXCEPTION_HANDLER(errorstream)
109 void * EmergeThread::Thread()
113 log_register_thread("EmergeThread");
115 DSTACK(__FUNCTION_NAME);
117 BEGIN_DEBUG_EXCEPTION_HANDLER
119 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
122 Get block info from queue, emerge them and send them
125 After queue is empty, exit.
129 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
133 SharedPtr<QueuedBlockEmerge> q(qptr);
139 Do not generate over-limit
141 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
142 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
143 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
144 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
145 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
146 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
149 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
151 //TimeTaker timer("block emerge");
154 Try to emerge it from somewhere.
156 If it is only wanted as optional, only loading from disk
161 Check if any peer wants it as non-optional. In that case it
164 Also decrement the emerge queue count in clients.
167 bool only_from_disk = true;
170 core::map<u16, u8>::Iterator i;
171 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
173 //u16 peer_id = i.getNode()->getKey();
176 u8 flags = i.getNode()->getValue();
177 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
178 only_from_disk = false;
183 if(enable_mapgen_debug_info)
184 infostream<<"EmergeThread: p="
185 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
186 <<"only_from_disk="<<only_from_disk<<std::endl;
188 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
190 //core::map<v3s16, MapBlock*> changed_blocks;
191 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
193 MapBlock *block = NULL;
194 bool got_block = true;
195 core::map<v3s16, MapBlock*> modified_blocks;
198 Fetch block from map or generate a single block
201 JMutexAutoLock envlock(m_server->m_env_mutex);
203 // Load sector if it isn't loaded
204 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
205 //map.loadSectorFull(p2d);
206 map.loadSectorMeta(p2d);
208 block = map.getBlockNoCreateNoEx(p);
209 if(!block || block->isDummy() || !block->isGenerated())
211 if(enable_mapgen_debug_info)
212 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
214 // Get, load or create sector
215 /*ServerMapSector *sector =
216 (ServerMapSector*)map.createSector(p2d);*/
218 // Load/generate block
220 /*block = map.emergeBlock(p, sector, changed_blocks,
221 lighting_invalidated_blocks);*/
223 block = map.loadBlock(p);
225 if(only_from_disk == false)
227 if(block == NULL || block->isGenerated() == false)
229 if(enable_mapgen_debug_info)
230 infostream<<"EmergeThread: generating"<<std::endl;
231 block = map.generateBlock(p, modified_blocks);
235 if(enable_mapgen_debug_info)
236 infostream<<"EmergeThread: ended up with: "
237 <<analyze_block(block)<<std::endl;
246 Ignore map edit events, they will not need to be
247 sent to anybody because the block hasn't been sent
250 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
252 // Activate objects and stuff
253 m_server->m_env.activateBlock(block, 3600);
258 /*if(block->getLightingExpired()){
259 lighting_invalidated_blocks[block->getPos()] = block;
263 // TODO: Some additional checking and lighting updating,
268 JMutexAutoLock envlock(m_server->m_env_mutex);
273 Collect a list of blocks that have been modified in
274 addition to the fetched one.
278 if(lighting_invalidated_blocks.size() > 0)
280 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
281 <<" blocks"<<std::endl;*/
283 // 50-100ms for single block generation
284 //TimeTaker timer("** EmergeThread updateLighting");
286 // Update lighting without locking the environment mutex,
287 // add modified blocks to changed blocks
288 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
291 // Add all from changed_blocks to modified_blocks
292 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
293 i.atEnd() == false; i++)
295 MapBlock *block = i.getNode()->getValue();
296 modified_blocks.insert(block->getPos(), block);
300 // If we got no block, there should be no invalidated blocks
303 //assert(lighting_invalidated_blocks.size() == 0);
309 Set sent status of modified blocks on clients
312 // NOTE: Server's clients are also behind the connection mutex
313 JMutexAutoLock lock(m_server->m_con_mutex);
316 Add the originally fetched block to the modified list
320 modified_blocks.insert(p, block);
324 Set the modified blocks unsent for all the clients
327 for(core::map<u16, RemoteClient*>::Iterator
328 i = m_server->m_clients.getIterator();
329 i.atEnd() == false; i++)
331 RemoteClient *client = i.getNode()->getValue();
333 if(modified_blocks.size() > 0)
335 // Remove block from sent history
336 client->SetBlocksNotSent(modified_blocks);
342 END_DEBUG_EXCEPTION_HANDLER(errorstream)
347 void RemoteClient::GetNextBlocks(Server *server, float dtime,
348 core::array<PrioritySortedBlockTransfer> &dest)
350 DSTACK(__FUNCTION_NAME);
353 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
356 m_nothing_to_send_pause_timer -= dtime;
357 m_nearest_unsent_reset_timer += dtime;
359 if(m_nothing_to_send_pause_timer >= 0)
364 // Won't send anything if already sending
365 if(m_blocks_sending.size() >= g_settings->getU16
366 ("max_simultaneous_block_sends_per_client"))
368 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
372 //TimeTaker timer("RemoteClient::GetNextBlocks");
374 Player *player = server->m_env.getPlayer(peer_id);
376 assert(player != NULL);
378 v3f playerpos = player->getPosition();
379 v3f playerspeed = player->getSpeed();
380 v3f playerspeeddir(0,0,0);
381 if(playerspeed.getLength() > 1.0*BS)
382 playerspeeddir = playerspeed / playerspeed.getLength();
383 // Predict to next block
384 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
386 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
388 v3s16 center = getNodeBlockPos(center_nodepos);
390 // Camera position and direction
391 v3f camera_pos = player->getEyePosition();
392 v3f camera_dir = v3f(0,0,1);
393 camera_dir.rotateYZBy(player->getPitch());
394 camera_dir.rotateXZBy(player->getYaw());
396 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
397 <<camera_dir.Z<<")"<<std::endl;*/
400 Get the starting value of the block finder radius.
403 if(m_last_center != center)
405 m_nearest_unsent_d = 0;
406 m_last_center = center;
409 /*infostream<<"m_nearest_unsent_reset_timer="
410 <<m_nearest_unsent_reset_timer<<std::endl;*/
412 // Reset periodically to workaround for some bugs or stuff
413 if(m_nearest_unsent_reset_timer > 20.0)
415 m_nearest_unsent_reset_timer = 0;
416 m_nearest_unsent_d = 0;
417 //infostream<<"Resetting m_nearest_unsent_d for "
418 // <<server->getPlayerName(peer_id)<<std::endl;
421 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
422 s16 d_start = m_nearest_unsent_d;
424 //infostream<<"d_start="<<d_start<<std::endl;
426 u16 max_simul_sends_setting = g_settings->getU16
427 ("max_simultaneous_block_sends_per_client");
428 u16 max_simul_sends_usually = max_simul_sends_setting;
431 Check the time from last addNode/removeNode.
433 Decrease send rate if player is building stuff.
435 m_time_from_building += dtime;
436 if(m_time_from_building < g_settings->getFloat(
437 "full_block_send_enable_min_time_from_building"))
439 max_simul_sends_usually
440 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
444 Number of blocks sending + number of blocks selected for sending
446 u32 num_blocks_selected = m_blocks_sending.size();
449 next time d will be continued from the d from which the nearest
450 unsent block was found this time.
452 This is because not necessarily any of the blocks found this
453 time are actually sent.
455 s32 new_nearest_unsent_d = -1;
457 s16 d_max = g_settings->getS16("max_block_send_distance");
458 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
460 // Don't loop very much at a time
461 s16 max_d_increment_at_time = 2;
462 if(d_max > d_start + max_d_increment_at_time)
463 d_max = d_start + max_d_increment_at_time;
464 /*if(d_max_gen > d_start+2)
465 d_max_gen = d_start+2;*/
467 //infostream<<"Starting from "<<d_start<<std::endl;
469 s32 nearest_emerged_d = -1;
470 s32 nearest_emergefull_d = -1;
471 s32 nearest_sent_d = -1;
472 bool queue_is_full = false;
475 for(d = d_start; d <= d_max; d++)
477 /*errorstream<<"checking d="<<d<<" for "
478 <<server->getPlayerName(peer_id)<<std::endl;*/
479 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
482 If m_nearest_unsent_d was changed by the EmergeThread
483 (it can change it to 0 through SetBlockNotSent),
485 Else update m_nearest_unsent_d
487 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
489 d = m_nearest_unsent_d;
490 last_nearest_unsent_d = m_nearest_unsent_d;
494 Get the border/face dot coordinates of a "d-radiused"
497 core::list<v3s16> list;
498 getFacePositions(list, d);
500 core::list<v3s16>::Iterator li;
501 for(li=list.begin(); li!=list.end(); li++)
503 v3s16 p = *li + center;
507 - Don't allow too many simultaneous transfers
508 - EXCEPT when the blocks are very close
510 Also, don't send blocks that are already flying.
513 // Start with the usual maximum
514 u16 max_simul_dynamic = max_simul_sends_usually;
516 // If block is very close, allow full maximum
517 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
518 max_simul_dynamic = max_simul_sends_setting;
520 // Don't select too many blocks for sending
521 if(num_blocks_selected >= max_simul_dynamic)
523 queue_is_full = true;
524 goto queue_full_break;
527 // Don't send blocks that are currently being transferred
528 if(m_blocks_sending.find(p) != NULL)
534 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
535 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
536 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
537 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
538 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
539 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
542 // If this is true, inexistent block will be made from scratch
543 bool generate = d <= d_max_gen;
546 /*// Limit the generating area vertically to 2/3
547 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
550 // Limit the send area vertically to 1/2
551 if(abs(p.Y - center.Y) > d_max / 2)
557 If block is far away, don't generate it unless it is
563 // Block center y in nodes
564 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
565 // Don't generate if it's very high or very low
566 if(y < -64 || y > 64)
570 v2s16 p2d_nodes_center(
574 // Get ground height in nodes
575 s16 gh = server->m_env.getServerMap().findGroundLevel(
578 // If differs a lot, don't generate
579 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
581 // Actually, don't even send it
587 //infostream<<"d="<<d<<std::endl;
590 Don't generate or send if not in sight
591 FIXME This only works if the client uses a small enough
592 FOV setting. The default of 72 degrees is fine.
595 float camera_fov = (72.0*PI/180) * 4./3.;
596 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
602 Don't send already sent blocks
605 if(m_blocks_sent.find(p) != NULL)
612 Check if map has this block
614 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
616 bool surely_not_found_on_disk = false;
617 bool block_is_invalid = false;
620 // Reset usage timer, this block will be of use in the future.
621 block->resetUsageTimer();
623 // Block is dummy if data doesn't exist.
624 // It means it has been not found from disk and not generated
627 surely_not_found_on_disk = true;
630 // Block is valid if lighting is up-to-date and data exists
631 if(block->isValid() == false)
633 block_is_invalid = true;
636 /*if(block->isFullyGenerated() == false)
638 block_is_invalid = true;
643 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
644 v2s16 chunkpos = map->sector_to_chunk(p2d);
645 if(map->chunkNonVolatile(chunkpos) == false)
646 block_is_invalid = true;
648 if(block->isGenerated() == false)
649 block_is_invalid = true;
652 If block is not close, don't send it unless it is near
655 Block is near ground level if night-time mesh
656 differs from day-time mesh.
660 if(block->dayNightDiffed() == false)
667 If block has been marked to not exist on disk (dummy)
668 and generating new ones is not wanted, skip block.
670 if(generate == false && surely_not_found_on_disk == true)
677 Add inexistent block to emerge queue.
679 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
681 //TODO: Get value from somewhere
682 // Allow only one block in emerge queue
683 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
684 // Allow two blocks in queue per client
685 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
686 if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
688 //infostream<<"Adding block to emerge queue"<<std::endl;
690 // Add it to the emerge queue and trigger the thread
693 if(generate == false)
694 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
696 server->m_emerge_queue.addBlock(peer_id, p, flags);
697 server->m_emergethread.trigger();
699 if(nearest_emerged_d == -1)
700 nearest_emerged_d = d;
702 if(nearest_emergefull_d == -1)
703 nearest_emergefull_d = d;
710 if(nearest_sent_d == -1)
714 Add block to send queue
717 /*errorstream<<"sending from d="<<d<<" to "
718 <<server->getPlayerName(peer_id)<<std::endl;*/
720 PrioritySortedBlockTransfer q((float)d, p, peer_id);
724 num_blocks_selected += 1;
729 //infostream<<"Stopped at "<<d<<std::endl;
731 // If nothing was found for sending and nothing was queued for
732 // emerging, continue next time browsing from here
733 if(nearest_emerged_d != -1){
734 new_nearest_unsent_d = nearest_emerged_d;
735 } else if(nearest_emergefull_d != -1){
736 new_nearest_unsent_d = nearest_emergefull_d;
738 if(d > g_settings->getS16("max_block_send_distance")){
739 new_nearest_unsent_d = 0;
740 m_nothing_to_send_pause_timer = 2.0;
741 /*infostream<<"GetNextBlocks(): d wrapped around for "
742 <<server->getPlayerName(peer_id)
743 <<"; setting to 0 and pausing"<<std::endl;*/
745 if(nearest_sent_d != -1)
746 new_nearest_unsent_d = nearest_sent_d;
748 new_nearest_unsent_d = d;
752 if(new_nearest_unsent_d != -1)
753 m_nearest_unsent_d = new_nearest_unsent_d;
755 /*timer_result = timer.stop(true);
756 if(timer_result != 0)
757 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
760 void RemoteClient::SendObjectData(
763 core::map<v3s16, bool> &stepped_blocks
766 DSTACK(__FUNCTION_NAME);
768 // Can't send anything without knowing version
769 if(serialization_version == SER_FMT_VER_INVALID)
771 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
777 Send a TOCLIENT_OBJECTDATA packet.
781 u16 number of player positions
793 std::ostringstream os(std::ios_base::binary);
797 writeU16(buf, TOCLIENT_OBJECTDATA);
798 os.write((char*)buf, 2);
801 Get and write player data
804 // Get connected players
805 core::list<Player*> players = server->m_env.getPlayers(true);
807 // Write player count
808 u16 playercount = players.size();
809 writeU16(buf, playercount);
810 os.write((char*)buf, 2);
812 core::list<Player*>::Iterator i;
813 for(i = players.begin();
814 i != players.end(); i++)
818 v3f pf = player->getPosition();
819 v3f sf = player->getSpeed();
821 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
822 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
823 s32 pitch_i (player->getPitch() * 100);
824 s32 yaw_i (player->getYaw() * 100);
826 writeU16(buf, player->peer_id);
827 os.write((char*)buf, 2);
828 writeV3S32(buf, position_i);
829 os.write((char*)buf, 12);
830 writeV3S32(buf, speed_i);
831 os.write((char*)buf, 12);
832 writeS32(buf, pitch_i);
833 os.write((char*)buf, 4);
834 writeS32(buf, yaw_i);
835 os.write((char*)buf, 4);
839 Get and write object data (dummy, for compatibility)
844 os.write((char*)buf, 2);
850 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
853 std::string s = os.str();
854 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
855 // Send as unreliable
856 server->m_con.Send(peer_id, 0, data, false);
859 void RemoteClient::GotBlock(v3s16 p)
861 if(m_blocks_sending.find(p) != NULL)
862 m_blocks_sending.remove(p);
865 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
866 " m_blocks_sending"<<std::endl;*/
867 m_excess_gotblocks++;
869 m_blocks_sent.insert(p, true);
872 void RemoteClient::SentBlock(v3s16 p)
874 if(m_blocks_sending.find(p) == NULL)
875 m_blocks_sending.insert(p, 0.0);
877 infostream<<"RemoteClient::SentBlock(): Sent block"
878 " already in m_blocks_sending"<<std::endl;
881 void RemoteClient::SetBlockNotSent(v3s16 p)
883 m_nearest_unsent_d = 0;
885 if(m_blocks_sending.find(p) != NULL)
886 m_blocks_sending.remove(p);
887 if(m_blocks_sent.find(p) != NULL)
888 m_blocks_sent.remove(p);
891 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
893 m_nearest_unsent_d = 0;
895 for(core::map<v3s16, MapBlock*>::Iterator
896 i = blocks.getIterator();
897 i.atEnd()==false; i++)
899 v3s16 p = i.getNode()->getKey();
901 if(m_blocks_sending.find(p) != NULL)
902 m_blocks_sending.remove(p);
903 if(m_blocks_sent.find(p) != NULL)
904 m_blocks_sent.remove(p);
912 PlayerInfo::PlayerInfo()
918 void PlayerInfo::PrintLine(std::ostream *s)
921 (*s)<<"\""<<name<<"\" ("
922 <<(position.X/10)<<","<<(position.Y/10)
923 <<","<<(position.Z/10)<<") ";
925 (*s)<<" avg_rtt="<<avg_rtt;
929 u32 PIChecksum(core::list<PlayerInfo> &l)
931 core::list<PlayerInfo>::Iterator i;
934 for(i=l.begin(); i!=l.end(); i++)
936 checksum += a * (i->id+1);
937 checksum ^= 0x435aafcd;
948 std::string mapsavedir,
949 std::string configpath
951 m_env(new ServerMap(mapsavedir), this),
952 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
953 m_authmanager(mapsavedir+"/auth.txt"),
954 m_banmanager(mapsavedir+"/ipban.txt"),
956 m_emergethread(this),
958 m_time_of_day_send_timer(0),
960 m_mapsavedir(mapsavedir),
961 m_configpath(configpath),
962 m_shutdown_requested(false),
963 m_ignore_map_edit_events(false),
964 m_ignore_map_edit_events_peer_id(0)
966 m_liquid_transform_timer = 0.0;
967 m_print_info_timer = 0.0;
968 m_objectdata_timer = 0.0;
969 m_emergethread_trigger_timer = 0.0;
970 m_savemap_timer = 0.0;
974 m_step_dtime_mutex.Init();
977 // Register us to receive map edit events
978 m_env.getMap().addEventReceiver(this);
980 // If file exists, load environment metadata
981 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
983 infostream<<"Server: Loading environment metadata"<<std::endl;
984 m_env.loadMeta(m_mapsavedir);
988 infostream<<"Server: Loading players"<<std::endl;
989 m_env.deSerializePlayers(m_mapsavedir);
994 infostream<<"Server::~Server()"<<std::endl;
997 Send shutdown message
1000 JMutexAutoLock conlock(m_con_mutex);
1002 std::wstring line = L"*** Server shutting down";
1005 Send the message to clients
1007 for(core::map<u16, RemoteClient*>::Iterator
1008 i = m_clients.getIterator();
1009 i.atEnd() == false; i++)
1011 // Get client and check that it is valid
1012 RemoteClient *client = i.getNode()->getValue();
1013 assert(client->peer_id == i.getNode()->getKey());
1014 if(client->serialization_version == SER_FMT_VER_INVALID)
1018 SendChatMessage(client->peer_id, line);
1020 catch(con::PeerNotFoundException &e)
1026 JMutexAutoLock envlock(m_env_mutex);
1031 infostream<<"Server: Saving players"<<std::endl;
1032 m_env.serializePlayers(m_mapsavedir);
1035 Save environment metadata
1037 infostream<<"Server: Saving environment metadata"<<std::endl;
1038 m_env.saveMeta(m_mapsavedir);
1050 JMutexAutoLock clientslock(m_con_mutex);
1052 for(core::map<u16, RemoteClient*>::Iterator
1053 i = m_clients.getIterator();
1054 i.atEnd() == false; i++)
1057 // NOTE: These are removed by env destructor
1059 u16 peer_id = i.getNode()->getKey();
1060 JMutexAutoLock envlock(m_env_mutex);
1061 m_env.removePlayer(peer_id);
1065 delete i.getNode()->getValue();
1070 void Server::start(unsigned short port)
1072 DSTACK(__FUNCTION_NAME);
1073 // Stop thread if already running
1076 // Initialize connection
1077 m_con.setTimeoutMs(30);
1081 m_thread.setRun(true);
1084 infostream<<"Server: Started on port "<<port<<std::endl;
1089 DSTACK(__FUNCTION_NAME);
1091 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1093 // Stop threads (set run=false first so both start stopping)
1094 m_thread.setRun(false);
1095 m_emergethread.setRun(false);
1097 m_emergethread.stop();
1099 infostream<<"Server: Threads stopped"<<std::endl;
1102 void Server::step(float dtime)
1104 DSTACK(__FUNCTION_NAME);
1109 JMutexAutoLock lock(m_step_dtime_mutex);
1110 m_step_dtime += dtime;
1114 void Server::AsyncRunStep()
1116 DSTACK(__FUNCTION_NAME);
1118 g_profiler->add("Server::AsyncRunStep (num)", 1);
1122 JMutexAutoLock lock1(m_step_dtime_mutex);
1123 dtime = m_step_dtime;
1127 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1128 // Send blocks to clients
1135 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1137 //infostream<<"Server steps "<<dtime<<std::endl;
1138 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1141 JMutexAutoLock lock1(m_step_dtime_mutex);
1142 m_step_dtime -= dtime;
1149 m_uptime.set(m_uptime.get() + dtime);
1153 // Process connection's timeouts
1154 JMutexAutoLock lock2(m_con_mutex);
1155 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1156 m_con.RunTimeouts(dtime);
1160 // This has to be called so that the client list gets synced
1161 // with the peer list of the connection
1162 handlePeerChanges();
1166 Update m_time_of_day and overall game time
1169 JMutexAutoLock envlock(m_env_mutex);
1171 m_time_counter += dtime;
1172 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1173 u32 units = (u32)(m_time_counter*speed);
1174 m_time_counter -= (f32)units / speed;
1176 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1178 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1181 Send to clients at constant intervals
1184 m_time_of_day_send_timer -= dtime;
1185 if(m_time_of_day_send_timer < 0.0)
1187 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1189 //JMutexAutoLock envlock(m_env_mutex);
1190 JMutexAutoLock conlock(m_con_mutex);
1192 for(core::map<u16, RemoteClient*>::Iterator
1193 i = m_clients.getIterator();
1194 i.atEnd() == false; i++)
1196 RemoteClient *client = i.getNode()->getValue();
1197 //Player *player = m_env.getPlayer(client->peer_id);
1199 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1200 m_env.getTimeOfDay());
1202 m_con.Send(client->peer_id, 0, data, true);
1208 JMutexAutoLock lock(m_env_mutex);
1210 ScopeProfiler sp(g_profiler, "SEnv step");
1211 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1215 const float map_timer_and_unload_dtime = 5.15;
1216 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1218 JMutexAutoLock lock(m_env_mutex);
1219 // Run Map's timers and unload unused data
1220 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1221 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1222 g_settings->getFloat("server_unload_unused_data_timeout"));
1232 m_liquid_transform_timer += dtime;
1233 if(m_liquid_transform_timer >= 1.00)
1235 m_liquid_transform_timer -= 1.00;
1237 JMutexAutoLock lock(m_env_mutex);
1239 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1241 core::map<v3s16, MapBlock*> modified_blocks;
1242 m_env.getMap().transformLiquids(modified_blocks);
1247 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1248 ServerMap &map = ((ServerMap&)m_env.getMap());
1249 map.updateLighting(modified_blocks, lighting_modified_blocks);
1251 // Add blocks modified by lighting to modified_blocks
1252 for(core::map<v3s16, MapBlock*>::Iterator
1253 i = lighting_modified_blocks.getIterator();
1254 i.atEnd() == false; i++)
1256 MapBlock *block = i.getNode()->getValue();
1257 modified_blocks.insert(block->getPos(), block);
1261 Set the modified blocks unsent for all the clients
1264 JMutexAutoLock lock2(m_con_mutex);
1266 for(core::map<u16, RemoteClient*>::Iterator
1267 i = m_clients.getIterator();
1268 i.atEnd() == false; i++)
1270 RemoteClient *client = i.getNode()->getValue();
1272 if(modified_blocks.size() > 0)
1274 // Remove block from sent history
1275 client->SetBlocksNotSent(modified_blocks);
1280 // Periodically print some info
1282 float &counter = m_print_info_timer;
1288 JMutexAutoLock lock2(m_con_mutex);
1290 if(m_clients.size() != 0)
1291 infostream<<"Players:"<<std::endl;
1292 for(core::map<u16, RemoteClient*>::Iterator
1293 i = m_clients.getIterator();
1294 i.atEnd() == false; i++)
1296 //u16 peer_id = i.getNode()->getKey();
1297 RemoteClient *client = i.getNode()->getValue();
1298 Player *player = m_env.getPlayer(client->peer_id);
1301 infostream<<"* "<<player->getName()<<"\t";
1302 client->PrintInfo(infostream);
1307 //if(g_settings->getBool("enable_experimental"))
1311 Check added and deleted active objects
1314 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1315 JMutexAutoLock envlock(m_env_mutex);
1316 JMutexAutoLock conlock(m_con_mutex);
1318 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1320 // Radius inside which objects are active
1321 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1322 radius *= MAP_BLOCKSIZE;
1324 for(core::map<u16, RemoteClient*>::Iterator
1325 i = m_clients.getIterator();
1326 i.atEnd() == false; i++)
1328 RemoteClient *client = i.getNode()->getValue();
1329 Player *player = m_env.getPlayer(client->peer_id);
1332 // This can happen if the client timeouts somehow
1333 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1335 <<" has no associated player"<<std::endl;*/
1338 v3s16 pos = floatToInt(player->getPosition(), BS);
1340 core::map<u16, bool> removed_objects;
1341 core::map<u16, bool> added_objects;
1342 m_env.getRemovedActiveObjects(pos, radius,
1343 client->m_known_objects, removed_objects);
1344 m_env.getAddedActiveObjects(pos, radius,
1345 client->m_known_objects, added_objects);
1347 // Ignore if nothing happened
1348 if(removed_objects.size() == 0 && added_objects.size() == 0)
1350 //infostream<<"active objects: none changed"<<std::endl;
1354 std::string data_buffer;
1358 // Handle removed objects
1359 writeU16((u8*)buf, removed_objects.size());
1360 data_buffer.append(buf, 2);
1361 for(core::map<u16, bool>::Iterator
1362 i = removed_objects.getIterator();
1363 i.atEnd()==false; i++)
1366 u16 id = i.getNode()->getKey();
1367 ServerActiveObject* obj = m_env.getActiveObject(id);
1369 // Add to data buffer for sending
1370 writeU16((u8*)buf, i.getNode()->getKey());
1371 data_buffer.append(buf, 2);
1373 // Remove from known objects
1374 client->m_known_objects.remove(i.getNode()->getKey());
1376 if(obj && obj->m_known_by_count > 0)
1377 obj->m_known_by_count--;
1380 // Handle added objects
1381 writeU16((u8*)buf, added_objects.size());
1382 data_buffer.append(buf, 2);
1383 for(core::map<u16, bool>::Iterator
1384 i = added_objects.getIterator();
1385 i.atEnd()==false; i++)
1388 u16 id = i.getNode()->getKey();
1389 ServerActiveObject* obj = m_env.getActiveObject(id);
1392 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1394 infostream<<"WARNING: "<<__FUNCTION_NAME
1395 <<": NULL object"<<std::endl;
1397 type = obj->getType();
1399 // Add to data buffer for sending
1400 writeU16((u8*)buf, id);
1401 data_buffer.append(buf, 2);
1402 writeU8((u8*)buf, type);
1403 data_buffer.append(buf, 1);
1406 data_buffer.append(serializeLongString(
1407 obj->getClientInitializationData()));
1409 data_buffer.append(serializeLongString(""));
1411 // Add to known objects
1412 client->m_known_objects.insert(i.getNode()->getKey(), false);
1415 obj->m_known_by_count++;
1419 SharedBuffer<u8> reply(2 + data_buffer.size());
1420 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1421 memcpy((char*)&reply[2], data_buffer.c_str(),
1422 data_buffer.size());
1424 m_con.Send(client->peer_id, 0, reply, true);
1426 infostream<<"Server: Sent object remove/add: "
1427 <<removed_objects.size()<<" removed, "
1428 <<added_objects.size()<<" added, "
1429 <<"packet size is "<<reply.getSize()<<std::endl;
1434 Collect a list of all the objects known by the clients
1435 and report it back to the environment.
1438 core::map<u16, bool> all_known_objects;
1440 for(core::map<u16, RemoteClient*>::Iterator
1441 i = m_clients.getIterator();
1442 i.atEnd() == false; i++)
1444 RemoteClient *client = i.getNode()->getValue();
1445 // Go through all known objects of client
1446 for(core::map<u16, bool>::Iterator
1447 i = client->m_known_objects.getIterator();
1448 i.atEnd()==false; i++)
1450 u16 id = i.getNode()->getKey();
1451 all_known_objects[id] = true;
1455 m_env.setKnownActiveObjects(whatever);
1461 Send object messages
1464 JMutexAutoLock envlock(m_env_mutex);
1465 JMutexAutoLock conlock(m_con_mutex);
1467 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1470 // Value = data sent by object
1471 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1473 // Get active object messages from environment
1476 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1480 core::list<ActiveObjectMessage>* message_list = NULL;
1481 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1482 n = buffered_messages.find(aom.id);
1485 message_list = new core::list<ActiveObjectMessage>;
1486 buffered_messages.insert(aom.id, message_list);
1490 message_list = n->getValue();
1492 message_list->push_back(aom);
1495 // Route data to every client
1496 for(core::map<u16, RemoteClient*>::Iterator
1497 i = m_clients.getIterator();
1498 i.atEnd()==false; i++)
1500 RemoteClient *client = i.getNode()->getValue();
1501 std::string reliable_data;
1502 std::string unreliable_data;
1503 // Go through all objects in message buffer
1504 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1505 j = buffered_messages.getIterator();
1506 j.atEnd()==false; j++)
1508 // If object is not known by client, skip it
1509 u16 id = j.getNode()->getKey();
1510 if(client->m_known_objects.find(id) == NULL)
1512 // Get message list of object
1513 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1514 // Go through every message
1515 for(core::list<ActiveObjectMessage>::Iterator
1516 k = list->begin(); k != list->end(); k++)
1518 // Compose the full new data with header
1519 ActiveObjectMessage aom = *k;
1520 std::string new_data;
1523 writeU16((u8*)&buf[0], aom.id);
1524 new_data.append(buf, 2);
1526 new_data += serializeString(aom.datastring);
1527 // Add data to buffer
1529 reliable_data += new_data;
1531 unreliable_data += new_data;
1535 reliable_data and unreliable_data are now ready.
1538 if(reliable_data.size() > 0)
1540 SharedBuffer<u8> reply(2 + reliable_data.size());
1541 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1542 memcpy((char*)&reply[2], reliable_data.c_str(),
1543 reliable_data.size());
1545 m_con.Send(client->peer_id, 0, reply, true);
1547 if(unreliable_data.size() > 0)
1549 SharedBuffer<u8> reply(2 + unreliable_data.size());
1550 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1551 memcpy((char*)&reply[2], unreliable_data.c_str(),
1552 unreliable_data.size());
1553 // Send as unreliable
1554 m_con.Send(client->peer_id, 0, reply, false);
1557 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1559 infostream<<"Server: Size of object message data: "
1560 <<"reliable: "<<reliable_data.size()
1561 <<", unreliable: "<<unreliable_data.size()
1566 // Clear buffered_messages
1567 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1568 i = buffered_messages.getIterator();
1569 i.atEnd()==false; i++)
1571 delete i.getNode()->getValue();
1575 } // enable_experimental
1578 Send queued-for-sending map edit events.
1581 // Don't send too many at a time
1584 // Single change sending is disabled if queue size is not small
1585 bool disable_single_change_sending = false;
1586 if(m_unsent_map_edit_queue.size() >= 4)
1587 disable_single_change_sending = true;
1589 bool got_any_events = false;
1591 // We'll log the amount of each
1594 while(m_unsent_map_edit_queue.size() != 0)
1596 got_any_events = true;
1598 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1600 // Players far away from the change are stored here.
1601 // Instead of sending the changes, MapBlocks are set not sent
1603 core::list<u16> far_players;
1605 if(event->type == MEET_ADDNODE)
1607 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1608 prof.add("MEET_ADDNODE", 1);
1609 if(disable_single_change_sending)
1610 sendAddNode(event->p, event->n, event->already_known_by_peer,
1613 sendAddNode(event->p, event->n, event->already_known_by_peer,
1616 else if(event->type == MEET_REMOVENODE)
1618 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1619 prof.add("MEET_REMOVENODE", 1);
1620 if(disable_single_change_sending)
1621 sendRemoveNode(event->p, event->already_known_by_peer,
1624 sendRemoveNode(event->p, event->already_known_by_peer,
1627 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1629 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1630 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1631 setBlockNotSent(event->p);
1633 else if(event->type == MEET_OTHER)
1635 infostream<<"Server: MEET_OTHER"<<std::endl;
1636 prof.add("MEET_OTHER", 1);
1637 for(core::map<v3s16, bool>::Iterator
1638 i = event->modified_blocks.getIterator();
1639 i.atEnd()==false; i++)
1641 v3s16 p = i.getNode()->getKey();
1647 prof.add("unknown", 1);
1648 infostream<<"WARNING: Server: Unknown MapEditEvent "
1649 <<((u32)event->type)<<std::endl;
1653 Set blocks not sent to far players
1655 if(far_players.size() > 0)
1657 // Convert list format to that wanted by SetBlocksNotSent
1658 core::map<v3s16, MapBlock*> modified_blocks2;
1659 for(core::map<v3s16, bool>::Iterator
1660 i = event->modified_blocks.getIterator();
1661 i.atEnd()==false; i++)
1663 v3s16 p = i.getNode()->getKey();
1664 modified_blocks2.insert(p,
1665 m_env.getMap().getBlockNoCreateNoEx(p));
1667 // Set blocks not sent
1668 for(core::list<u16>::Iterator
1669 i = far_players.begin();
1670 i != far_players.end(); i++)
1673 RemoteClient *client = getClient(peer_id);
1676 client->SetBlocksNotSent(modified_blocks2);
1682 /*// Don't send too many at a time
1684 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1690 infostream<<"Server: MapEditEvents:"<<std::endl;
1691 prof.print(infostream);
1697 Send object positions
1700 float &counter = m_objectdata_timer;
1702 if(counter >= g_settings->getFloat("objectdata_interval"))
1704 JMutexAutoLock lock1(m_env_mutex);
1705 JMutexAutoLock lock2(m_con_mutex);
1707 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1709 SendObjectData(counter);
1716 Trigger emergethread (it somehow gets to a non-triggered but
1717 bysy state sometimes)
1720 float &counter = m_emergethread_trigger_timer;
1726 m_emergethread.trigger();
1730 // Save map, players and auth stuff
1732 float &counter = m_savemap_timer;
1734 if(counter >= g_settings->getFloat("server_map_save_interval"))
1738 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1741 if(m_authmanager.isModified())
1742 m_authmanager.save();
1745 if(m_banmanager.isModified())
1746 m_banmanager.save();
1749 JMutexAutoLock lock(m_env_mutex);
1751 /*// Unload unused data (delete from memory)
1752 m_env.getMap().unloadUnusedData(
1753 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1755 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1756 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1759 // Save only changed parts
1760 m_env.getMap().save(true);
1762 /*if(deleted_count > 0)
1764 infostream<<"Server: Unloaded "<<deleted_count
1765 <<" blocks from memory"<<std::endl;
1769 m_env.serializePlayers(m_mapsavedir);
1771 // Save environment metadata
1772 m_env.saveMeta(m_mapsavedir);
1777 void Server::Receive()
1779 DSTACK(__FUNCTION_NAME);
1780 u32 data_maxsize = 10000;
1781 Buffer<u8> data(data_maxsize);
1786 JMutexAutoLock conlock(m_con_mutex);
1787 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1790 // This has to be called so that the client list gets synced
1791 // with the peer list of the connection
1792 handlePeerChanges();
1794 ProcessData(*data, datasize, peer_id);
1796 catch(con::InvalidIncomingDataException &e)
1798 infostream<<"Server::Receive(): "
1799 "InvalidIncomingDataException: what()="
1800 <<e.what()<<std::endl;
1802 catch(con::PeerNotFoundException &e)
1804 //NOTE: This is not needed anymore
1806 // The peer has been disconnected.
1807 // Find the associated player and remove it.
1809 /*JMutexAutoLock envlock(m_env_mutex);
1811 infostream<<"ServerThread: peer_id="<<peer_id
1812 <<" has apparently closed connection. "
1813 <<"Removing player."<<std::endl;
1815 m_env.removePlayer(peer_id);*/
1819 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1821 DSTACK(__FUNCTION_NAME);
1822 // Environment is locked first.
1823 JMutexAutoLock envlock(m_env_mutex);
1824 JMutexAutoLock conlock(m_con_mutex);
1828 peer = m_con.GetPeer(peer_id);
1830 catch(con::PeerNotFoundException &e)
1832 infostream<<"Server::ProcessData(): Cancelling: peer "
1833 <<peer_id<<" not found"<<std::endl;
1837 // drop player if is ip is banned
1838 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1839 SendAccessDenied(m_con, peer_id,
1840 L"Your ip is banned. Banned name was "
1841 +narrow_to_wide(m_banmanager.getBanName(
1842 peer->address.serializeString())));
1843 m_con.deletePeer(peer_id, false);
1847 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1855 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1857 if(command == TOSERVER_INIT)
1859 // [0] u16 TOSERVER_INIT
1860 // [2] u8 SER_FMT_VER_HIGHEST
1861 // [3] u8[20] player_name
1862 // [23] u8[28] password <--- can be sent without this, from old versions
1864 if(datasize < 2+1+PLAYERNAME_SIZE)
1867 infostream<<"Server: Got TOSERVER_INIT from "
1868 <<peer->id<<std::endl;
1870 // First byte after command is maximum supported
1871 // serialization version
1872 u8 client_max = data[2];
1873 u8 our_max = SER_FMT_VER_HIGHEST;
1874 // Use the highest version supported by both
1875 u8 deployed = core::min_(client_max, our_max);
1876 // If it's lower than the lowest supported, give up.
1877 if(deployed < SER_FMT_VER_LOWEST)
1878 deployed = SER_FMT_VER_INVALID;
1880 //peer->serialization_version = deployed;
1881 getClient(peer->id)->pending_serialization_version = deployed;
1883 if(deployed == SER_FMT_VER_INVALID)
1885 infostream<<"Server: Cannot negotiate "
1886 "serialization version with peer "
1887 <<peer_id<<std::endl;
1888 SendAccessDenied(m_con, peer_id,
1889 L"Your client is too old (map format)");
1894 Read and check network protocol version
1897 u16 net_proto_version = 0;
1898 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1900 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1903 getClient(peer->id)->net_proto_version = net_proto_version;
1905 if(net_proto_version == 0)
1907 SendAccessDenied(m_con, peer_id,
1908 L"Your client is too old. Please upgrade.");
1912 /* Uhh... this should actually be a warning but let's do it like this */
1913 if(g_settings->getBool("strict_protocol_version_checking"))
1915 if(net_proto_version < PROTOCOL_VERSION)
1917 SendAccessDenied(m_con, peer_id,
1918 L"Your client is too old. Please upgrade.");
1928 char playername[PLAYERNAME_SIZE];
1929 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1931 playername[i] = data[3+i];
1933 playername[PLAYERNAME_SIZE-1] = 0;
1935 if(playername[0]=='\0')
1937 infostream<<"Server: Player has empty name"<<std::endl;
1938 SendAccessDenied(m_con, peer_id,
1943 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1945 infostream<<"Server: Player has invalid name"<<std::endl;
1946 SendAccessDenied(m_con, peer_id,
1947 L"Name contains unallowed characters");
1952 char password[PASSWORD_SIZE];
1953 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1955 // old version - assume blank password
1960 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1962 password[i] = data[23+i];
1964 password[PASSWORD_SIZE-1] = 0;
1967 std::string checkpwd;
1968 if(m_authmanager.exists(playername))
1970 checkpwd = m_authmanager.getPassword(playername);
1974 checkpwd = g_settings->get("default_password");
1977 /*infostream<<"Server: Client gave password '"<<password
1978 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
1980 if(password != checkpwd && m_authmanager.exists(playername))
1982 infostream<<"Server: peer_id="<<peer_id
1983 <<": supplied invalid password for "
1984 <<playername<<std::endl;
1985 SendAccessDenied(m_con, peer_id, L"Invalid password");
1989 // Add player to auth manager
1990 if(m_authmanager.exists(playername) == false)
1992 infostream<<"Server: adding player "<<playername
1993 <<" to auth manager"<<std::endl;
1994 m_authmanager.add(playername);
1995 m_authmanager.setPassword(playername, checkpwd);
1996 m_authmanager.setPrivs(playername,
1997 stringToPrivs(g_settings->get("default_privs")));
1998 m_authmanager.save();
2001 // Enforce user limit.
2002 // Don't enforce for users that have some admin right
2003 if(m_clients.size() >= g_settings->getU16("max_users") &&
2004 (m_authmanager.getPrivs(playername)
2005 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2006 playername != g_settings->get("name"))
2008 SendAccessDenied(m_con, peer_id, L"Too many users.");
2013 Player *player = emergePlayer(playername, password, peer_id);
2015 // If failed, cancel
2018 infostream<<"Server: peer_id="<<peer_id
2019 <<": failed to emerge player"<<std::endl;
2024 Answer with a TOCLIENT_INIT
2027 SharedBuffer<u8> reply(2+1+6+8);
2028 writeU16(&reply[0], TOCLIENT_INIT);
2029 writeU8(&reply[2], deployed);
2030 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2031 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2034 m_con.Send(peer_id, 0, reply, true);
2038 Send complete position information
2040 SendMovePlayer(player);
2045 if(command == TOSERVER_INIT2)
2047 infostream<<"Server: Got TOSERVER_INIT2 from "
2048 <<peer->id<<std::endl;
2051 getClient(peer->id)->serialization_version
2052 = getClient(peer->id)->pending_serialization_version;
2055 Send some initialization data
2058 // Send player info to all players
2061 // Send inventory to player
2062 UpdateCrafting(peer->id);
2063 SendInventory(peer->id);
2065 // Send player items to all players
2068 Player *player = m_env.getPlayer(peer_id);
2071 SendPlayerHP(player);
2075 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2076 m_env.getTimeOfDay());
2077 m_con.Send(peer->id, 0, data, true);
2080 // Send information about server to player in chat
2081 SendChatMessage(peer_id, getStatusString());
2083 // Send information about joining in chat
2085 std::wstring name = L"unknown";
2086 Player *player = m_env.getPlayer(peer_id);
2088 name = narrow_to_wide(player->getName());
2090 std::wstring message;
2093 message += L" joined game";
2094 BroadcastChatMessage(message);
2097 // Warnings about protocol version can be issued here
2098 if(getClient(peer->id)->net_proto_version < PROTOCOL_VERSION)
2100 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2104 Check HP, respawn if necessary
2106 HandlePlayerHP(player, 0);
2112 std::ostringstream os(std::ios_base::binary);
2113 for(core::map<u16, RemoteClient*>::Iterator
2114 i = m_clients.getIterator();
2115 i.atEnd() == false; i++)
2117 RemoteClient *client = i.getNode()->getValue();
2118 assert(client->peer_id == i.getNode()->getKey());
2119 if(client->serialization_version == SER_FMT_VER_INVALID)
2122 Player *player = m_env.getPlayer(client->peer_id);
2125 // Get name of player
2126 os<<player->getName()<<" ";
2129 actionstream<<player->getName()<<" joins game. List of players: "
2130 <<os.str()<<std::endl;
2136 if(peer_ser_ver == SER_FMT_VER_INVALID)
2138 infostream<<"Server::ProcessData(): Cancelling: Peer"
2139 " serialization format invalid or not initialized."
2140 " Skipping incoming command="<<command<<std::endl;
2144 Player *player = m_env.getPlayer(peer_id);
2147 infostream<<"Server::ProcessData(): Cancelling: "
2148 "No player for peer_id="<<peer_id
2152 if(command == TOSERVER_PLAYERPOS)
2154 if(datasize < 2+12+12+4+4)
2158 v3s32 ps = readV3S32(&data[start+2]);
2159 v3s32 ss = readV3S32(&data[start+2+12]);
2160 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2161 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2162 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2163 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2164 pitch = wrapDegrees(pitch);
2165 yaw = wrapDegrees(yaw);
2167 player->setPosition(position);
2168 player->setSpeed(speed);
2169 player->setPitch(pitch);
2170 player->setYaw(yaw);
2172 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2173 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2174 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2176 else if(command == TOSERVER_GOTBLOCKS)
2189 u16 count = data[2];
2190 for(u16 i=0; i<count; i++)
2192 if((s16)datasize < 2+1+(i+1)*6)
2193 throw con::InvalidIncomingDataException
2194 ("GOTBLOCKS length is too short");
2195 v3s16 p = readV3S16(&data[2+1+i*6]);
2196 /*infostream<<"Server: GOTBLOCKS ("
2197 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2198 RemoteClient *client = getClient(peer_id);
2199 client->GotBlock(p);
2202 else if(command == TOSERVER_DELETEDBLOCKS)
2215 u16 count = data[2];
2216 for(u16 i=0; i<count; i++)
2218 if((s16)datasize < 2+1+(i+1)*6)
2219 throw con::InvalidIncomingDataException
2220 ("DELETEDBLOCKS length is too short");
2221 v3s16 p = readV3S16(&data[2+1+i*6]);
2222 /*infostream<<"Server: DELETEDBLOCKS ("
2223 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2224 RemoteClient *client = getClient(peer_id);
2225 client->SetBlockNotSent(p);
2228 else if(command == TOSERVER_CLICK_OBJECT)
2230 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2233 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2238 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2244 [2] u8 button (0=left, 1=right)
2248 u8 button = readU8(&data[2]);
2249 u16 id = readS16(&data[3]);
2250 u16 item_i = readU16(&data[5]);
2252 ServerActiveObject *obj = m_env.getActiveObject(id);
2256 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2261 // Skip if object has been removed
2265 //TODO: Check that object is reasonably close
2267 // Left click, pick object up (usually)
2271 Try creating inventory item
2273 InventoryItem *item = obj->createPickedUpItem();
2277 InventoryList *ilist = player->inventory.getList("main");
2280 actionstream<<player->getName()<<" picked up "
2281 <<item->getName()<<std::endl;
2282 if(g_settings->getBool("creative_mode") == false)
2284 // Skip if inventory has no free space
2285 if(ilist->roomForItem(item) == false)
2287 infostream<<"Player inventory has no free space"<<std::endl;
2291 // Add to inventory and send inventory
2292 ilist->addItem(item);
2293 UpdateCrafting(player->peer_id);
2294 SendInventory(player->peer_id);
2297 // Remove object from environment
2298 obj->m_removed = true;
2304 Item cannot be picked up. Punch it instead.
2307 actionstream<<player->getName()<<" punches object "
2308 <<obj->getId()<<std::endl;
2310 ToolItem *titem = NULL;
2311 std::string toolname = "";
2313 InventoryList *mlist = player->inventory.getList("main");
2316 InventoryItem *item = mlist->getItem(item_i);
2317 if(item && (std::string)item->getName() == "ToolItem")
2319 titem = (ToolItem*)item;
2320 toolname = titem->getToolName();
2324 v3f playerpos = player->getPosition();
2325 v3f objpos = obj->getBasePosition();
2326 v3f dir = (objpos - playerpos).normalize();
2328 u16 wear = obj->punch(toolname, dir, player->getName());
2332 bool weared_out = titem->addWear(wear);
2334 mlist->deleteItem(item_i);
2335 SendInventory(player->peer_id);
2339 // Right click, do something with object
2342 actionstream<<player->getName()<<" right clicks object "
2343 <<obj->getId()<<std::endl;
2345 // Track hp changes super-crappily
2346 u16 oldhp = player->hp;
2349 obj->rightClick(player);
2352 if(player->hp != oldhp)
2354 SendPlayerHP(player);
2358 else if(command == TOSERVER_GROUND_ACTION)
2366 [3] v3s16 nodepos_undersurface
2367 [9] v3s16 nodepos_abovesurface
2372 2: stop digging (all parameters ignored)
2373 3: digging completed
2375 u8 action = readU8(&data[2]);
2377 p_under.X = readS16(&data[3]);
2378 p_under.Y = readS16(&data[5]);
2379 p_under.Z = readS16(&data[7]);
2381 p_over.X = readS16(&data[9]);
2382 p_over.Y = readS16(&data[11]);
2383 p_over.Z = readS16(&data[13]);
2384 u16 item_i = readU16(&data[15]);
2386 //TODO: Check that target is reasonably close
2394 NOTE: This can be used in the future to check if
2395 somebody is cheating, by checking the timing.
2402 else if(action == 2)
2405 RemoteClient *client = getClient(peer->id);
2406 JMutexAutoLock digmutex(client->m_dig_mutex);
2407 client->m_dig_tool_item = -1;
2412 3: Digging completed
2414 else if(action == 3)
2416 // Mandatory parameter; actually used for nothing
2417 core::map<v3s16, MapBlock*> modified_blocks;
2419 content_t material = CONTENT_IGNORE;
2420 u8 mineral = MINERAL_NONE;
2422 bool cannot_remove_node = false;
2426 MapNode n = m_env.getMap().getNode(p_under);
2428 mineral = n.getMineral();
2429 // Get material at position
2430 material = n.getContent();
2431 // If not yet cancelled
2432 if(cannot_remove_node == false)
2434 // If it's not diggable, do nothing
2435 if(content_diggable(material) == false)
2437 infostream<<"Server: Not finishing digging: "
2438 <<"Node not diggable"
2440 cannot_remove_node = true;
2443 // If not yet cancelled
2444 if(cannot_remove_node == false)
2446 // Get node metadata
2447 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2448 if(meta && meta->nodeRemovalDisabled() == true)
2450 infostream<<"Server: Not finishing digging: "
2451 <<"Node metadata disables removal"
2453 cannot_remove_node = true;
2457 catch(InvalidPositionException &e)
2459 infostream<<"Server: Not finishing digging: Node not found."
2460 <<" Adding block to emerge queue."
2462 m_emerge_queue.addBlock(peer_id,
2463 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2464 cannot_remove_node = true;
2467 // Make sure the player is allowed to do it
2468 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2470 infostream<<"Player "<<player->getName()<<" cannot remove node"
2471 <<" because privileges are "<<getPlayerPrivs(player)
2473 cannot_remove_node = true;
2477 If node can't be removed, set block to be re-sent to
2480 if(cannot_remove_node)
2482 infostream<<"Server: Not finishing digging."<<std::endl;
2484 // Client probably has wrong data.
2485 // Set block not sent, so that client will get
2487 infostream<<"Client "<<peer_id<<" tried to dig "
2488 <<"node; but node cannot be removed."
2489 <<" setting MapBlock not sent."<<std::endl;
2490 RemoteClient *client = getClient(peer_id);
2491 v3s16 blockpos = getNodeBlockPos(p_under);
2492 client->SetBlockNotSent(blockpos);
2497 actionstream<<player->getName()<<" digs "<<PP(p_under)
2498 <<", gets material "<<(int)material<<", mineral "
2499 <<(int)mineral<<std::endl;
2502 Send the removal to all close-by players.
2503 - If other player is close, send REMOVENODE
2504 - Otherwise set blocks not sent
2506 core::list<u16> far_players;
2507 sendRemoveNode(p_under, peer_id, &far_players, 30);
2510 Update and send inventory
2513 if(g_settings->getBool("creative_mode") == false)
2518 InventoryList *mlist = player->inventory.getList("main");
2521 InventoryItem *item = mlist->getItem(item_i);
2522 if(item && (std::string)item->getName() == "ToolItem")
2524 ToolItem *titem = (ToolItem*)item;
2525 std::string toolname = titem->getToolName();
2527 // Get digging properties for material and tool
2528 DiggingProperties prop =
2529 getDiggingProperties(material, toolname);
2531 if(prop.diggable == false)
2533 infostream<<"Server: WARNING: Player digged"
2534 <<" with impossible material + tool"
2535 <<" combination"<<std::endl;
2538 bool weared_out = titem->addWear(prop.wear);
2542 mlist->deleteItem(item_i);
2548 Add dug item to inventory
2551 InventoryItem *item = NULL;
2553 if(mineral != MINERAL_NONE)
2554 item = getDiggedMineralItem(mineral);
2559 std::string &dug_s = content_features(material).dug_item;
2562 std::istringstream is(dug_s, std::ios::binary);
2563 item = InventoryItem::deSerialize(is);
2569 // Add a item to inventory
2570 player->inventory.addItem("main", item);
2573 UpdateCrafting(player->peer_id);
2574 SendInventory(player->peer_id);
2579 if(mineral != MINERAL_NONE)
2580 item = getDiggedMineralItem(mineral);
2585 std::string &extra_dug_s = content_features(material).extra_dug_item;
2586 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2587 if(extra_dug_s != "" && extra_rarity != 0
2588 && myrand() % extra_rarity == 0)
2590 std::istringstream is(extra_dug_s, std::ios::binary);
2591 item = InventoryItem::deSerialize(is);
2597 // Add a item to inventory
2598 player->inventory.addItem("main", item);
2601 UpdateCrafting(player->peer_id);
2602 SendInventory(player->peer_id);
2608 (this takes some time so it is done after the quick stuff)
2611 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2613 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2616 Set blocks not sent to far players
2618 for(core::list<u16>::Iterator
2619 i = far_players.begin();
2620 i != far_players.end(); i++)
2623 RemoteClient *client = getClient(peer_id);
2626 client->SetBlocksNotSent(modified_blocks);
2633 else if(action == 1)
2636 InventoryList *ilist = player->inventory.getList("main");
2641 InventoryItem *item = ilist->getItem(item_i);
2643 // If there is no item, it is not possible to add it anywhere
2648 Handle material items
2650 if(std::string("MaterialItem") == item->getName())
2653 // Don't add a node if this is not a free space
2654 MapNode n2 = m_env.getMap().getNode(p_over);
2655 bool no_enough_privs =
2656 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2658 infostream<<"Player "<<player->getName()<<" cannot add node"
2659 <<" because privileges are "<<getPlayerPrivs(player)
2662 if(content_features(n2).buildable_to == false
2665 // Client probably has wrong data.
2666 // Set block not sent, so that client will get
2668 infostream<<"Client "<<peer_id<<" tried to place"
2669 <<" node in invalid position; setting"
2670 <<" MapBlock not sent."<<std::endl;
2671 RemoteClient *client = getClient(peer_id);
2672 v3s16 blockpos = getNodeBlockPos(p_over);
2673 client->SetBlockNotSent(blockpos);
2677 catch(InvalidPositionException &e)
2679 infostream<<"Server: Ignoring ADDNODE: Node not found"
2680 <<" Adding block to emerge queue."
2682 m_emerge_queue.addBlock(peer_id,
2683 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2687 // Reset build time counter
2688 getClient(peer->id)->m_time_from_building = 0.0;
2691 MaterialItem *mitem = (MaterialItem*)item;
2693 n.setContent(mitem->getMaterial());
2695 actionstream<<player->getName()<<" places material "
2696 <<(int)mitem->getMaterial()
2697 <<" at "<<PP(p_under)<<std::endl;
2699 // Calculate direction for wall mounted stuff
2700 if(content_features(n).wall_mounted)
2701 n.param2 = packDir(p_under - p_over);
2703 // Calculate the direction for furnaces and chests and stuff
2704 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2706 v3f playerpos = player->getPosition();
2707 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2708 blockpos = blockpos.normalize();
2710 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2724 Send to all close-by players
2726 core::list<u16> far_players;
2727 sendAddNode(p_over, n, 0, &far_players, 30);
2732 InventoryList *ilist = player->inventory.getList("main");
2733 if(g_settings->getBool("creative_mode") == false && ilist)
2735 // Remove from inventory and send inventory
2736 if(mitem->getCount() == 1)
2737 ilist->deleteItem(item_i);
2741 UpdateCrafting(peer_id);
2742 SendInventory(peer_id);
2748 This takes some time so it is done after the quick stuff
2750 core::map<v3s16, MapBlock*> modified_blocks;
2752 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2754 std::string p_name = std::string(player->getName());
2755 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2758 Set blocks not sent to far players
2760 for(core::list<u16>::Iterator
2761 i = far_players.begin();
2762 i != far_players.end(); i++)
2765 RemoteClient *client = getClient(peer_id);
2768 client->SetBlocksNotSent(modified_blocks);
2772 Calculate special events
2775 /*if(n.d == CONTENT_MESE)
2778 for(s16 z=-1; z<=1; z++)
2779 for(s16 y=-1; y<=1; y++)
2780 for(s16 x=-1; x<=1; x++)
2787 Place other item (not a block)
2791 v3s16 blockpos = getNodeBlockPos(p_over);
2794 Check that the block is loaded so that the item
2795 can properly be added to the static list too
2797 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2800 infostream<<"Error while placing object: "
2801 "block not found"<<std::endl;
2806 If in creative mode, item dropping is disabled unless
2807 player has build privileges
2809 if(g_settings->getBool("creative_mode") &&
2810 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2812 infostream<<"Not allowing player to drop item: "
2813 "creative mode and no build privs"<<std::endl;
2817 // Calculate a position for it
2818 v3f pos = intToFloat(p_over, BS);
2820 pos.Y -= BS*0.25; // let it drop a bit
2822 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2823 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2828 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2832 infostream<<"WARNING: item resulted in NULL object, "
2833 <<"not placing onto map"
2838 actionstream<<player->getName()<<" places "<<item->getName()
2839 <<" at "<<PP(p_over)<<std::endl;
2841 // Add the object to the environment
2842 m_env.addActiveObject(obj);
2844 infostream<<"Placed object"<<std::endl;
2846 if(g_settings->getBool("creative_mode") == false)
2848 // Delete the right amount of items from the slot
2849 u16 dropcount = item->getDropCount();
2851 // Delete item if all gone
2852 if(item->getCount() <= dropcount)
2854 if(item->getCount() < dropcount)
2855 infostream<<"WARNING: Server: dropped more items"
2856 <<" than the slot contains"<<std::endl;
2858 InventoryList *ilist = player->inventory.getList("main");
2860 // Remove from inventory and send inventory
2861 ilist->deleteItem(item_i);
2863 // Else decrement it
2865 item->remove(dropcount);
2868 UpdateCrafting(peer_id);
2869 SendInventory(peer_id);
2877 Catch invalid actions
2881 infostream<<"WARNING: Server: Invalid action "
2882 <<action<<std::endl;
2886 else if(command == TOSERVER_RELEASE)
2895 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2898 else if(command == TOSERVER_SIGNTEXT)
2900 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2904 else if(command == TOSERVER_SIGNNODETEXT)
2906 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2914 std::string datastring((char*)&data[2], datasize-2);
2915 std::istringstream is(datastring, std::ios_base::binary);
2918 is.read((char*)buf, 6);
2919 v3s16 p = readV3S16(buf);
2920 is.read((char*)buf, 2);
2921 u16 textlen = readU16(buf);
2923 for(u16 i=0; i<textlen; i++)
2925 is.read((char*)buf, 1);
2926 text += (char)buf[0];
2929 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2932 if(meta->typeId() != CONTENT_SIGN_WALL)
2934 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2935 signmeta->setText(text);
2937 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
2938 <<" at "<<PP(p)<<std::endl;
2940 v3s16 blockpos = getNodeBlockPos(p);
2941 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2944 block->setChangedFlag();
2947 for(core::map<u16, RemoteClient*>::Iterator
2948 i = m_clients.getIterator();
2949 i.atEnd()==false; i++)
2951 RemoteClient *client = i.getNode()->getValue();
2952 client->SetBlockNotSent(blockpos);
2955 else if(command == TOSERVER_INVENTORY_ACTION)
2957 /*// Ignore inventory changes if in creative mode
2958 if(g_settings->getBool("creative_mode") == true)
2960 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2964 // Strip command and create a stream
2965 std::string datastring((char*)&data[2], datasize-2);
2966 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2967 std::istringstream is(datastring, std::ios_base::binary);
2969 InventoryAction *a = InventoryAction::deSerialize(is);
2974 c.current_player = player;
2977 Handle craftresult specially if not in creative mode
2979 bool disable_action = false;
2980 if(a->getType() == IACTION_MOVE
2981 && g_settings->getBool("creative_mode") == false)
2983 IMoveAction *ma = (IMoveAction*)a;
2984 if(ma->to_inv == "current_player" &&
2985 ma->from_inv == "current_player")
2987 InventoryList *rlist = player->inventory.getList("craftresult");
2989 InventoryList *clist = player->inventory.getList("craft");
2991 InventoryList *mlist = player->inventory.getList("main");
2994 Craftresult is no longer preview if something
2997 if(ma->to_list == "craftresult"
2998 && ma->from_list != "craftresult")
3000 // If it currently is a preview, remove
3002 if(player->craftresult_is_preview)
3004 rlist->deleteItem(0);
3006 player->craftresult_is_preview = false;
3009 Crafting takes place if this condition is true.
3011 if(player->craftresult_is_preview &&
3012 ma->from_list == "craftresult")
3014 player->craftresult_is_preview = false;
3015 clist->decrementMaterials(1);
3017 /* Print out action */
3018 InventoryList *list =
3019 player->inventory.getList("craftresult");
3021 InventoryItem *item = list->getItem(0);
3022 std::string itemname = "NULL";
3024 itemname = item->getName();
3025 actionstream<<player->getName()<<" crafts "
3026 <<itemname<<std::endl;
3029 If the craftresult is placed on itself, move it to
3030 main inventory instead of doing the action
3032 if(ma->to_list == "craftresult"
3033 && ma->from_list == "craftresult")
3035 disable_action = true;
3037 InventoryItem *item1 = rlist->changeItem(0, NULL);
3038 mlist->addItem(item1);
3041 // Disallow moving items if not allowed to build
3042 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3046 // if it's a locking chest, only allow the owner or server admins to move items
3047 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3049 Strfnd fn(ma->from_inv);
3050 std::string id0 = fn.next(":");
3051 if(id0 == "nodemeta")
3054 p.X = stoi(fn.next(","));
3055 p.Y = stoi(fn.next(","));
3056 p.Z = stoi(fn.next(","));
3057 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3058 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3059 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3060 if (lcm->getOwner() != player->getName())
3065 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3067 Strfnd fn(ma->to_inv);
3068 std::string id0 = fn.next(":");
3069 if(id0 == "nodemeta")
3072 p.X = stoi(fn.next(","));
3073 p.Y = stoi(fn.next(","));
3074 p.Z = stoi(fn.next(","));
3075 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3076 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3077 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3078 if (lcm->getOwner() != player->getName())
3085 if(disable_action == false)
3087 // Feed action to player inventory
3095 UpdateCrafting(player->peer_id);
3096 SendInventory(player->peer_id);
3101 infostream<<"TOSERVER_INVENTORY_ACTION: "
3102 <<"InventoryAction::deSerialize() returned NULL"
3106 else if(command == TOSERVER_CHAT_MESSAGE)
3114 std::string datastring((char*)&data[2], datasize-2);
3115 std::istringstream is(datastring, std::ios_base::binary);
3118 is.read((char*)buf, 2);
3119 u16 len = readU16(buf);
3121 std::wstring message;
3122 for(u16 i=0; i<len; i++)
3124 is.read((char*)buf, 2);
3125 message += (wchar_t)readU16(buf);
3128 // Get player name of this client
3129 std::wstring name = narrow_to_wide(player->getName());
3131 // Line to send to players
3133 // Whether to send to the player that sent the line
3134 bool send_to_sender = false;
3135 // Whether to send to other players
3136 bool send_to_others = false;
3138 // Local player gets all privileges regardless of
3139 // what's set on their account.
3140 u64 privs = getPlayerPrivs(player);
3143 if(message[0] == L'/')
3145 size_t strip_size = 1;
3146 if (message[1] == L'#') // support old-style commans
3148 message = message.substr(strip_size);
3150 WStrfnd f1(message);
3151 f1.next(L" "); // Skip over /#whatever
3152 std::wstring paramstring = f1.next(L"");
3154 ServerCommandContext *ctx = new ServerCommandContext(
3155 str_split(message, L' '),
3162 std::wstring reply(processServerCommand(ctx));
3163 send_to_sender = ctx->flags & SEND_TO_SENDER;
3164 send_to_others = ctx->flags & SEND_TO_OTHERS;
3166 if (ctx->flags & SEND_NO_PREFIX)
3169 line += L"Server: " + reply;
3176 if(privs & PRIV_SHOUT)
3182 send_to_others = true;
3186 line += L"Server: You are not allowed to shout";
3187 send_to_sender = true;
3194 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3197 Send the message to clients
3199 for(core::map<u16, RemoteClient*>::Iterator
3200 i = m_clients.getIterator();
3201 i.atEnd() == false; i++)
3203 // Get client and check that it is valid
3204 RemoteClient *client = i.getNode()->getValue();
3205 assert(client->peer_id == i.getNode()->getKey());
3206 if(client->serialization_version == SER_FMT_VER_INVALID)
3210 bool sender_selected = (peer_id == client->peer_id);
3211 if(sender_selected == true && send_to_sender == false)
3213 if(sender_selected == false && send_to_others == false)
3216 SendChatMessage(client->peer_id, line);
3220 else if(command == TOSERVER_DAMAGE)
3222 std::string datastring((char*)&data[2], datasize-2);
3223 std::istringstream is(datastring, std::ios_base::binary);
3224 u8 damage = readU8(is);
3226 if(g_settings->getBool("enable_damage"))
3228 actionstream<<player->getName()<<" damaged by "
3229 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3232 HandlePlayerHP(player, damage);
3236 SendPlayerHP(player);
3239 else if(command == TOSERVER_PASSWORD)
3242 [0] u16 TOSERVER_PASSWORD
3243 [2] u8[28] old password
3244 [30] u8[28] new password
3247 if(datasize != 2+PASSWORD_SIZE*2)
3249 /*char password[PASSWORD_SIZE];
3250 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3251 password[i] = data[2+i];
3252 password[PASSWORD_SIZE-1] = 0;*/
3254 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3262 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3264 char c = data[2+PASSWORD_SIZE+i];
3270 infostream<<"Server: Client requests a password change from "
3271 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3273 std::string playername = player->getName();
3275 if(m_authmanager.exists(playername) == false)
3277 infostream<<"Server: playername not found in authmanager"<<std::endl;
3278 // Wrong old password supplied!!
3279 SendChatMessage(peer_id, L"playername not found in authmanager");
3283 std::string checkpwd = m_authmanager.getPassword(playername);
3285 if(oldpwd != checkpwd)
3287 infostream<<"Server: invalid old password"<<std::endl;
3288 // Wrong old password supplied!!
3289 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3293 actionstream<<player->getName()<<" changes password"<<std::endl;
3295 m_authmanager.setPassword(playername, newpwd);
3297 infostream<<"Server: password change successful for "<<playername
3299 SendChatMessage(peer_id, L"Password change successful");
3301 else if(command == TOSERVER_PLAYERITEM)
3306 u16 item = readU16(&data[2]);
3307 player->wieldItem(item);
3308 SendWieldedItem(player);
3310 else if(command == TOSERVER_RESPAWN)
3315 RespawnPlayer(player);
3317 actionstream<<player->getName()<<" respawns at "
3318 <<PP(player->getPosition()/BS)<<std::endl;
3322 infostream<<"Server::ProcessData(): Ignoring "
3323 "unknown command "<<command<<std::endl;
3327 catch(SendFailedException &e)
3329 errorstream<<"Server::ProcessData(): SendFailedException: "
3335 void Server::onMapEditEvent(MapEditEvent *event)
3337 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3338 if(m_ignore_map_edit_events)
3340 MapEditEvent *e = event->clone();
3341 m_unsent_map_edit_queue.push_back(e);
3344 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3346 if(id == "current_player")
3348 assert(c->current_player);
3349 return &(c->current_player->inventory);
3353 std::string id0 = fn.next(":");
3355 if(id0 == "nodemeta")
3358 p.X = stoi(fn.next(","));
3359 p.Y = stoi(fn.next(","));
3360 p.Z = stoi(fn.next(","));
3361 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3363 return meta->getInventory();
3364 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3365 <<"no metadata found"<<std::endl;
3369 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3372 void Server::inventoryModified(InventoryContext *c, std::string id)
3374 if(id == "current_player")
3376 assert(c->current_player);
3378 UpdateCrafting(c->current_player->peer_id);
3379 SendInventory(c->current_player->peer_id);
3384 std::string id0 = fn.next(":");
3386 if(id0 == "nodemeta")
3389 p.X = stoi(fn.next(","));
3390 p.Y = stoi(fn.next(","));
3391 p.Z = stoi(fn.next(","));
3392 v3s16 blockpos = getNodeBlockPos(p);
3394 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3396 meta->inventoryModified();
3398 for(core::map<u16, RemoteClient*>::Iterator
3399 i = m_clients.getIterator();
3400 i.atEnd()==false; i++)
3402 RemoteClient *client = i.getNode()->getValue();
3403 client->SetBlockNotSent(blockpos);
3409 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3412 core::list<PlayerInfo> Server::getPlayerInfo()
3414 DSTACK(__FUNCTION_NAME);
3415 JMutexAutoLock envlock(m_env_mutex);
3416 JMutexAutoLock conlock(m_con_mutex);
3418 core::list<PlayerInfo> list;
3420 core::list<Player*> players = m_env.getPlayers();
3422 core::list<Player*>::Iterator i;
3423 for(i = players.begin();
3424 i != players.end(); i++)
3428 Player *player = *i;
3431 con::Peer *peer = m_con.GetPeer(player->peer_id);
3432 // Copy info from peer to info struct
3434 info.address = peer->address;
3435 info.avg_rtt = peer->avg_rtt;
3437 catch(con::PeerNotFoundException &e)
3439 // Set dummy peer info
3441 info.address = Address(0,0,0,0,0);
3445 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3446 info.position = player->getPosition();
3448 list.push_back(info);
3455 void Server::peerAdded(con::Peer *peer)
3457 DSTACK(__FUNCTION_NAME);
3458 infostream<<"Server::peerAdded(): peer->id="
3459 <<peer->id<<std::endl;
3462 c.type = PEER_ADDED;
3463 c.peer_id = peer->id;
3465 m_peer_change_queue.push_back(c);
3468 void Server::deletingPeer(con::Peer *peer, bool timeout)
3470 DSTACK(__FUNCTION_NAME);
3471 infostream<<"Server::deletingPeer(): peer->id="
3472 <<peer->id<<", timeout="<<timeout<<std::endl;
3475 c.type = PEER_REMOVED;
3476 c.peer_id = peer->id;
3477 c.timeout = timeout;
3478 m_peer_change_queue.push_back(c);
3485 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3487 DSTACK(__FUNCTION_NAME);
3488 std::ostringstream os(std::ios_base::binary);
3490 writeU16(os, TOCLIENT_HP);
3494 std::string s = os.str();
3495 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3497 con.Send(peer_id, 0, data, true);
3500 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3501 const std::wstring &reason)
3503 DSTACK(__FUNCTION_NAME);
3504 std::ostringstream os(std::ios_base::binary);
3506 writeU16(os, TOCLIENT_ACCESS_DENIED);
3507 os<<serializeWideString(reason);
3510 std::string s = os.str();
3511 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3513 con.Send(peer_id, 0, data, true);
3516 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3517 bool set_camera_point_target, v3f camera_point_target)
3519 DSTACK(__FUNCTION_NAME);
3520 std::ostringstream os(std::ios_base::binary);
3522 writeU16(os, TOCLIENT_DEATHSCREEN);
3523 writeU8(os, set_camera_point_target);
3524 writeV3F1000(os, camera_point_target);
3527 std::string s = os.str();
3528 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3530 con.Send(peer_id, 0, data, true);
3534 Non-static send methods
3537 void Server::SendObjectData(float dtime)
3539 DSTACK(__FUNCTION_NAME);
3541 core::map<v3s16, bool> stepped_blocks;
3543 for(core::map<u16, RemoteClient*>::Iterator
3544 i = m_clients.getIterator();
3545 i.atEnd() == false; i++)
3547 u16 peer_id = i.getNode()->getKey();
3548 RemoteClient *client = i.getNode()->getValue();
3549 assert(client->peer_id == peer_id);
3551 if(client->serialization_version == SER_FMT_VER_INVALID)
3554 client->SendObjectData(this, dtime, stepped_blocks);
3558 void Server::SendPlayerInfos()
3560 DSTACK(__FUNCTION_NAME);
3562 //JMutexAutoLock envlock(m_env_mutex);
3564 // Get connected players
3565 core::list<Player*> players = m_env.getPlayers(true);
3567 u32 player_count = players.getSize();
3568 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3570 SharedBuffer<u8> data(datasize);
3571 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3574 core::list<Player*>::Iterator i;
3575 for(i = players.begin();
3576 i != players.end(); i++)
3578 Player *player = *i;
3580 /*infostream<<"Server sending player info for player with "
3581 "peer_id="<<player->peer_id<<std::endl;*/
3583 writeU16(&data[start], player->peer_id);
3584 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3585 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3586 start += 2+PLAYERNAME_SIZE;
3589 //JMutexAutoLock conlock(m_con_mutex);
3592 m_con.SendToAll(0, data, true);
3595 void Server::SendInventory(u16 peer_id)
3597 DSTACK(__FUNCTION_NAME);
3599 Player* player = m_env.getPlayer(peer_id);
3606 std::ostringstream os;
3607 //os.imbue(std::locale("C"));
3609 player->inventory.serialize(os);
3611 std::string s = os.str();
3613 SharedBuffer<u8> data(s.size()+2);
3614 writeU16(&data[0], TOCLIENT_INVENTORY);
3615 memcpy(&data[2], s.c_str(), s.size());
3618 m_con.Send(peer_id, 0, data, true);
3621 std::string getWieldedItemString(const Player *player)
3623 const InventoryItem *item = player->getWieldItem();
3625 return std::string("");
3626 std::ostringstream os(std::ios_base::binary);
3627 item->serialize(os);
3631 void Server::SendWieldedItem(const Player* player)
3633 DSTACK(__FUNCTION_NAME);
3637 std::ostringstream os(std::ios_base::binary);
3639 writeU16(os, TOCLIENT_PLAYERITEM);
3641 writeU16(os, player->peer_id);
3642 os<<serializeString(getWieldedItemString(player));
3645 std::string s = os.str();
3646 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3648 m_con.SendToAll(0, data, true);
3651 void Server::SendPlayerItems()
3653 DSTACK(__FUNCTION_NAME);
3655 std::ostringstream os(std::ios_base::binary);
3656 core::list<Player *> players = m_env.getPlayers(true);
3658 writeU16(os, TOCLIENT_PLAYERITEM);
3659 writeU16(os, players.size());
3660 core::list<Player *>::Iterator i;
3661 for(i = players.begin(); i != players.end(); ++i)
3664 writeU16(os, p->peer_id);
3665 os<<serializeString(getWieldedItemString(p));
3669 std::string s = os.str();
3670 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3672 m_con.SendToAll(0, data, true);
3675 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3677 DSTACK(__FUNCTION_NAME);
3679 std::ostringstream os(std::ios_base::binary);
3683 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3684 os.write((char*)buf, 2);
3687 writeU16(buf, message.size());
3688 os.write((char*)buf, 2);
3691 for(u32 i=0; i<message.size(); i++)
3695 os.write((char*)buf, 2);
3699 std::string s = os.str();
3700 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3702 m_con.Send(peer_id, 0, data, true);
3705 void Server::BroadcastChatMessage(const std::wstring &message)
3707 for(core::map<u16, RemoteClient*>::Iterator
3708 i = m_clients.getIterator();
3709 i.atEnd() == false; i++)
3711 // Get client and check that it is valid
3712 RemoteClient *client = i.getNode()->getValue();
3713 assert(client->peer_id == i.getNode()->getKey());
3714 if(client->serialization_version == SER_FMT_VER_INVALID)
3717 SendChatMessage(client->peer_id, message);
3721 void Server::SendPlayerHP(Player *player)
3723 SendHP(m_con, player->peer_id, player->hp);
3726 void Server::SendMovePlayer(Player *player)
3728 DSTACK(__FUNCTION_NAME);
3729 std::ostringstream os(std::ios_base::binary);
3731 writeU16(os, TOCLIENT_MOVE_PLAYER);
3732 writeV3F1000(os, player->getPosition());
3733 writeF1000(os, player->getPitch());
3734 writeF1000(os, player->getYaw());
3737 v3f pos = player->getPosition();
3738 f32 pitch = player->getPitch();
3739 f32 yaw = player->getYaw();
3740 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3741 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3748 std::string s = os.str();
3749 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3751 m_con.Send(player->peer_id, 0, data, true);
3754 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3755 core::list<u16> *far_players, float far_d_nodes)
3757 float maxd = far_d_nodes*BS;
3758 v3f p_f = intToFloat(p, BS);
3762 SharedBuffer<u8> reply(replysize);
3763 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3764 writeS16(&reply[2], p.X);
3765 writeS16(&reply[4], p.Y);
3766 writeS16(&reply[6], p.Z);
3768 for(core::map<u16, RemoteClient*>::Iterator
3769 i = m_clients.getIterator();
3770 i.atEnd() == false; i++)
3772 // Get client and check that it is valid
3773 RemoteClient *client = i.getNode()->getValue();
3774 assert(client->peer_id == i.getNode()->getKey());
3775 if(client->serialization_version == SER_FMT_VER_INVALID)
3778 // Don't send if it's the same one
3779 if(client->peer_id == ignore_id)
3785 Player *player = m_env.getPlayer(client->peer_id);
3788 // If player is far away, only set modified blocks not sent
3789 v3f player_pos = player->getPosition();
3790 if(player_pos.getDistanceFrom(p_f) > maxd)
3792 far_players->push_back(client->peer_id);
3799 m_con.Send(client->peer_id, 0, reply, true);
3803 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3804 core::list<u16> *far_players, float far_d_nodes)
3806 float maxd = far_d_nodes*BS;
3807 v3f p_f = intToFloat(p, BS);
3809 for(core::map<u16, RemoteClient*>::Iterator
3810 i = m_clients.getIterator();
3811 i.atEnd() == false; i++)
3813 // Get client and check that it is valid
3814 RemoteClient *client = i.getNode()->getValue();
3815 assert(client->peer_id == i.getNode()->getKey());
3816 if(client->serialization_version == SER_FMT_VER_INVALID)
3819 // Don't send if it's the same one
3820 if(client->peer_id == ignore_id)
3826 Player *player = m_env.getPlayer(client->peer_id);
3829 // If player is far away, only set modified blocks not sent
3830 v3f player_pos = player->getPosition();
3831 if(player_pos.getDistanceFrom(p_f) > maxd)
3833 far_players->push_back(client->peer_id);
3840 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3841 SharedBuffer<u8> reply(replysize);
3842 writeU16(&reply[0], TOCLIENT_ADDNODE);
3843 writeS16(&reply[2], p.X);
3844 writeS16(&reply[4], p.Y);
3845 writeS16(&reply[6], p.Z);
3846 n.serialize(&reply[8], client->serialization_version);
3849 m_con.Send(client->peer_id, 0, reply, true);
3853 void Server::setBlockNotSent(v3s16 p)
3855 for(core::map<u16, RemoteClient*>::Iterator
3856 i = m_clients.getIterator();
3857 i.atEnd()==false; i++)
3859 RemoteClient *client = i.getNode()->getValue();
3860 client->SetBlockNotSent(p);
3864 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3866 DSTACK(__FUNCTION_NAME);
3868 v3s16 p = block->getPos();
3872 bool completely_air = true;
3873 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3874 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3875 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3877 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3879 completely_air = false;
3880 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3885 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3887 infostream<<"[completely air] ";
3888 infostream<<std::endl;
3892 Create a packet with the block in the right format
3895 std::ostringstream os(std::ios_base::binary);
3896 block->serialize(os, ver);
3897 std::string s = os.str();
3898 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3900 u32 replysize = 8 + blockdata.getSize();
3901 SharedBuffer<u8> reply(replysize);
3902 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3903 writeS16(&reply[2], p.X);
3904 writeS16(&reply[4], p.Y);
3905 writeS16(&reply[6], p.Z);
3906 memcpy(&reply[8], *blockdata, blockdata.getSize());
3908 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3909 <<": \tpacket size: "<<replysize<<std::endl;*/
3914 m_con.Send(peer_id, 1, reply, true);
3917 void Server::SendBlocks(float dtime)
3919 DSTACK(__FUNCTION_NAME);
3921 JMutexAutoLock envlock(m_env_mutex);
3922 JMutexAutoLock conlock(m_con_mutex);
3924 //TimeTaker timer("Server::SendBlocks");
3926 core::array<PrioritySortedBlockTransfer> queue;
3928 s32 total_sending = 0;
3931 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3933 for(core::map<u16, RemoteClient*>::Iterator
3934 i = m_clients.getIterator();
3935 i.atEnd() == false; i++)
3937 RemoteClient *client = i.getNode()->getValue();
3938 assert(client->peer_id == i.getNode()->getKey());
3940 total_sending += client->SendingCount();
3942 if(client->serialization_version == SER_FMT_VER_INVALID)
3945 client->GetNextBlocks(this, dtime, queue);
3950 // Lowest priority number comes first.
3951 // Lowest is most important.
3954 for(u32 i=0; i<queue.size(); i++)
3956 //TODO: Calculate limit dynamically
3957 if(total_sending >= g_settings->getS32
3958 ("max_simultaneous_block_sends_server_total"))
3961 PrioritySortedBlockTransfer q = queue[i];
3963 MapBlock *block = NULL;
3966 block = m_env.getMap().getBlockNoCreate(q.pos);
3968 catch(InvalidPositionException &e)
3973 RemoteClient *client = getClient(q.peer_id);
3975 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3977 client->SentBlock(q.pos);
3987 void Server::HandlePlayerHP(Player *player, s16 damage)
3989 if(player->hp > damage)
3991 player->hp -= damage;
3992 SendPlayerHP(player);
3996 infostream<<"Server::HandlePlayerHP(): Player "
3997 <<player->getName()<<" dies"<<std::endl;
4001 //TODO: Throw items around
4003 // Handle players that are not connected
4004 if(player->peer_id == PEER_ID_INEXISTENT){
4005 RespawnPlayer(player);
4009 SendPlayerHP(player);
4011 RemoteClient *client = getClient(player->peer_id);
4012 if(client->net_proto_version >= 3)
4014 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4018 RespawnPlayer(player);
4023 void Server::RespawnPlayer(Player *player)
4025 v3f pos = findSpawnPos(m_env.getServerMap());
4026 player->setPosition(pos);
4028 SendMovePlayer(player);
4029 SendPlayerHP(player);
4032 void Server::UpdateCrafting(u16 peer_id)
4034 DSTACK(__FUNCTION_NAME);
4036 Player* player = m_env.getPlayer(peer_id);
4040 Calculate crafting stuff
4042 if(g_settings->getBool("creative_mode") == false)
4044 InventoryList *clist = player->inventory.getList("craft");
4045 InventoryList *rlist = player->inventory.getList("craftresult");
4047 if(rlist && rlist->getUsedSlots() == 0)
4048 player->craftresult_is_preview = true;
4050 if(rlist && player->craftresult_is_preview)
4052 rlist->clearItems();
4054 if(clist && rlist && player->craftresult_is_preview)
4056 InventoryItem *items[9];
4057 for(u16 i=0; i<9; i++)
4059 items[i] = clist->getItem(i);
4062 // Get result of crafting grid
4063 InventoryItem *result = craft_get_result(items);
4065 rlist->addItem(result);
4068 } // if creative_mode == false
4071 RemoteClient* Server::getClient(u16 peer_id)
4073 DSTACK(__FUNCTION_NAME);
4074 //JMutexAutoLock lock(m_con_mutex);
4075 core::map<u16, RemoteClient*>::Node *n;
4076 n = m_clients.find(peer_id);
4077 // A client should exist for all peers
4079 return n->getValue();
4082 std::wstring Server::getStatusString()
4084 std::wostringstream os(std::ios_base::binary);
4087 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4089 os<<L", uptime="<<m_uptime.get();
4090 // Information about clients
4092 for(core::map<u16, RemoteClient*>::Iterator
4093 i = m_clients.getIterator();
4094 i.atEnd() == false; i++)
4096 // Get client and check that it is valid
4097 RemoteClient *client = i.getNode()->getValue();
4098 assert(client->peer_id == i.getNode()->getKey());
4099 if(client->serialization_version == SER_FMT_VER_INVALID)
4102 Player *player = m_env.getPlayer(client->peer_id);
4103 // Get name of player
4104 std::wstring name = L"unknown";
4106 name = narrow_to_wide(player->getName());
4107 // Add name to information string
4111 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4112 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4113 if(g_settings->get("motd") != "")
4114 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4118 // Saves g_settings to configpath given at initialization
4119 void Server::saveConfig()
4121 if(m_configpath != "")
4122 g_settings->updateConfigFile(m_configpath.c_str());
4125 void Server::notifyPlayer(const char *name, const std::wstring msg)
4127 Player *player = m_env.getPlayer(name);
4130 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4133 void Server::notifyPlayers(const std::wstring msg)
4135 BroadcastChatMessage(msg);
4138 v3f findSpawnPos(ServerMap &map)
4140 //return v3f(50,50,50)*BS;
4143 s16 groundheight = 0;
4146 nodepos = v2s16(0,0);
4151 // Try to find a good place a few times
4152 for(s32 i=0; i<1000; i++)
4155 // We're going to try to throw the player to this position
4156 nodepos = v2s16(-range + (myrand()%(range*2)),
4157 -range + (myrand()%(range*2)));
4158 v2s16 sectorpos = getNodeSectorPos(nodepos);
4159 // Get sector (NOTE: Don't get because it's slow)
4160 //m_env.getMap().emergeSector(sectorpos);
4161 // Get ground height at point (fallbacks to heightmap function)
4162 groundheight = map.findGroundLevel(nodepos);
4163 // Don't go underwater
4164 if(groundheight < WATER_LEVEL)
4166 //infostream<<"-> Underwater"<<std::endl;
4169 // Don't go to high places
4170 if(groundheight > WATER_LEVEL + 4)
4172 //infostream<<"-> Underwater"<<std::endl;
4176 // Found a good place
4177 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4182 // If no suitable place was not found, go above water at least.
4183 if(groundheight < WATER_LEVEL)
4184 groundheight = WATER_LEVEL;
4186 return intToFloat(v3s16(
4193 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4196 Try to get an existing player
4198 Player *player = m_env.getPlayer(name);
4201 // If player is already connected, cancel
4202 if(player->peer_id != 0)
4204 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4209 player->peer_id = peer_id;
4211 // Reset inventory to creative if in creative mode
4212 if(g_settings->getBool("creative_mode"))
4214 // Warning: double code below
4215 // Backup actual inventory
4216 player->inventory_backup = new Inventory();
4217 *(player->inventory_backup) = player->inventory;
4218 // Set creative inventory
4219 craft_set_creative_inventory(player);
4226 If player with the wanted peer_id already exists, cancel.
4228 if(m_env.getPlayer(peer_id) != NULL)
4230 infostream<<"emergePlayer(): Player with wrong name but same"
4231 " peer_id already exists"<<std::endl;
4239 player = new ServerRemotePlayer();
4240 //player->peer_id = c.peer_id;
4241 //player->peer_id = PEER_ID_INEXISTENT;
4242 player->peer_id = peer_id;
4243 player->updateName(name);
4244 m_authmanager.add(name);
4245 m_authmanager.setPassword(name, password);
4246 m_authmanager.setPrivs(name,
4247 stringToPrivs(g_settings->get("default_privs")));
4253 infostream<<"Server: Finding spawn place for player \""
4254 <<player->getName()<<"\""<<std::endl;
4256 v3f pos = findSpawnPos(m_env.getServerMap());
4258 player->setPosition(pos);
4261 Add player to environment
4264 m_env.addPlayer(player);
4267 Add stuff to inventory
4270 if(g_settings->getBool("creative_mode"))
4272 // Warning: double code above
4273 // Backup actual inventory
4274 player->inventory_backup = new Inventory();
4275 *(player->inventory_backup) = player->inventory;
4276 // Set creative inventory
4277 craft_set_creative_inventory(player);
4279 else if(g_settings->getBool("give_initial_stuff"))
4281 craft_give_initial_stuff(player);
4286 } // create new player
4289 void Server::handlePeerChange(PeerChange &c)
4291 JMutexAutoLock envlock(m_env_mutex);
4292 JMutexAutoLock conlock(m_con_mutex);
4294 if(c.type == PEER_ADDED)
4301 core::map<u16, RemoteClient*>::Node *n;
4302 n = m_clients.find(c.peer_id);
4303 // The client shouldn't already exist
4307 RemoteClient *client = new RemoteClient();
4308 client->peer_id = c.peer_id;
4309 m_clients.insert(client->peer_id, client);
4312 else if(c.type == PEER_REMOVED)
4319 core::map<u16, RemoteClient*>::Node *n;
4320 n = m_clients.find(c.peer_id);
4321 // The client should exist
4325 Mark objects to be not known by the client
4327 RemoteClient *client = n->getValue();
4329 for(core::map<u16, bool>::Iterator
4330 i = client->m_known_objects.getIterator();
4331 i.atEnd()==false; i++)
4334 u16 id = i.getNode()->getKey();
4335 ServerActiveObject* obj = m_env.getActiveObject(id);
4337 if(obj && obj->m_known_by_count > 0)
4338 obj->m_known_by_count--;
4341 // Collect information about leaving in chat
4342 std::wstring message;
4344 Player *player = m_env.getPlayer(c.peer_id);
4347 std::wstring name = narrow_to_wide(player->getName());
4350 message += L" left game";
4352 message += L" (timed out)";
4358 m_env.removePlayer(c.peer_id);
4361 // Set player client disconnected
4363 Player *player = m_env.getPlayer(c.peer_id);
4365 player->peer_id = 0;
4372 std::ostringstream os(std::ios_base::binary);
4373 for(core::map<u16, RemoteClient*>::Iterator
4374 i = m_clients.getIterator();
4375 i.atEnd() == false; i++)
4377 RemoteClient *client = i.getNode()->getValue();
4378 assert(client->peer_id == i.getNode()->getKey());
4379 if(client->serialization_version == SER_FMT_VER_INVALID)
4382 Player *player = m_env.getPlayer(client->peer_id);
4385 // Get name of player
4386 os<<player->getName()<<" ";
4389 actionstream<<player->getName()<<" "
4390 <<(c.timeout?"times out.":"leaves game.")
4391 <<" List of players: "
4392 <<os.str()<<std::endl;
4397 delete m_clients[c.peer_id];
4398 m_clients.remove(c.peer_id);
4400 // Send player info to all remaining clients
4403 // Send leave chat message to all remaining clients
4404 BroadcastChatMessage(message);
4413 void Server::handlePeerChanges()
4415 while(m_peer_change_queue.size() > 0)
4417 PeerChange c = m_peer_change_queue.pop_front();
4419 infostream<<"Server: Handling peer change: "
4420 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4423 handlePeerChange(c);
4427 u64 Server::getPlayerPrivs(Player *player)
4431 std::string playername = player->getName();
4432 // Local player gets all privileges regardless of
4433 // what's set on their account.
4434 if(g_settings->get("name") == playername)
4440 return getPlayerAuthPrivs(playername);
4444 void dedicated_server_loop(Server &server, bool &kill)
4446 DSTACK(__FUNCTION_NAME);
4448 infostream<<DTIME<<std::endl;
4449 infostream<<"========================"<<std::endl;
4450 infostream<<"Running dedicated server"<<std::endl;
4451 infostream<<"========================"<<std::endl;
4452 infostream<<std::endl;
4454 IntervalLimiter m_profiler_interval;
4458 // This is kind of a hack but can be done like this
4459 // because server.step() is very light
4461 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4466 if(server.getShutdownRequested() || kill)
4468 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4475 float profiler_print_interval =
4476 g_settings->getFloat("profiler_print_interval");
4477 if(profiler_print_interval != 0)
4479 if(m_profiler_interval.step(0.030, profiler_print_interval))
4481 infostream<<"Profiler:"<<std::endl;
4482 g_profiler->print(infostream);
4483 g_profiler->clear();
4490 static int counter = 0;
4496 core::list<PlayerInfo> list = server.getPlayerInfo();
4497 core::list<PlayerInfo>::Iterator i;
4498 static u32 sum_old = 0;
4499 u32 sum = PIChecksum(list);
4502 infostream<<DTIME<<"Player info:"<<std::endl;
4503 for(i=list.begin(); i!=list.end(); i++)
4505 i->PrintLine(&infostream);