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);
1827 Address address = m_con.GetPeerAddress(peer_id);
1829 // drop player if is ip is banned
1830 if(m_banmanager.isIpBanned(address.serializeString())){
1831 SendAccessDenied(m_con, peer_id,
1832 L"Your ip is banned. Banned name was "
1833 +narrow_to_wide(m_banmanager.getBanName(
1834 address.serializeString())));
1835 m_con.DeletePeer(peer_id);
1839 catch(con::PeerNotFoundException &e)
1841 infostream<<"Server::ProcessData(): Cancelling: peer "
1842 <<peer_id<<" not found"<<std::endl;
1846 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1854 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1856 if(command == TOSERVER_INIT)
1858 // [0] u16 TOSERVER_INIT
1859 // [2] u8 SER_FMT_VER_HIGHEST
1860 // [3] u8[20] player_name
1861 // [23] u8[28] password <--- can be sent without this, from old versions
1863 if(datasize < 2+1+PLAYERNAME_SIZE)
1866 infostream<<"Server: Got TOSERVER_INIT from "
1867 <<peer_id<<std::endl;
1869 // First byte after command is maximum supported
1870 // serialization version
1871 u8 client_max = data[2];
1872 u8 our_max = SER_FMT_VER_HIGHEST;
1873 // Use the highest version supported by both
1874 u8 deployed = core::min_(client_max, our_max);
1875 // If it's lower than the lowest supported, give up.
1876 if(deployed < SER_FMT_VER_LOWEST)
1877 deployed = SER_FMT_VER_INVALID;
1879 //peer->serialization_version = deployed;
1880 getClient(peer_id)->pending_serialization_version = deployed;
1882 if(deployed == SER_FMT_VER_INVALID)
1884 infostream<<"Server: Cannot negotiate "
1885 "serialization version with peer "
1886 <<peer_id<<std::endl;
1887 SendAccessDenied(m_con, peer_id,
1888 L"Your client is too old (map format)");
1893 Read and check network protocol version
1896 u16 net_proto_version = 0;
1897 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1899 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1902 getClient(peer_id)->net_proto_version = net_proto_version;
1904 if(net_proto_version == 0)
1906 SendAccessDenied(m_con, peer_id,
1907 L"Your client is too old. Please upgrade.");
1911 /* Uhh... this should actually be a warning but let's do it like this */
1912 if(g_settings->getBool("strict_protocol_version_checking"))
1914 if(net_proto_version < PROTOCOL_VERSION)
1916 SendAccessDenied(m_con, peer_id,
1917 L"Your client is too old. Please upgrade.");
1927 char playername[PLAYERNAME_SIZE];
1928 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1930 playername[i] = data[3+i];
1932 playername[PLAYERNAME_SIZE-1] = 0;
1934 if(playername[0]=='\0')
1936 infostream<<"Server: Player has empty name"<<std::endl;
1937 SendAccessDenied(m_con, peer_id,
1942 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1944 infostream<<"Server: Player has invalid name"<<std::endl;
1945 SendAccessDenied(m_con, peer_id,
1946 L"Name contains unallowed characters");
1951 char password[PASSWORD_SIZE];
1952 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1954 // old version - assume blank password
1959 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1961 password[i] = data[23+i];
1963 password[PASSWORD_SIZE-1] = 0;
1966 std::string checkpwd;
1967 if(m_authmanager.exists(playername))
1969 checkpwd = m_authmanager.getPassword(playername);
1973 checkpwd = g_settings->get("default_password");
1976 /*infostream<<"Server: Client gave password '"<<password
1977 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
1979 if(password != checkpwd && m_authmanager.exists(playername))
1981 infostream<<"Server: peer_id="<<peer_id
1982 <<": supplied invalid password for "
1983 <<playername<<std::endl;
1984 SendAccessDenied(m_con, peer_id, L"Invalid password");
1988 // Add player to auth manager
1989 if(m_authmanager.exists(playername) == false)
1991 infostream<<"Server: adding player "<<playername
1992 <<" to auth manager"<<std::endl;
1993 m_authmanager.add(playername);
1994 m_authmanager.setPassword(playername, checkpwd);
1995 m_authmanager.setPrivs(playername,
1996 stringToPrivs(g_settings->get("default_privs")));
1997 m_authmanager.save();
2000 // Enforce user limit.
2001 // Don't enforce for users that have some admin right
2002 if(m_clients.size() >= g_settings->getU16("max_users") &&
2003 (m_authmanager.getPrivs(playername)
2004 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2005 playername != g_settings->get("name"))
2007 SendAccessDenied(m_con, peer_id, L"Too many users.");
2012 Player *player = emergePlayer(playername, password, peer_id);
2014 // If failed, cancel
2017 infostream<<"Server: peer_id="<<peer_id
2018 <<": failed to emerge player"<<std::endl;
2023 Answer with a TOCLIENT_INIT
2026 SharedBuffer<u8> reply(2+1+6+8);
2027 writeU16(&reply[0], TOCLIENT_INIT);
2028 writeU8(&reply[2], deployed);
2029 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2030 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2033 m_con.Send(peer_id, 0, reply, true);
2037 Send complete position information
2039 SendMovePlayer(player);
2044 if(command == TOSERVER_INIT2)
2046 infostream<<"Server: Got TOSERVER_INIT2 from "
2047 <<peer_id<<std::endl;
2050 getClient(peer_id)->serialization_version
2051 = getClient(peer_id)->pending_serialization_version;
2054 Send some initialization data
2057 // Send player info to all players
2060 // Send inventory to player
2061 UpdateCrafting(peer_id);
2062 SendInventory(peer_id);
2064 // Send player items to all players
2067 Player *player = m_env.getPlayer(peer_id);
2070 SendPlayerHP(player);
2074 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2075 m_env.getTimeOfDay());
2076 m_con.Send(peer_id, 0, data, true);
2079 // Send information about server to player in chat
2080 SendChatMessage(peer_id, getStatusString());
2082 // Send information about joining in chat
2084 std::wstring name = L"unknown";
2085 Player *player = m_env.getPlayer(peer_id);
2087 name = narrow_to_wide(player->getName());
2089 std::wstring message;
2092 message += L" joined game";
2093 BroadcastChatMessage(message);
2096 // Warnings about protocol version can be issued here
2097 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2099 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2103 Check HP, respawn if necessary
2105 HandlePlayerHP(player, 0);
2111 std::ostringstream os(std::ios_base::binary);
2112 for(core::map<u16, RemoteClient*>::Iterator
2113 i = m_clients.getIterator();
2114 i.atEnd() == false; i++)
2116 RemoteClient *client = i.getNode()->getValue();
2117 assert(client->peer_id == i.getNode()->getKey());
2118 if(client->serialization_version == SER_FMT_VER_INVALID)
2121 Player *player = m_env.getPlayer(client->peer_id);
2124 // Get name of player
2125 os<<player->getName()<<" ";
2128 actionstream<<player->getName()<<" joins game. List of players: "
2129 <<os.str()<<std::endl;
2135 if(peer_ser_ver == SER_FMT_VER_INVALID)
2137 infostream<<"Server::ProcessData(): Cancelling: Peer"
2138 " serialization format invalid or not initialized."
2139 " Skipping incoming command="<<command<<std::endl;
2143 Player *player = m_env.getPlayer(peer_id);
2146 infostream<<"Server::ProcessData(): Cancelling: "
2147 "No player for peer_id="<<peer_id
2151 if(command == TOSERVER_PLAYERPOS)
2153 if(datasize < 2+12+12+4+4)
2157 v3s32 ps = readV3S32(&data[start+2]);
2158 v3s32 ss = readV3S32(&data[start+2+12]);
2159 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2160 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2161 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2162 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2163 pitch = wrapDegrees(pitch);
2164 yaw = wrapDegrees(yaw);
2166 player->setPosition(position);
2167 player->setSpeed(speed);
2168 player->setPitch(pitch);
2169 player->setYaw(yaw);
2171 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2172 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2173 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2175 else if(command == TOSERVER_GOTBLOCKS)
2188 u16 count = data[2];
2189 for(u16 i=0; i<count; i++)
2191 if((s16)datasize < 2+1+(i+1)*6)
2192 throw con::InvalidIncomingDataException
2193 ("GOTBLOCKS length is too short");
2194 v3s16 p = readV3S16(&data[2+1+i*6]);
2195 /*infostream<<"Server: GOTBLOCKS ("
2196 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2197 RemoteClient *client = getClient(peer_id);
2198 client->GotBlock(p);
2201 else if(command == TOSERVER_DELETEDBLOCKS)
2214 u16 count = data[2];
2215 for(u16 i=0; i<count; i++)
2217 if((s16)datasize < 2+1+(i+1)*6)
2218 throw con::InvalidIncomingDataException
2219 ("DELETEDBLOCKS length is too short");
2220 v3s16 p = readV3S16(&data[2+1+i*6]);
2221 /*infostream<<"Server: DELETEDBLOCKS ("
2222 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2223 RemoteClient *client = getClient(peer_id);
2224 client->SetBlockNotSent(p);
2227 else if(command == TOSERVER_CLICK_OBJECT)
2229 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2232 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2237 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2243 [2] u8 button (0=left, 1=right)
2247 u8 button = readU8(&data[2]);
2248 u16 id = readS16(&data[3]);
2249 u16 item_i = readU16(&data[5]);
2251 ServerActiveObject *obj = m_env.getActiveObject(id);
2255 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2260 // Skip if object has been removed
2264 //TODO: Check that object is reasonably close
2266 // Left click, pick object up (usually)
2270 Try creating inventory item
2272 InventoryItem *item = obj->createPickedUpItem();
2276 InventoryList *ilist = player->inventory.getList("main");
2279 actionstream<<player->getName()<<" picked up "
2280 <<item->getName()<<std::endl;
2281 if(g_settings->getBool("creative_mode") == false)
2283 // Skip if inventory has no free space
2284 if(ilist->roomForItem(item) == false)
2286 infostream<<"Player inventory has no free space"<<std::endl;
2290 // Add to inventory and send inventory
2291 ilist->addItem(item);
2292 UpdateCrafting(player->peer_id);
2293 SendInventory(player->peer_id);
2296 // Remove object from environment
2297 obj->m_removed = true;
2303 Item cannot be picked up. Punch it instead.
2306 actionstream<<player->getName()<<" punches object "
2307 <<obj->getId()<<std::endl;
2309 ToolItem *titem = NULL;
2310 std::string toolname = "";
2312 InventoryList *mlist = player->inventory.getList("main");
2315 InventoryItem *item = mlist->getItem(item_i);
2316 if(item && (std::string)item->getName() == "ToolItem")
2318 titem = (ToolItem*)item;
2319 toolname = titem->getToolName();
2323 v3f playerpos = player->getPosition();
2324 v3f objpos = obj->getBasePosition();
2325 v3f dir = (objpos - playerpos).normalize();
2327 u16 wear = obj->punch(toolname, dir, player->getName());
2331 bool weared_out = titem->addWear(wear);
2333 mlist->deleteItem(item_i);
2334 SendInventory(player->peer_id);
2338 // Right click, do something with object
2341 actionstream<<player->getName()<<" right clicks object "
2342 <<obj->getId()<<std::endl;
2344 // Track hp changes super-crappily
2345 u16 oldhp = player->hp;
2348 obj->rightClick(player);
2351 if(player->hp != oldhp)
2353 SendPlayerHP(player);
2357 else if(command == TOSERVER_GROUND_ACTION)
2365 [3] v3s16 nodepos_undersurface
2366 [9] v3s16 nodepos_abovesurface
2371 2: stop digging (all parameters ignored)
2372 3: digging completed
2374 u8 action = readU8(&data[2]);
2376 p_under.X = readS16(&data[3]);
2377 p_under.Y = readS16(&data[5]);
2378 p_under.Z = readS16(&data[7]);
2380 p_over.X = readS16(&data[9]);
2381 p_over.Y = readS16(&data[11]);
2382 p_over.Z = readS16(&data[13]);
2383 u16 item_i = readU16(&data[15]);
2385 //TODO: Check that target is reasonably close
2393 NOTE: This can be used in the future to check if
2394 somebody is cheating, by checking the timing.
2401 else if(action == 2)
2404 RemoteClient *client = getClient(peer_id);
2405 JMutexAutoLock digmutex(client->m_dig_mutex);
2406 client->m_dig_tool_item = -1;
2411 3: Digging completed
2413 else if(action == 3)
2415 // Mandatory parameter; actually used for nothing
2416 core::map<v3s16, MapBlock*> modified_blocks;
2418 content_t material = CONTENT_IGNORE;
2419 u8 mineral = MINERAL_NONE;
2421 bool cannot_remove_node = false;
2425 MapNode n = m_env.getMap().getNode(p_under);
2427 mineral = n.getMineral();
2428 // Get material at position
2429 material = n.getContent();
2430 // If not yet cancelled
2431 if(cannot_remove_node == false)
2433 // If it's not diggable, do nothing
2434 if(content_diggable(material) == false)
2436 infostream<<"Server: Not finishing digging: "
2437 <<"Node not diggable"
2439 cannot_remove_node = true;
2442 // If not yet cancelled
2443 if(cannot_remove_node == false)
2445 // Get node metadata
2446 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2447 if(meta && meta->nodeRemovalDisabled() == true)
2449 infostream<<"Server: Not finishing digging: "
2450 <<"Node metadata disables removal"
2452 cannot_remove_node = true;
2456 catch(InvalidPositionException &e)
2458 infostream<<"Server: Not finishing digging: Node not found."
2459 <<" Adding block to emerge queue."
2461 m_emerge_queue.addBlock(peer_id,
2462 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2463 cannot_remove_node = true;
2466 // Make sure the player is allowed to do it
2467 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2469 infostream<<"Player "<<player->getName()<<" cannot remove node"
2470 <<" because privileges are "<<getPlayerPrivs(player)
2472 cannot_remove_node = true;
2476 If node can't be removed, set block to be re-sent to
2479 if(cannot_remove_node)
2481 infostream<<"Server: Not finishing digging."<<std::endl;
2483 // Client probably has wrong data.
2484 // Set block not sent, so that client will get
2486 infostream<<"Client "<<peer_id<<" tried to dig "
2487 <<"node; but node cannot be removed."
2488 <<" setting MapBlock not sent."<<std::endl;
2489 RemoteClient *client = getClient(peer_id);
2490 v3s16 blockpos = getNodeBlockPos(p_under);
2491 client->SetBlockNotSent(blockpos);
2496 actionstream<<player->getName()<<" digs "<<PP(p_under)
2497 <<", gets material "<<(int)material<<", mineral "
2498 <<(int)mineral<<std::endl;
2501 Send the removal to all close-by players.
2502 - If other player is close, send REMOVENODE
2503 - Otherwise set blocks not sent
2505 core::list<u16> far_players;
2506 sendRemoveNode(p_under, peer_id, &far_players, 30);
2509 Update and send inventory
2512 if(g_settings->getBool("creative_mode") == false)
2517 InventoryList *mlist = player->inventory.getList("main");
2520 InventoryItem *item = mlist->getItem(item_i);
2521 if(item && (std::string)item->getName() == "ToolItem")
2523 ToolItem *titem = (ToolItem*)item;
2524 std::string toolname = titem->getToolName();
2526 // Get digging properties for material and tool
2527 DiggingProperties prop =
2528 getDiggingProperties(material, toolname);
2530 if(prop.diggable == false)
2532 infostream<<"Server: WARNING: Player digged"
2533 <<" with impossible material + tool"
2534 <<" combination"<<std::endl;
2537 bool weared_out = titem->addWear(prop.wear);
2541 mlist->deleteItem(item_i);
2547 Add dug item to inventory
2550 InventoryItem *item = NULL;
2552 if(mineral != MINERAL_NONE)
2553 item = getDiggedMineralItem(mineral);
2558 std::string &dug_s = content_features(material).dug_item;
2561 std::istringstream is(dug_s, std::ios::binary);
2562 item = InventoryItem::deSerialize(is);
2568 // Add a item to inventory
2569 player->inventory.addItem("main", item);
2572 UpdateCrafting(player->peer_id);
2573 SendInventory(player->peer_id);
2578 if(mineral != MINERAL_NONE)
2579 item = getDiggedMineralItem(mineral);
2584 std::string &extra_dug_s = content_features(material).extra_dug_item;
2585 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2586 if(extra_dug_s != "" && extra_rarity != 0
2587 && myrand() % extra_rarity == 0)
2589 std::istringstream is(extra_dug_s, std::ios::binary);
2590 item = InventoryItem::deSerialize(is);
2596 // Add a item to inventory
2597 player->inventory.addItem("main", item);
2600 UpdateCrafting(player->peer_id);
2601 SendInventory(player->peer_id);
2607 (this takes some time so it is done after the quick stuff)
2610 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2612 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2615 Set blocks not sent to far players
2617 for(core::list<u16>::Iterator
2618 i = far_players.begin();
2619 i != far_players.end(); i++)
2622 RemoteClient *client = getClient(peer_id);
2625 client->SetBlocksNotSent(modified_blocks);
2632 else if(action == 1)
2635 InventoryList *ilist = player->inventory.getList("main");
2640 InventoryItem *item = ilist->getItem(item_i);
2642 // If there is no item, it is not possible to add it anywhere
2647 Handle material items
2649 if(std::string("MaterialItem") == item->getName())
2652 // Don't add a node if this is not a free space
2653 MapNode n2 = m_env.getMap().getNode(p_over);
2654 bool no_enough_privs =
2655 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2657 infostream<<"Player "<<player->getName()<<" cannot add node"
2658 <<" because privileges are "<<getPlayerPrivs(player)
2661 if(content_features(n2).buildable_to == false
2664 // Client probably has wrong data.
2665 // Set block not sent, so that client will get
2667 infostream<<"Client "<<peer_id<<" tried to place"
2668 <<" node in invalid position; setting"
2669 <<" MapBlock not sent."<<std::endl;
2670 RemoteClient *client = getClient(peer_id);
2671 v3s16 blockpos = getNodeBlockPos(p_over);
2672 client->SetBlockNotSent(blockpos);
2676 catch(InvalidPositionException &e)
2678 infostream<<"Server: Ignoring ADDNODE: Node not found"
2679 <<" Adding block to emerge queue."
2681 m_emerge_queue.addBlock(peer_id,
2682 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2686 // Reset build time counter
2687 getClient(peer_id)->m_time_from_building = 0.0;
2690 MaterialItem *mitem = (MaterialItem*)item;
2692 n.setContent(mitem->getMaterial());
2694 actionstream<<player->getName()<<" places material "
2695 <<(int)mitem->getMaterial()
2696 <<" at "<<PP(p_under)<<std::endl;
2698 // Calculate direction for wall mounted stuff
2699 if(content_features(n).wall_mounted)
2700 n.param2 = packDir(p_under - p_over);
2702 // Calculate the direction for furnaces and chests and stuff
2703 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2705 v3f playerpos = player->getPosition();
2706 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2707 blockpos = blockpos.normalize();
2709 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2723 Send to all close-by players
2725 core::list<u16> far_players;
2726 sendAddNode(p_over, n, 0, &far_players, 30);
2731 InventoryList *ilist = player->inventory.getList("main");
2732 if(g_settings->getBool("creative_mode") == false && ilist)
2734 // Remove from inventory and send inventory
2735 if(mitem->getCount() == 1)
2736 ilist->deleteItem(item_i);
2740 UpdateCrafting(peer_id);
2741 SendInventory(peer_id);
2747 This takes some time so it is done after the quick stuff
2749 core::map<v3s16, MapBlock*> modified_blocks;
2751 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2753 std::string p_name = std::string(player->getName());
2754 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2757 Set blocks not sent to far players
2759 for(core::list<u16>::Iterator
2760 i = far_players.begin();
2761 i != far_players.end(); i++)
2764 RemoteClient *client = getClient(peer_id);
2767 client->SetBlocksNotSent(modified_blocks);
2771 Calculate special events
2774 /*if(n.d == CONTENT_MESE)
2777 for(s16 z=-1; z<=1; z++)
2778 for(s16 y=-1; y<=1; y++)
2779 for(s16 x=-1; x<=1; x++)
2786 Place other item (not a block)
2790 v3s16 blockpos = getNodeBlockPos(p_over);
2793 Check that the block is loaded so that the item
2794 can properly be added to the static list too
2796 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2799 infostream<<"Error while placing object: "
2800 "block not found"<<std::endl;
2805 If in creative mode, item dropping is disabled unless
2806 player has build privileges
2808 if(g_settings->getBool("creative_mode") &&
2809 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2811 infostream<<"Not allowing player to drop item: "
2812 "creative mode and no build privs"<<std::endl;
2816 // Calculate a position for it
2817 v3f pos = intToFloat(p_over, BS);
2819 pos.Y -= BS*0.25; // let it drop a bit
2821 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2822 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2827 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2831 infostream<<"WARNING: item resulted in NULL object, "
2832 <<"not placing onto map"
2837 actionstream<<player->getName()<<" places "<<item->getName()
2838 <<" at "<<PP(p_over)<<std::endl;
2840 // Add the object to the environment
2841 m_env.addActiveObject(obj);
2843 infostream<<"Placed object"<<std::endl;
2845 if(g_settings->getBool("creative_mode") == false)
2847 // Delete the right amount of items from the slot
2848 u16 dropcount = item->getDropCount();
2850 // Delete item if all gone
2851 if(item->getCount() <= dropcount)
2853 if(item->getCount() < dropcount)
2854 infostream<<"WARNING: Server: dropped more items"
2855 <<" than the slot contains"<<std::endl;
2857 InventoryList *ilist = player->inventory.getList("main");
2859 // Remove from inventory and send inventory
2860 ilist->deleteItem(item_i);
2862 // Else decrement it
2864 item->remove(dropcount);
2867 UpdateCrafting(peer_id);
2868 SendInventory(peer_id);
2876 Catch invalid actions
2880 infostream<<"WARNING: Server: Invalid action "
2881 <<action<<std::endl;
2885 else if(command == TOSERVER_RELEASE)
2894 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2897 else if(command == TOSERVER_SIGNTEXT)
2899 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2903 else if(command == TOSERVER_SIGNNODETEXT)
2905 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2913 std::string datastring((char*)&data[2], datasize-2);
2914 std::istringstream is(datastring, std::ios_base::binary);
2917 is.read((char*)buf, 6);
2918 v3s16 p = readV3S16(buf);
2919 is.read((char*)buf, 2);
2920 u16 textlen = readU16(buf);
2922 for(u16 i=0; i<textlen; i++)
2924 is.read((char*)buf, 1);
2925 text += (char)buf[0];
2928 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2931 if(meta->typeId() != CONTENT_SIGN_WALL)
2933 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2934 signmeta->setText(text);
2936 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
2937 <<" at "<<PP(p)<<std::endl;
2939 v3s16 blockpos = getNodeBlockPos(p);
2940 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2943 block->setChangedFlag();
2946 for(core::map<u16, RemoteClient*>::Iterator
2947 i = m_clients.getIterator();
2948 i.atEnd()==false; i++)
2950 RemoteClient *client = i.getNode()->getValue();
2951 client->SetBlockNotSent(blockpos);
2954 else if(command == TOSERVER_INVENTORY_ACTION)
2956 /*// Ignore inventory changes if in creative mode
2957 if(g_settings->getBool("creative_mode") == true)
2959 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2963 // Strip command and create a stream
2964 std::string datastring((char*)&data[2], datasize-2);
2965 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2966 std::istringstream is(datastring, std::ios_base::binary);
2968 InventoryAction *a = InventoryAction::deSerialize(is);
2973 c.current_player = player;
2976 Handle craftresult specially if not in creative mode
2978 bool disable_action = false;
2979 if(a->getType() == IACTION_MOVE
2980 && g_settings->getBool("creative_mode") == false)
2982 IMoveAction *ma = (IMoveAction*)a;
2983 if(ma->to_inv == "current_player" &&
2984 ma->from_inv == "current_player")
2986 InventoryList *rlist = player->inventory.getList("craftresult");
2988 InventoryList *clist = player->inventory.getList("craft");
2990 InventoryList *mlist = player->inventory.getList("main");
2993 Craftresult is no longer preview if something
2996 if(ma->to_list == "craftresult"
2997 && ma->from_list != "craftresult")
2999 // If it currently is a preview, remove
3001 if(player->craftresult_is_preview)
3003 rlist->deleteItem(0);
3005 player->craftresult_is_preview = false;
3008 Crafting takes place if this condition is true.
3010 if(player->craftresult_is_preview &&
3011 ma->from_list == "craftresult")
3013 player->craftresult_is_preview = false;
3014 clist->decrementMaterials(1);
3016 /* Print out action */
3017 InventoryList *list =
3018 player->inventory.getList("craftresult");
3020 InventoryItem *item = list->getItem(0);
3021 std::string itemname = "NULL";
3023 itemname = item->getName();
3024 actionstream<<player->getName()<<" crafts "
3025 <<itemname<<std::endl;
3028 If the craftresult is placed on itself, move it to
3029 main inventory instead of doing the action
3031 if(ma->to_list == "craftresult"
3032 && ma->from_list == "craftresult")
3034 disable_action = true;
3036 InventoryItem *item1 = rlist->changeItem(0, NULL);
3037 mlist->addItem(item1);
3040 // Disallow moving items if not allowed to build
3041 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3045 // if it's a locking chest, only allow the owner or server admins to move items
3046 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3048 Strfnd fn(ma->from_inv);
3049 std::string id0 = fn.next(":");
3050 if(id0 == "nodemeta")
3053 p.X = stoi(fn.next(","));
3054 p.Y = stoi(fn.next(","));
3055 p.Z = stoi(fn.next(","));
3056 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3057 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3058 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3059 if (lcm->getOwner() != player->getName())
3064 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3066 Strfnd fn(ma->to_inv);
3067 std::string id0 = fn.next(":");
3068 if(id0 == "nodemeta")
3071 p.X = stoi(fn.next(","));
3072 p.Y = stoi(fn.next(","));
3073 p.Z = stoi(fn.next(","));
3074 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3075 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3076 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3077 if (lcm->getOwner() != player->getName())
3084 if(disable_action == false)
3086 // Feed action to player inventory
3094 UpdateCrafting(player->peer_id);
3095 SendInventory(player->peer_id);
3100 infostream<<"TOSERVER_INVENTORY_ACTION: "
3101 <<"InventoryAction::deSerialize() returned NULL"
3105 else if(command == TOSERVER_CHAT_MESSAGE)
3113 std::string datastring((char*)&data[2], datasize-2);
3114 std::istringstream is(datastring, std::ios_base::binary);
3117 is.read((char*)buf, 2);
3118 u16 len = readU16(buf);
3120 std::wstring message;
3121 for(u16 i=0; i<len; i++)
3123 is.read((char*)buf, 2);
3124 message += (wchar_t)readU16(buf);
3127 // Get player name of this client
3128 std::wstring name = narrow_to_wide(player->getName());
3130 // Line to send to players
3132 // Whether to send to the player that sent the line
3133 bool send_to_sender = false;
3134 // Whether to send to other players
3135 bool send_to_others = false;
3137 // Local player gets all privileges regardless of
3138 // what's set on their account.
3139 u64 privs = getPlayerPrivs(player);
3142 if(message[0] == L'/')
3144 size_t strip_size = 1;
3145 if (message[1] == L'#') // support old-style commans
3147 message = message.substr(strip_size);
3149 WStrfnd f1(message);
3150 f1.next(L" "); // Skip over /#whatever
3151 std::wstring paramstring = f1.next(L"");
3153 ServerCommandContext *ctx = new ServerCommandContext(
3154 str_split(message, L' '),
3161 std::wstring reply(processServerCommand(ctx));
3162 send_to_sender = ctx->flags & SEND_TO_SENDER;
3163 send_to_others = ctx->flags & SEND_TO_OTHERS;
3165 if (ctx->flags & SEND_NO_PREFIX)
3168 line += L"Server: " + reply;
3175 if(privs & PRIV_SHOUT)
3181 send_to_others = true;
3185 line += L"Server: You are not allowed to shout";
3186 send_to_sender = true;
3193 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3196 Send the message to clients
3198 for(core::map<u16, RemoteClient*>::Iterator
3199 i = m_clients.getIterator();
3200 i.atEnd() == false; i++)
3202 // Get client and check that it is valid
3203 RemoteClient *client = i.getNode()->getValue();
3204 assert(client->peer_id == i.getNode()->getKey());
3205 if(client->serialization_version == SER_FMT_VER_INVALID)
3209 bool sender_selected = (peer_id == client->peer_id);
3210 if(sender_selected == true && send_to_sender == false)
3212 if(sender_selected == false && send_to_others == false)
3215 SendChatMessage(client->peer_id, line);
3219 else if(command == TOSERVER_DAMAGE)
3221 std::string datastring((char*)&data[2], datasize-2);
3222 std::istringstream is(datastring, std::ios_base::binary);
3223 u8 damage = readU8(is);
3225 if(g_settings->getBool("enable_damage"))
3227 actionstream<<player->getName()<<" damaged by "
3228 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3231 HandlePlayerHP(player, damage);
3235 SendPlayerHP(player);
3238 else if(command == TOSERVER_PASSWORD)
3241 [0] u16 TOSERVER_PASSWORD
3242 [2] u8[28] old password
3243 [30] u8[28] new password
3246 if(datasize != 2+PASSWORD_SIZE*2)
3248 /*char password[PASSWORD_SIZE];
3249 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3250 password[i] = data[2+i];
3251 password[PASSWORD_SIZE-1] = 0;*/
3253 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3261 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3263 char c = data[2+PASSWORD_SIZE+i];
3269 infostream<<"Server: Client requests a password change from "
3270 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3272 std::string playername = player->getName();
3274 if(m_authmanager.exists(playername) == false)
3276 infostream<<"Server: playername not found in authmanager"<<std::endl;
3277 // Wrong old password supplied!!
3278 SendChatMessage(peer_id, L"playername not found in authmanager");
3282 std::string checkpwd = m_authmanager.getPassword(playername);
3284 if(oldpwd != checkpwd)
3286 infostream<<"Server: invalid old password"<<std::endl;
3287 // Wrong old password supplied!!
3288 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3292 actionstream<<player->getName()<<" changes password"<<std::endl;
3294 m_authmanager.setPassword(playername, newpwd);
3296 infostream<<"Server: password change successful for "<<playername
3298 SendChatMessage(peer_id, L"Password change successful");
3300 else if(command == TOSERVER_PLAYERITEM)
3305 u16 item = readU16(&data[2]);
3306 player->wieldItem(item);
3307 SendWieldedItem(player);
3309 else if(command == TOSERVER_RESPAWN)
3314 RespawnPlayer(player);
3316 actionstream<<player->getName()<<" respawns at "
3317 <<PP(player->getPosition()/BS)<<std::endl;
3321 infostream<<"Server::ProcessData(): Ignoring "
3322 "unknown command "<<command<<std::endl;
3326 catch(SendFailedException &e)
3328 errorstream<<"Server::ProcessData(): SendFailedException: "
3334 void Server::onMapEditEvent(MapEditEvent *event)
3336 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3337 if(m_ignore_map_edit_events)
3339 MapEditEvent *e = event->clone();
3340 m_unsent_map_edit_queue.push_back(e);
3343 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3345 if(id == "current_player")
3347 assert(c->current_player);
3348 return &(c->current_player->inventory);
3352 std::string id0 = fn.next(":");
3354 if(id0 == "nodemeta")
3357 p.X = stoi(fn.next(","));
3358 p.Y = stoi(fn.next(","));
3359 p.Z = stoi(fn.next(","));
3360 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3362 return meta->getInventory();
3363 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3364 <<"no metadata found"<<std::endl;
3368 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3371 void Server::inventoryModified(InventoryContext *c, std::string id)
3373 if(id == "current_player")
3375 assert(c->current_player);
3377 UpdateCrafting(c->current_player->peer_id);
3378 SendInventory(c->current_player->peer_id);
3383 std::string id0 = fn.next(":");
3385 if(id0 == "nodemeta")
3388 p.X = stoi(fn.next(","));
3389 p.Y = stoi(fn.next(","));
3390 p.Z = stoi(fn.next(","));
3391 v3s16 blockpos = getNodeBlockPos(p);
3393 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3395 meta->inventoryModified();
3397 for(core::map<u16, RemoteClient*>::Iterator
3398 i = m_clients.getIterator();
3399 i.atEnd()==false; i++)
3401 RemoteClient *client = i.getNode()->getValue();
3402 client->SetBlockNotSent(blockpos);
3408 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3411 core::list<PlayerInfo> Server::getPlayerInfo()
3413 DSTACK(__FUNCTION_NAME);
3414 JMutexAutoLock envlock(m_env_mutex);
3415 JMutexAutoLock conlock(m_con_mutex);
3417 core::list<PlayerInfo> list;
3419 core::list<Player*> players = m_env.getPlayers();
3421 core::list<Player*>::Iterator i;
3422 for(i = players.begin();
3423 i != players.end(); i++)
3427 Player *player = *i;
3430 // Copy info from connection to info struct
3431 info.id = player->peer_id;
3432 info.address = m_con.GetPeerAddress(player->peer_id);
3433 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3435 catch(con::PeerNotFoundException &e)
3437 // Set dummy peer info
3439 info.address = Address(0,0,0,0,0);
3443 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3444 info.position = player->getPosition();
3446 list.push_back(info);
3453 void Server::peerAdded(con::Peer *peer)
3455 DSTACK(__FUNCTION_NAME);
3456 infostream<<"Server::peerAdded(): peer->id="
3457 <<peer->id<<std::endl;
3460 c.type = PEER_ADDED;
3461 c.peer_id = peer->id;
3463 m_peer_change_queue.push_back(c);
3466 void Server::deletingPeer(con::Peer *peer, bool timeout)
3468 DSTACK(__FUNCTION_NAME);
3469 infostream<<"Server::deletingPeer(): peer->id="
3470 <<peer->id<<", timeout="<<timeout<<std::endl;
3473 c.type = PEER_REMOVED;
3474 c.peer_id = peer->id;
3475 c.timeout = timeout;
3476 m_peer_change_queue.push_back(c);
3483 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3485 DSTACK(__FUNCTION_NAME);
3486 std::ostringstream os(std::ios_base::binary);
3488 writeU16(os, TOCLIENT_HP);
3492 std::string s = os.str();
3493 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3495 con.Send(peer_id, 0, data, true);
3498 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3499 const std::wstring &reason)
3501 DSTACK(__FUNCTION_NAME);
3502 std::ostringstream os(std::ios_base::binary);
3504 writeU16(os, TOCLIENT_ACCESS_DENIED);
3505 os<<serializeWideString(reason);
3508 std::string s = os.str();
3509 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3511 con.Send(peer_id, 0, data, true);
3514 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3515 bool set_camera_point_target, v3f camera_point_target)
3517 DSTACK(__FUNCTION_NAME);
3518 std::ostringstream os(std::ios_base::binary);
3520 writeU16(os, TOCLIENT_DEATHSCREEN);
3521 writeU8(os, set_camera_point_target);
3522 writeV3F1000(os, camera_point_target);
3525 std::string s = os.str();
3526 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3528 con.Send(peer_id, 0, data, true);
3532 Non-static send methods
3535 void Server::SendObjectData(float dtime)
3537 DSTACK(__FUNCTION_NAME);
3539 core::map<v3s16, bool> stepped_blocks;
3541 for(core::map<u16, RemoteClient*>::Iterator
3542 i = m_clients.getIterator();
3543 i.atEnd() == false; i++)
3545 u16 peer_id = i.getNode()->getKey();
3546 RemoteClient *client = i.getNode()->getValue();
3547 assert(client->peer_id == peer_id);
3549 if(client->serialization_version == SER_FMT_VER_INVALID)
3552 client->SendObjectData(this, dtime, stepped_blocks);
3556 void Server::SendPlayerInfos()
3558 DSTACK(__FUNCTION_NAME);
3560 //JMutexAutoLock envlock(m_env_mutex);
3562 // Get connected players
3563 core::list<Player*> players = m_env.getPlayers(true);
3565 u32 player_count = players.getSize();
3566 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3568 SharedBuffer<u8> data(datasize);
3569 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3572 core::list<Player*>::Iterator i;
3573 for(i = players.begin();
3574 i != players.end(); i++)
3576 Player *player = *i;
3578 /*infostream<<"Server sending player info for player with "
3579 "peer_id="<<player->peer_id<<std::endl;*/
3581 writeU16(&data[start], player->peer_id);
3582 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3583 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3584 start += 2+PLAYERNAME_SIZE;
3587 //JMutexAutoLock conlock(m_con_mutex);
3590 m_con.SendToAll(0, data, true);
3593 void Server::SendInventory(u16 peer_id)
3595 DSTACK(__FUNCTION_NAME);
3597 Player* player = m_env.getPlayer(peer_id);
3604 std::ostringstream os;
3605 //os.imbue(std::locale("C"));
3607 player->inventory.serialize(os);
3609 std::string s = os.str();
3611 SharedBuffer<u8> data(s.size()+2);
3612 writeU16(&data[0], TOCLIENT_INVENTORY);
3613 memcpy(&data[2], s.c_str(), s.size());
3616 m_con.Send(peer_id, 0, data, true);
3619 std::string getWieldedItemString(const Player *player)
3621 const InventoryItem *item = player->getWieldItem();
3623 return std::string("");
3624 std::ostringstream os(std::ios_base::binary);
3625 item->serialize(os);
3629 void Server::SendWieldedItem(const Player* player)
3631 DSTACK(__FUNCTION_NAME);
3635 std::ostringstream os(std::ios_base::binary);
3637 writeU16(os, TOCLIENT_PLAYERITEM);
3639 writeU16(os, player->peer_id);
3640 os<<serializeString(getWieldedItemString(player));
3643 std::string s = os.str();
3644 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3646 m_con.SendToAll(0, data, true);
3649 void Server::SendPlayerItems()
3651 DSTACK(__FUNCTION_NAME);
3653 std::ostringstream os(std::ios_base::binary);
3654 core::list<Player *> players = m_env.getPlayers(true);
3656 writeU16(os, TOCLIENT_PLAYERITEM);
3657 writeU16(os, players.size());
3658 core::list<Player *>::Iterator i;
3659 for(i = players.begin(); i != players.end(); ++i)
3662 writeU16(os, p->peer_id);
3663 os<<serializeString(getWieldedItemString(p));
3667 std::string s = os.str();
3668 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3670 m_con.SendToAll(0, data, true);
3673 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3675 DSTACK(__FUNCTION_NAME);
3677 std::ostringstream os(std::ios_base::binary);
3681 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3682 os.write((char*)buf, 2);
3685 writeU16(buf, message.size());
3686 os.write((char*)buf, 2);
3689 for(u32 i=0; i<message.size(); i++)
3693 os.write((char*)buf, 2);
3697 std::string s = os.str();
3698 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3700 m_con.Send(peer_id, 0, data, true);
3703 void Server::BroadcastChatMessage(const std::wstring &message)
3705 for(core::map<u16, RemoteClient*>::Iterator
3706 i = m_clients.getIterator();
3707 i.atEnd() == false; i++)
3709 // Get client and check that it is valid
3710 RemoteClient *client = i.getNode()->getValue();
3711 assert(client->peer_id == i.getNode()->getKey());
3712 if(client->serialization_version == SER_FMT_VER_INVALID)
3715 SendChatMessage(client->peer_id, message);
3719 void Server::SendPlayerHP(Player *player)
3721 SendHP(m_con, player->peer_id, player->hp);
3724 void Server::SendMovePlayer(Player *player)
3726 DSTACK(__FUNCTION_NAME);
3727 std::ostringstream os(std::ios_base::binary);
3729 writeU16(os, TOCLIENT_MOVE_PLAYER);
3730 writeV3F1000(os, player->getPosition());
3731 writeF1000(os, player->getPitch());
3732 writeF1000(os, player->getYaw());
3735 v3f pos = player->getPosition();
3736 f32 pitch = player->getPitch();
3737 f32 yaw = player->getYaw();
3738 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3739 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3746 std::string s = os.str();
3747 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3749 m_con.Send(player->peer_id, 0, data, true);
3752 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3753 core::list<u16> *far_players, float far_d_nodes)
3755 float maxd = far_d_nodes*BS;
3756 v3f p_f = intToFloat(p, BS);
3760 SharedBuffer<u8> reply(replysize);
3761 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3762 writeS16(&reply[2], p.X);
3763 writeS16(&reply[4], p.Y);
3764 writeS16(&reply[6], p.Z);
3766 for(core::map<u16, RemoteClient*>::Iterator
3767 i = m_clients.getIterator();
3768 i.atEnd() == false; i++)
3770 // Get client and check that it is valid
3771 RemoteClient *client = i.getNode()->getValue();
3772 assert(client->peer_id == i.getNode()->getKey());
3773 if(client->serialization_version == SER_FMT_VER_INVALID)
3776 // Don't send if it's the same one
3777 if(client->peer_id == ignore_id)
3783 Player *player = m_env.getPlayer(client->peer_id);
3786 // If player is far away, only set modified blocks not sent
3787 v3f player_pos = player->getPosition();
3788 if(player_pos.getDistanceFrom(p_f) > maxd)
3790 far_players->push_back(client->peer_id);
3797 m_con.Send(client->peer_id, 0, reply, true);
3801 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3802 core::list<u16> *far_players, float far_d_nodes)
3804 float maxd = far_d_nodes*BS;
3805 v3f p_f = intToFloat(p, BS);
3807 for(core::map<u16, RemoteClient*>::Iterator
3808 i = m_clients.getIterator();
3809 i.atEnd() == false; i++)
3811 // Get client and check that it is valid
3812 RemoteClient *client = i.getNode()->getValue();
3813 assert(client->peer_id == i.getNode()->getKey());
3814 if(client->serialization_version == SER_FMT_VER_INVALID)
3817 // Don't send if it's the same one
3818 if(client->peer_id == ignore_id)
3824 Player *player = m_env.getPlayer(client->peer_id);
3827 // If player is far away, only set modified blocks not sent
3828 v3f player_pos = player->getPosition();
3829 if(player_pos.getDistanceFrom(p_f) > maxd)
3831 far_players->push_back(client->peer_id);
3838 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3839 SharedBuffer<u8> reply(replysize);
3840 writeU16(&reply[0], TOCLIENT_ADDNODE);
3841 writeS16(&reply[2], p.X);
3842 writeS16(&reply[4], p.Y);
3843 writeS16(&reply[6], p.Z);
3844 n.serialize(&reply[8], client->serialization_version);
3847 m_con.Send(client->peer_id, 0, reply, true);
3851 void Server::setBlockNotSent(v3s16 p)
3853 for(core::map<u16, RemoteClient*>::Iterator
3854 i = m_clients.getIterator();
3855 i.atEnd()==false; i++)
3857 RemoteClient *client = i.getNode()->getValue();
3858 client->SetBlockNotSent(p);
3862 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3864 DSTACK(__FUNCTION_NAME);
3866 v3s16 p = block->getPos();
3870 bool completely_air = true;
3871 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3872 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3873 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3875 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3877 completely_air = false;
3878 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3883 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3885 infostream<<"[completely air] ";
3886 infostream<<std::endl;
3890 Create a packet with the block in the right format
3893 std::ostringstream os(std::ios_base::binary);
3894 block->serialize(os, ver);
3895 std::string s = os.str();
3896 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3898 u32 replysize = 8 + blockdata.getSize();
3899 SharedBuffer<u8> reply(replysize);
3900 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3901 writeS16(&reply[2], p.X);
3902 writeS16(&reply[4], p.Y);
3903 writeS16(&reply[6], p.Z);
3904 memcpy(&reply[8], *blockdata, blockdata.getSize());
3906 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3907 <<": \tpacket size: "<<replysize<<std::endl;*/
3912 m_con.Send(peer_id, 1, reply, true);
3915 void Server::SendBlocks(float dtime)
3917 DSTACK(__FUNCTION_NAME);
3919 JMutexAutoLock envlock(m_env_mutex);
3920 JMutexAutoLock conlock(m_con_mutex);
3922 //TimeTaker timer("Server::SendBlocks");
3924 core::array<PrioritySortedBlockTransfer> queue;
3926 s32 total_sending = 0;
3929 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3931 for(core::map<u16, RemoteClient*>::Iterator
3932 i = m_clients.getIterator();
3933 i.atEnd() == false; i++)
3935 RemoteClient *client = i.getNode()->getValue();
3936 assert(client->peer_id == i.getNode()->getKey());
3938 total_sending += client->SendingCount();
3940 if(client->serialization_version == SER_FMT_VER_INVALID)
3943 client->GetNextBlocks(this, dtime, queue);
3948 // Lowest priority number comes first.
3949 // Lowest is most important.
3952 for(u32 i=0; i<queue.size(); i++)
3954 //TODO: Calculate limit dynamically
3955 if(total_sending >= g_settings->getS32
3956 ("max_simultaneous_block_sends_server_total"))
3959 PrioritySortedBlockTransfer q = queue[i];
3961 MapBlock *block = NULL;
3964 block = m_env.getMap().getBlockNoCreate(q.pos);
3966 catch(InvalidPositionException &e)
3971 RemoteClient *client = getClient(q.peer_id);
3973 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3975 client->SentBlock(q.pos);
3985 void Server::HandlePlayerHP(Player *player, s16 damage)
3987 if(player->hp > damage)
3989 player->hp -= damage;
3990 SendPlayerHP(player);
3994 infostream<<"Server::HandlePlayerHP(): Player "
3995 <<player->getName()<<" dies"<<std::endl;
3999 //TODO: Throw items around
4001 // Handle players that are not connected
4002 if(player->peer_id == PEER_ID_INEXISTENT){
4003 RespawnPlayer(player);
4007 SendPlayerHP(player);
4009 RemoteClient *client = getClient(player->peer_id);
4010 if(client->net_proto_version >= 3)
4012 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4016 RespawnPlayer(player);
4021 void Server::RespawnPlayer(Player *player)
4023 v3f pos = findSpawnPos(m_env.getServerMap());
4024 player->setPosition(pos);
4026 SendMovePlayer(player);
4027 SendPlayerHP(player);
4030 void Server::UpdateCrafting(u16 peer_id)
4032 DSTACK(__FUNCTION_NAME);
4034 Player* player = m_env.getPlayer(peer_id);
4038 Calculate crafting stuff
4040 if(g_settings->getBool("creative_mode") == false)
4042 InventoryList *clist = player->inventory.getList("craft");
4043 InventoryList *rlist = player->inventory.getList("craftresult");
4045 if(rlist && rlist->getUsedSlots() == 0)
4046 player->craftresult_is_preview = true;
4048 if(rlist && player->craftresult_is_preview)
4050 rlist->clearItems();
4052 if(clist && rlist && player->craftresult_is_preview)
4054 InventoryItem *items[9];
4055 for(u16 i=0; i<9; i++)
4057 items[i] = clist->getItem(i);
4060 // Get result of crafting grid
4061 InventoryItem *result = craft_get_result(items);
4063 rlist->addItem(result);
4066 } // if creative_mode == false
4069 RemoteClient* Server::getClient(u16 peer_id)
4071 DSTACK(__FUNCTION_NAME);
4072 //JMutexAutoLock lock(m_con_mutex);
4073 core::map<u16, RemoteClient*>::Node *n;
4074 n = m_clients.find(peer_id);
4075 // A client should exist for all peers
4077 return n->getValue();
4080 std::wstring Server::getStatusString()
4082 std::wostringstream os(std::ios_base::binary);
4085 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4087 os<<L", uptime="<<m_uptime.get();
4088 // Information about clients
4090 for(core::map<u16, RemoteClient*>::Iterator
4091 i = m_clients.getIterator();
4092 i.atEnd() == false; i++)
4094 // Get client and check that it is valid
4095 RemoteClient *client = i.getNode()->getValue();
4096 assert(client->peer_id == i.getNode()->getKey());
4097 if(client->serialization_version == SER_FMT_VER_INVALID)
4100 Player *player = m_env.getPlayer(client->peer_id);
4101 // Get name of player
4102 std::wstring name = L"unknown";
4104 name = narrow_to_wide(player->getName());
4105 // Add name to information string
4109 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4110 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4111 if(g_settings->get("motd") != "")
4112 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4116 // Saves g_settings to configpath given at initialization
4117 void Server::saveConfig()
4119 if(m_configpath != "")
4120 g_settings->updateConfigFile(m_configpath.c_str());
4123 void Server::notifyPlayer(const char *name, const std::wstring msg)
4125 Player *player = m_env.getPlayer(name);
4128 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4131 void Server::notifyPlayers(const std::wstring msg)
4133 BroadcastChatMessage(msg);
4136 v3f findSpawnPos(ServerMap &map)
4138 //return v3f(50,50,50)*BS;
4141 s16 groundheight = 0;
4144 nodepos = v2s16(0,0);
4149 // Try to find a good place a few times
4150 for(s32 i=0; i<1000; i++)
4153 // We're going to try to throw the player to this position
4154 nodepos = v2s16(-range + (myrand()%(range*2)),
4155 -range + (myrand()%(range*2)));
4156 v2s16 sectorpos = getNodeSectorPos(nodepos);
4157 // Get sector (NOTE: Don't get because it's slow)
4158 //m_env.getMap().emergeSector(sectorpos);
4159 // Get ground height at point (fallbacks to heightmap function)
4160 groundheight = map.findGroundLevel(nodepos);
4161 // Don't go underwater
4162 if(groundheight < WATER_LEVEL)
4164 //infostream<<"-> Underwater"<<std::endl;
4167 // Don't go to high places
4168 if(groundheight > WATER_LEVEL + 4)
4170 //infostream<<"-> Underwater"<<std::endl;
4174 // Found a good place
4175 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4180 // If no suitable place was not found, go above water at least.
4181 if(groundheight < WATER_LEVEL)
4182 groundheight = WATER_LEVEL;
4184 return intToFloat(v3s16(
4191 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4194 Try to get an existing player
4196 Player *player = m_env.getPlayer(name);
4199 // If player is already connected, cancel
4200 if(player->peer_id != 0)
4202 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4207 player->peer_id = peer_id;
4209 // Reset inventory to creative if in creative mode
4210 if(g_settings->getBool("creative_mode"))
4212 // Warning: double code below
4213 // Backup actual inventory
4214 player->inventory_backup = new Inventory();
4215 *(player->inventory_backup) = player->inventory;
4216 // Set creative inventory
4217 craft_set_creative_inventory(player);
4224 If player with the wanted peer_id already exists, cancel.
4226 if(m_env.getPlayer(peer_id) != NULL)
4228 infostream<<"emergePlayer(): Player with wrong name but same"
4229 " peer_id already exists"<<std::endl;
4237 player = new ServerRemotePlayer();
4238 //player->peer_id = c.peer_id;
4239 //player->peer_id = PEER_ID_INEXISTENT;
4240 player->peer_id = peer_id;
4241 player->updateName(name);
4242 m_authmanager.add(name);
4243 m_authmanager.setPassword(name, password);
4244 m_authmanager.setPrivs(name,
4245 stringToPrivs(g_settings->get("default_privs")));
4251 infostream<<"Server: Finding spawn place for player \""
4252 <<player->getName()<<"\""<<std::endl;
4254 v3f pos = findSpawnPos(m_env.getServerMap());
4256 player->setPosition(pos);
4259 Add player to environment
4262 m_env.addPlayer(player);
4265 Add stuff to inventory
4268 if(g_settings->getBool("creative_mode"))
4270 // Warning: double code above
4271 // Backup actual inventory
4272 player->inventory_backup = new Inventory();
4273 *(player->inventory_backup) = player->inventory;
4274 // Set creative inventory
4275 craft_set_creative_inventory(player);
4277 else if(g_settings->getBool("give_initial_stuff"))
4279 craft_give_initial_stuff(player);
4284 } // create new player
4287 void Server::handlePeerChange(PeerChange &c)
4289 JMutexAutoLock envlock(m_env_mutex);
4290 JMutexAutoLock conlock(m_con_mutex);
4292 if(c.type == PEER_ADDED)
4299 core::map<u16, RemoteClient*>::Node *n;
4300 n = m_clients.find(c.peer_id);
4301 // The client shouldn't already exist
4305 RemoteClient *client = new RemoteClient();
4306 client->peer_id = c.peer_id;
4307 m_clients.insert(client->peer_id, client);
4310 else if(c.type == PEER_REMOVED)
4317 core::map<u16, RemoteClient*>::Node *n;
4318 n = m_clients.find(c.peer_id);
4319 // The client should exist
4323 Mark objects to be not known by the client
4325 RemoteClient *client = n->getValue();
4327 for(core::map<u16, bool>::Iterator
4328 i = client->m_known_objects.getIterator();
4329 i.atEnd()==false; i++)
4332 u16 id = i.getNode()->getKey();
4333 ServerActiveObject* obj = m_env.getActiveObject(id);
4335 if(obj && obj->m_known_by_count > 0)
4336 obj->m_known_by_count--;
4339 // Collect information about leaving in chat
4340 std::wstring message;
4342 Player *player = m_env.getPlayer(c.peer_id);
4345 std::wstring name = narrow_to_wide(player->getName());
4348 message += L" left game";
4350 message += L" (timed out)";
4356 m_env.removePlayer(c.peer_id);
4359 // Set player client disconnected
4361 Player *player = m_env.getPlayer(c.peer_id);
4363 player->peer_id = 0;
4370 std::ostringstream os(std::ios_base::binary);
4371 for(core::map<u16, RemoteClient*>::Iterator
4372 i = m_clients.getIterator();
4373 i.atEnd() == false; i++)
4375 RemoteClient *client = i.getNode()->getValue();
4376 assert(client->peer_id == i.getNode()->getKey());
4377 if(client->serialization_version == SER_FMT_VER_INVALID)
4380 Player *player = m_env.getPlayer(client->peer_id);
4383 // Get name of player
4384 os<<player->getName()<<" ";
4387 actionstream<<player->getName()<<" "
4388 <<(c.timeout?"times out.":"leaves game.")
4389 <<" List of players: "
4390 <<os.str()<<std::endl;
4395 delete m_clients[c.peer_id];
4396 m_clients.remove(c.peer_id);
4398 // Send player info to all remaining clients
4401 // Send leave chat message to all remaining clients
4402 BroadcastChatMessage(message);
4411 void Server::handlePeerChanges()
4413 while(m_peer_change_queue.size() > 0)
4415 PeerChange c = m_peer_change_queue.pop_front();
4417 infostream<<"Server: Handling peer change: "
4418 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4421 handlePeerChange(c);
4425 u64 Server::getPlayerPrivs(Player *player)
4429 std::string playername = player->getName();
4430 // Local player gets all privileges regardless of
4431 // what's set on their account.
4432 if(g_settings->get("name") == playername)
4438 return getPlayerAuthPrivs(playername);
4442 void dedicated_server_loop(Server &server, bool &kill)
4444 DSTACK(__FUNCTION_NAME);
4446 infostream<<DTIME<<std::endl;
4447 infostream<<"========================"<<std::endl;
4448 infostream<<"Running dedicated server"<<std::endl;
4449 infostream<<"========================"<<std::endl;
4450 infostream<<std::endl;
4452 IntervalLimiter m_profiler_interval;
4456 // This is kind of a hack but can be done like this
4457 // because server.step() is very light
4459 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4464 if(server.getShutdownRequested() || kill)
4466 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4473 float profiler_print_interval =
4474 g_settings->getFloat("profiler_print_interval");
4475 if(profiler_print_interval != 0)
4477 if(m_profiler_interval.step(0.030, profiler_print_interval))
4479 infostream<<"Profiler:"<<std::endl;
4480 g_profiler->print(infostream);
4481 g_profiler->clear();
4488 static int counter = 0;
4494 core::list<PlayerInfo> list = server.getPlayerInfo();
4495 core::list<PlayerInfo>::Iterator i;
4496 static u32 sum_old = 0;
4497 u32 sum = PIChecksum(list);
4500 infostream<<DTIME<<"Player info:"<<std::endl;
4501 for(i=list.begin(); i!=list.end(); i++)
4503 i->PrintLine(&infostream);