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 SharedBuffer<u8> data;
1785 JMutexAutoLock conlock(m_con_mutex);
1786 datasize = m_con.Receive(peer_id, data);
1789 // This has to be called so that the client list gets synced
1790 // with the peer list of the connection
1791 handlePeerChanges();
1793 ProcessData(*data, datasize, peer_id);
1795 catch(con::InvalidIncomingDataException &e)
1797 infostream<<"Server::Receive(): "
1798 "InvalidIncomingDataException: what()="
1799 <<e.what()<<std::endl;
1801 catch(con::PeerNotFoundException &e)
1803 //NOTE: This is not needed anymore
1805 // The peer has been disconnected.
1806 // Find the associated player and remove it.
1808 /*JMutexAutoLock envlock(m_env_mutex);
1810 infostream<<"ServerThread: peer_id="<<peer_id
1811 <<" has apparently closed connection. "
1812 <<"Removing player."<<std::endl;
1814 m_env.removePlayer(peer_id);*/
1818 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1820 DSTACK(__FUNCTION_NAME);
1821 // Environment is locked first.
1822 JMutexAutoLock envlock(m_env_mutex);
1823 JMutexAutoLock conlock(m_con_mutex);
1826 Address address = m_con.GetPeerAddress(peer_id);
1828 // drop player if is ip is banned
1829 if(m_banmanager.isIpBanned(address.serializeString())){
1830 SendAccessDenied(m_con, peer_id,
1831 L"Your ip is banned. Banned name was "
1832 +narrow_to_wide(m_banmanager.getBanName(
1833 address.serializeString())));
1834 m_con.DeletePeer(peer_id);
1838 catch(con::PeerNotFoundException &e)
1840 infostream<<"Server::ProcessData(): Cancelling: peer "
1841 <<peer_id<<" not found"<<std::endl;
1845 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1853 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1855 if(command == TOSERVER_INIT)
1857 // [0] u16 TOSERVER_INIT
1858 // [2] u8 SER_FMT_VER_HIGHEST
1859 // [3] u8[20] player_name
1860 // [23] u8[28] password <--- can be sent without this, from old versions
1862 if(datasize < 2+1+PLAYERNAME_SIZE)
1865 infostream<<"Server: Got TOSERVER_INIT from "
1866 <<peer_id<<std::endl;
1868 // First byte after command is maximum supported
1869 // serialization version
1870 u8 client_max = data[2];
1871 u8 our_max = SER_FMT_VER_HIGHEST;
1872 // Use the highest version supported by both
1873 u8 deployed = core::min_(client_max, our_max);
1874 // If it's lower than the lowest supported, give up.
1875 if(deployed < SER_FMT_VER_LOWEST)
1876 deployed = SER_FMT_VER_INVALID;
1878 //peer->serialization_version = deployed;
1879 getClient(peer_id)->pending_serialization_version = deployed;
1881 if(deployed == SER_FMT_VER_INVALID)
1883 infostream<<"Server: Cannot negotiate "
1884 "serialization version with peer "
1885 <<peer_id<<std::endl;
1886 SendAccessDenied(m_con, peer_id,
1887 L"Your client is too old (map format)");
1892 Read and check network protocol version
1895 u16 net_proto_version = 0;
1896 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1898 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1901 getClient(peer_id)->net_proto_version = net_proto_version;
1903 if(net_proto_version == 0)
1905 SendAccessDenied(m_con, peer_id,
1906 L"Your client is too old. Please upgrade.");
1910 /* Uhh... this should actually be a warning but let's do it like this */
1911 if(g_settings->getBool("strict_protocol_version_checking"))
1913 if(net_proto_version < PROTOCOL_VERSION)
1915 SendAccessDenied(m_con, peer_id,
1916 L"Your client is too old. Please upgrade.");
1926 char playername[PLAYERNAME_SIZE];
1927 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1929 playername[i] = data[3+i];
1931 playername[PLAYERNAME_SIZE-1] = 0;
1933 if(playername[0]=='\0')
1935 infostream<<"Server: Player has empty name"<<std::endl;
1936 SendAccessDenied(m_con, peer_id,
1941 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1943 infostream<<"Server: Player has invalid name"<<std::endl;
1944 SendAccessDenied(m_con, peer_id,
1945 L"Name contains unallowed characters");
1950 char password[PASSWORD_SIZE];
1951 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1953 // old version - assume blank password
1958 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1960 password[i] = data[23+i];
1962 password[PASSWORD_SIZE-1] = 0;
1965 std::string checkpwd;
1966 if(m_authmanager.exists(playername))
1968 checkpwd = m_authmanager.getPassword(playername);
1972 checkpwd = g_settings->get("default_password");
1975 /*infostream<<"Server: Client gave password '"<<password
1976 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
1978 if(password != checkpwd && m_authmanager.exists(playername))
1980 infostream<<"Server: peer_id="<<peer_id
1981 <<": supplied invalid password for "
1982 <<playername<<std::endl;
1983 SendAccessDenied(m_con, peer_id, L"Invalid password");
1987 // Add player to auth manager
1988 if(m_authmanager.exists(playername) == false)
1990 infostream<<"Server: adding player "<<playername
1991 <<" to auth manager"<<std::endl;
1992 m_authmanager.add(playername);
1993 m_authmanager.setPassword(playername, checkpwd);
1994 m_authmanager.setPrivs(playername,
1995 stringToPrivs(g_settings->get("default_privs")));
1996 m_authmanager.save();
1999 // Enforce user limit.
2000 // Don't enforce for users that have some admin right
2001 if(m_clients.size() >= g_settings->getU16("max_users") &&
2002 (m_authmanager.getPrivs(playername)
2003 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2004 playername != g_settings->get("name"))
2006 SendAccessDenied(m_con, peer_id, L"Too many users.");
2011 Player *player = emergePlayer(playername, password, peer_id);
2013 // If failed, cancel
2016 infostream<<"Server: peer_id="<<peer_id
2017 <<": failed to emerge player"<<std::endl;
2022 Answer with a TOCLIENT_INIT
2025 SharedBuffer<u8> reply(2+1+6+8);
2026 writeU16(&reply[0], TOCLIENT_INIT);
2027 writeU8(&reply[2], deployed);
2028 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2029 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2032 m_con.Send(peer_id, 0, reply, true);
2036 Send complete position information
2038 SendMovePlayer(player);
2043 if(command == TOSERVER_INIT2)
2045 infostream<<"Server: Got TOSERVER_INIT2 from "
2046 <<peer_id<<std::endl;
2049 getClient(peer_id)->serialization_version
2050 = getClient(peer_id)->pending_serialization_version;
2053 Send some initialization data
2056 // Send player info to all players
2059 // Send inventory to player
2060 UpdateCrafting(peer_id);
2061 SendInventory(peer_id);
2063 // Send player items to all players
2066 Player *player = m_env.getPlayer(peer_id);
2069 SendPlayerHP(player);
2073 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2074 m_env.getTimeOfDay());
2075 m_con.Send(peer_id, 0, data, true);
2078 // Send information about server to player in chat
2079 SendChatMessage(peer_id, getStatusString());
2081 // Send information about joining in chat
2083 std::wstring name = L"unknown";
2084 Player *player = m_env.getPlayer(peer_id);
2086 name = narrow_to_wide(player->getName());
2088 std::wstring message;
2091 message += L" joined game";
2092 BroadcastChatMessage(message);
2095 // Warnings about protocol version can be issued here
2096 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2098 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2102 Check HP, respawn if necessary
2104 HandlePlayerHP(player, 0);
2110 std::ostringstream os(std::ios_base::binary);
2111 for(core::map<u16, RemoteClient*>::Iterator
2112 i = m_clients.getIterator();
2113 i.atEnd() == false; i++)
2115 RemoteClient *client = i.getNode()->getValue();
2116 assert(client->peer_id == i.getNode()->getKey());
2117 if(client->serialization_version == SER_FMT_VER_INVALID)
2120 Player *player = m_env.getPlayer(client->peer_id);
2123 // Get name of player
2124 os<<player->getName()<<" ";
2127 actionstream<<player->getName()<<" joins game. List of players: "
2128 <<os.str()<<std::endl;
2134 if(peer_ser_ver == SER_FMT_VER_INVALID)
2136 infostream<<"Server::ProcessData(): Cancelling: Peer"
2137 " serialization format invalid or not initialized."
2138 " Skipping incoming command="<<command<<std::endl;
2142 Player *player = m_env.getPlayer(peer_id);
2145 infostream<<"Server::ProcessData(): Cancelling: "
2146 "No player for peer_id="<<peer_id
2150 if(command == TOSERVER_PLAYERPOS)
2152 if(datasize < 2+12+12+4+4)
2156 v3s32 ps = readV3S32(&data[start+2]);
2157 v3s32 ss = readV3S32(&data[start+2+12]);
2158 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2159 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2160 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2161 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2162 pitch = wrapDegrees(pitch);
2163 yaw = wrapDegrees(yaw);
2165 player->setPosition(position);
2166 player->setSpeed(speed);
2167 player->setPitch(pitch);
2168 player->setYaw(yaw);
2170 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2171 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2172 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2174 else if(command == TOSERVER_GOTBLOCKS)
2187 u16 count = data[2];
2188 for(u16 i=0; i<count; i++)
2190 if((s16)datasize < 2+1+(i+1)*6)
2191 throw con::InvalidIncomingDataException
2192 ("GOTBLOCKS length is too short");
2193 v3s16 p = readV3S16(&data[2+1+i*6]);
2194 /*infostream<<"Server: GOTBLOCKS ("
2195 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2196 RemoteClient *client = getClient(peer_id);
2197 client->GotBlock(p);
2200 else if(command == TOSERVER_DELETEDBLOCKS)
2213 u16 count = data[2];
2214 for(u16 i=0; i<count; i++)
2216 if((s16)datasize < 2+1+(i+1)*6)
2217 throw con::InvalidIncomingDataException
2218 ("DELETEDBLOCKS length is too short");
2219 v3s16 p = readV3S16(&data[2+1+i*6]);
2220 /*infostream<<"Server: DELETEDBLOCKS ("
2221 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2222 RemoteClient *client = getClient(peer_id);
2223 client->SetBlockNotSent(p);
2226 else if(command == TOSERVER_CLICK_OBJECT)
2228 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2231 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2236 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2242 [2] u8 button (0=left, 1=right)
2246 u8 button = readU8(&data[2]);
2247 u16 id = readS16(&data[3]);
2248 u16 item_i = readU16(&data[5]);
2250 ServerActiveObject *obj = m_env.getActiveObject(id);
2254 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2259 // Skip if object has been removed
2263 //TODO: Check that object is reasonably close
2265 // Left click, pick object up (usually)
2269 Try creating inventory item
2271 InventoryItem *item = obj->createPickedUpItem();
2275 InventoryList *ilist = player->inventory.getList("main");
2278 actionstream<<player->getName()<<" picked up "
2279 <<item->getName()<<std::endl;
2280 if(g_settings->getBool("creative_mode") == false)
2282 // Skip if inventory has no free space
2283 if(ilist->roomForItem(item) == false)
2285 infostream<<"Player inventory has no free space"<<std::endl;
2289 // Add to inventory and send inventory
2290 ilist->addItem(item);
2291 UpdateCrafting(player->peer_id);
2292 SendInventory(player->peer_id);
2295 // Remove object from environment
2296 obj->m_removed = true;
2302 Item cannot be picked up. Punch it instead.
2305 actionstream<<player->getName()<<" punches object "
2306 <<obj->getId()<<std::endl;
2308 ToolItem *titem = NULL;
2309 std::string toolname = "";
2311 InventoryList *mlist = player->inventory.getList("main");
2314 InventoryItem *item = mlist->getItem(item_i);
2315 if(item && (std::string)item->getName() == "ToolItem")
2317 titem = (ToolItem*)item;
2318 toolname = titem->getToolName();
2322 v3f playerpos = player->getPosition();
2323 v3f objpos = obj->getBasePosition();
2324 v3f dir = (objpos - playerpos).normalize();
2326 u16 wear = obj->punch(toolname, dir, player->getName());
2330 bool weared_out = titem->addWear(wear);
2332 mlist->deleteItem(item_i);
2333 SendInventory(player->peer_id);
2337 // Right click, do something with object
2340 actionstream<<player->getName()<<" right clicks object "
2341 <<obj->getId()<<std::endl;
2343 // Track hp changes super-crappily
2344 u16 oldhp = player->hp;
2347 obj->rightClick(player);
2350 if(player->hp != oldhp)
2352 SendPlayerHP(player);
2356 else if(command == TOSERVER_GROUND_ACTION)
2364 [3] v3s16 nodepos_undersurface
2365 [9] v3s16 nodepos_abovesurface
2370 2: stop digging (all parameters ignored)
2371 3: digging completed
2373 u8 action = readU8(&data[2]);
2375 p_under.X = readS16(&data[3]);
2376 p_under.Y = readS16(&data[5]);
2377 p_under.Z = readS16(&data[7]);
2379 p_over.X = readS16(&data[9]);
2380 p_over.Y = readS16(&data[11]);
2381 p_over.Z = readS16(&data[13]);
2382 u16 item_i = readU16(&data[15]);
2384 //TODO: Check that target is reasonably close
2392 NOTE: This can be used in the future to check if
2393 somebody is cheating, by checking the timing.
2400 else if(action == 2)
2403 RemoteClient *client = getClient(peer_id);
2404 JMutexAutoLock digmutex(client->m_dig_mutex);
2405 client->m_dig_tool_item = -1;
2410 3: Digging completed
2412 else if(action == 3)
2414 // Mandatory parameter; actually used for nothing
2415 core::map<v3s16, MapBlock*> modified_blocks;
2417 content_t material = CONTENT_IGNORE;
2418 u8 mineral = MINERAL_NONE;
2420 bool cannot_remove_node = false;
2424 MapNode n = m_env.getMap().getNode(p_under);
2426 mineral = n.getMineral();
2427 // Get material at position
2428 material = n.getContent();
2429 // If not yet cancelled
2430 if(cannot_remove_node == false)
2432 // If it's not diggable, do nothing
2433 if(content_diggable(material) == false)
2435 infostream<<"Server: Not finishing digging: "
2436 <<"Node not diggable"
2438 cannot_remove_node = true;
2441 // If not yet cancelled
2442 if(cannot_remove_node == false)
2444 // Get node metadata
2445 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2446 if(meta && meta->nodeRemovalDisabled() == true)
2448 infostream<<"Server: Not finishing digging: "
2449 <<"Node metadata disables removal"
2451 cannot_remove_node = true;
2455 catch(InvalidPositionException &e)
2457 infostream<<"Server: Not finishing digging: Node not found."
2458 <<" Adding block to emerge queue."
2460 m_emerge_queue.addBlock(peer_id,
2461 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2462 cannot_remove_node = true;
2465 // Make sure the player is allowed to do it
2466 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2468 infostream<<"Player "<<player->getName()<<" cannot remove node"
2469 <<" because privileges are "<<getPlayerPrivs(player)
2471 cannot_remove_node = true;
2475 If node can't be removed, set block to be re-sent to
2478 if(cannot_remove_node)
2480 infostream<<"Server: Not finishing digging."<<std::endl;
2482 // Client probably has wrong data.
2483 // Set block not sent, so that client will get
2485 infostream<<"Client "<<peer_id<<" tried to dig "
2486 <<"node; but node cannot be removed."
2487 <<" setting MapBlock not sent."<<std::endl;
2488 RemoteClient *client = getClient(peer_id);
2489 v3s16 blockpos = getNodeBlockPos(p_under);
2490 client->SetBlockNotSent(blockpos);
2495 actionstream<<player->getName()<<" digs "<<PP(p_under)
2496 <<", gets material "<<(int)material<<", mineral "
2497 <<(int)mineral<<std::endl;
2500 Send the removal to all close-by players.
2501 - If other player is close, send REMOVENODE
2502 - Otherwise set blocks not sent
2504 core::list<u16> far_players;
2505 sendRemoveNode(p_under, peer_id, &far_players, 30);
2508 Update and send inventory
2511 if(g_settings->getBool("creative_mode") == false)
2516 InventoryList *mlist = player->inventory.getList("main");
2519 InventoryItem *item = mlist->getItem(item_i);
2520 if(item && (std::string)item->getName() == "ToolItem")
2522 ToolItem *titem = (ToolItem*)item;
2523 std::string toolname = titem->getToolName();
2525 // Get digging properties for material and tool
2526 DiggingProperties prop =
2527 getDiggingProperties(material, toolname);
2529 if(prop.diggable == false)
2531 infostream<<"Server: WARNING: Player digged"
2532 <<" with impossible material + tool"
2533 <<" combination"<<std::endl;
2536 bool weared_out = titem->addWear(prop.wear);
2540 mlist->deleteItem(item_i);
2546 Add dug item to inventory
2549 InventoryItem *item = NULL;
2551 if(mineral != MINERAL_NONE)
2552 item = getDiggedMineralItem(mineral);
2557 std::string &dug_s = content_features(material).dug_item;
2560 std::istringstream is(dug_s, std::ios::binary);
2561 item = InventoryItem::deSerialize(is);
2567 // Add a item to inventory
2568 player->inventory.addItem("main", item);
2571 UpdateCrafting(player->peer_id);
2572 SendInventory(player->peer_id);
2577 if(mineral != MINERAL_NONE)
2578 item = getDiggedMineralItem(mineral);
2583 std::string &extra_dug_s = content_features(material).extra_dug_item;
2584 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2585 if(extra_dug_s != "" && extra_rarity != 0
2586 && myrand() % extra_rarity == 0)
2588 std::istringstream is(extra_dug_s, std::ios::binary);
2589 item = InventoryItem::deSerialize(is);
2595 // Add a item to inventory
2596 player->inventory.addItem("main", item);
2599 UpdateCrafting(player->peer_id);
2600 SendInventory(player->peer_id);
2606 (this takes some time so it is done after the quick stuff)
2609 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2611 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2614 Set blocks not sent to far players
2616 for(core::list<u16>::Iterator
2617 i = far_players.begin();
2618 i != far_players.end(); i++)
2621 RemoteClient *client = getClient(peer_id);
2624 client->SetBlocksNotSent(modified_blocks);
2631 else if(action == 1)
2634 InventoryList *ilist = player->inventory.getList("main");
2639 InventoryItem *item = ilist->getItem(item_i);
2641 // If there is no item, it is not possible to add it anywhere
2646 Handle material items
2648 if(std::string("MaterialItem") == item->getName())
2651 // Don't add a node if this is not a free space
2652 MapNode n2 = m_env.getMap().getNode(p_over);
2653 bool no_enough_privs =
2654 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2656 infostream<<"Player "<<player->getName()<<" cannot add node"
2657 <<" because privileges are "<<getPlayerPrivs(player)
2660 if(content_features(n2).buildable_to == false
2663 // Client probably has wrong data.
2664 // Set block not sent, so that client will get
2666 infostream<<"Client "<<peer_id<<" tried to place"
2667 <<" node in invalid position; setting"
2668 <<" MapBlock not sent."<<std::endl;
2669 RemoteClient *client = getClient(peer_id);
2670 v3s16 blockpos = getNodeBlockPos(p_over);
2671 client->SetBlockNotSent(blockpos);
2675 catch(InvalidPositionException &e)
2677 infostream<<"Server: Ignoring ADDNODE: Node not found"
2678 <<" Adding block to emerge queue."
2680 m_emerge_queue.addBlock(peer_id,
2681 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2685 // Reset build time counter
2686 getClient(peer_id)->m_time_from_building = 0.0;
2689 MaterialItem *mitem = (MaterialItem*)item;
2691 n.setContent(mitem->getMaterial());
2693 actionstream<<player->getName()<<" places material "
2694 <<(int)mitem->getMaterial()
2695 <<" at "<<PP(p_under)<<std::endl;
2697 // Calculate direction for wall mounted stuff
2698 if(content_features(n).wall_mounted)
2699 n.param2 = packDir(p_under - p_over);
2701 // Calculate the direction for furnaces and chests and stuff
2702 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2704 v3f playerpos = player->getPosition();
2705 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2706 blockpos = blockpos.normalize();
2708 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2722 Send to all close-by players
2724 core::list<u16> far_players;
2725 sendAddNode(p_over, n, 0, &far_players, 30);
2730 InventoryList *ilist = player->inventory.getList("main");
2731 if(g_settings->getBool("creative_mode") == false && ilist)
2733 // Remove from inventory and send inventory
2734 if(mitem->getCount() == 1)
2735 ilist->deleteItem(item_i);
2739 UpdateCrafting(peer_id);
2740 SendInventory(peer_id);
2746 This takes some time so it is done after the quick stuff
2748 core::map<v3s16, MapBlock*> modified_blocks;
2750 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2752 std::string p_name = std::string(player->getName());
2753 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2756 Set blocks not sent to far players
2758 for(core::list<u16>::Iterator
2759 i = far_players.begin();
2760 i != far_players.end(); i++)
2763 RemoteClient *client = getClient(peer_id);
2766 client->SetBlocksNotSent(modified_blocks);
2770 Calculate special events
2773 /*if(n.d == CONTENT_MESE)
2776 for(s16 z=-1; z<=1; z++)
2777 for(s16 y=-1; y<=1; y++)
2778 for(s16 x=-1; x<=1; x++)
2785 Place other item (not a block)
2789 v3s16 blockpos = getNodeBlockPos(p_over);
2792 Check that the block is loaded so that the item
2793 can properly be added to the static list too
2795 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2798 infostream<<"Error while placing object: "
2799 "block not found"<<std::endl;
2804 If in creative mode, item dropping is disabled unless
2805 player has build privileges
2807 if(g_settings->getBool("creative_mode") &&
2808 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2810 infostream<<"Not allowing player to drop item: "
2811 "creative mode and no build privs"<<std::endl;
2815 // Calculate a position for it
2816 v3f pos = intToFloat(p_over, BS);
2818 pos.Y -= BS*0.25; // let it drop a bit
2820 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2821 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2826 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2830 infostream<<"WARNING: item resulted in NULL object, "
2831 <<"not placing onto map"
2836 actionstream<<player->getName()<<" places "<<item->getName()
2837 <<" at "<<PP(p_over)<<std::endl;
2839 // Add the object to the environment
2840 m_env.addActiveObject(obj);
2842 infostream<<"Placed object"<<std::endl;
2844 if(g_settings->getBool("creative_mode") == false)
2846 // Delete the right amount of items from the slot
2847 u16 dropcount = item->getDropCount();
2849 // Delete item if all gone
2850 if(item->getCount() <= dropcount)
2852 if(item->getCount() < dropcount)
2853 infostream<<"WARNING: Server: dropped more items"
2854 <<" than the slot contains"<<std::endl;
2856 InventoryList *ilist = player->inventory.getList("main");
2858 // Remove from inventory and send inventory
2859 ilist->deleteItem(item_i);
2861 // Else decrement it
2863 item->remove(dropcount);
2866 UpdateCrafting(peer_id);
2867 SendInventory(peer_id);
2875 Catch invalid actions
2879 infostream<<"WARNING: Server: Invalid action "
2880 <<action<<std::endl;
2884 else if(command == TOSERVER_RELEASE)
2893 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2896 else if(command == TOSERVER_SIGNTEXT)
2898 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2902 else if(command == TOSERVER_SIGNNODETEXT)
2904 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2912 std::string datastring((char*)&data[2], datasize-2);
2913 std::istringstream is(datastring, std::ios_base::binary);
2916 is.read((char*)buf, 6);
2917 v3s16 p = readV3S16(buf);
2918 is.read((char*)buf, 2);
2919 u16 textlen = readU16(buf);
2921 for(u16 i=0; i<textlen; i++)
2923 is.read((char*)buf, 1);
2924 text += (char)buf[0];
2927 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2930 if(meta->typeId() != CONTENT_SIGN_WALL)
2932 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2933 signmeta->setText(text);
2935 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
2936 <<" at "<<PP(p)<<std::endl;
2938 v3s16 blockpos = getNodeBlockPos(p);
2939 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2942 block->setChangedFlag();
2945 for(core::map<u16, RemoteClient*>::Iterator
2946 i = m_clients.getIterator();
2947 i.atEnd()==false; i++)
2949 RemoteClient *client = i.getNode()->getValue();
2950 client->SetBlockNotSent(blockpos);
2953 else if(command == TOSERVER_INVENTORY_ACTION)
2955 /*// Ignore inventory changes if in creative mode
2956 if(g_settings->getBool("creative_mode") == true)
2958 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2962 // Strip command and create a stream
2963 std::string datastring((char*)&data[2], datasize-2);
2964 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2965 std::istringstream is(datastring, std::ios_base::binary);
2967 InventoryAction *a = InventoryAction::deSerialize(is);
2972 c.current_player = player;
2975 Handle craftresult specially if not in creative mode
2977 bool disable_action = false;
2978 if(a->getType() == IACTION_MOVE
2979 && g_settings->getBool("creative_mode") == false)
2981 IMoveAction *ma = (IMoveAction*)a;
2982 if(ma->to_inv == "current_player" &&
2983 ma->from_inv == "current_player")
2985 InventoryList *rlist = player->inventory.getList("craftresult");
2987 InventoryList *clist = player->inventory.getList("craft");
2989 InventoryList *mlist = player->inventory.getList("main");
2992 Craftresult is no longer preview if something
2995 if(ma->to_list == "craftresult"
2996 && ma->from_list != "craftresult")
2998 // If it currently is a preview, remove
3000 if(player->craftresult_is_preview)
3002 rlist->deleteItem(0);
3004 player->craftresult_is_preview = false;
3007 Crafting takes place if this condition is true.
3009 if(player->craftresult_is_preview &&
3010 ma->from_list == "craftresult")
3012 player->craftresult_is_preview = false;
3013 clist->decrementMaterials(1);
3015 /* Print out action */
3016 InventoryList *list =
3017 player->inventory.getList("craftresult");
3019 InventoryItem *item = list->getItem(0);
3020 std::string itemname = "NULL";
3022 itemname = item->getName();
3023 actionstream<<player->getName()<<" crafts "
3024 <<itemname<<std::endl;
3027 If the craftresult is placed on itself, move it to
3028 main inventory instead of doing the action
3030 if(ma->to_list == "craftresult"
3031 && ma->from_list == "craftresult")
3033 disable_action = true;
3035 InventoryItem *item1 = rlist->changeItem(0, NULL);
3036 mlist->addItem(item1);
3039 // Disallow moving items if not allowed to build
3040 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3044 // if it's a locking chest, only allow the owner or server admins to move items
3045 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3047 Strfnd fn(ma->from_inv);
3048 std::string id0 = fn.next(":");
3049 if(id0 == "nodemeta")
3052 p.X = stoi(fn.next(","));
3053 p.Y = stoi(fn.next(","));
3054 p.Z = stoi(fn.next(","));
3055 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3056 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3057 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3058 if (lcm->getOwner() != player->getName())
3063 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3065 Strfnd fn(ma->to_inv);
3066 std::string id0 = fn.next(":");
3067 if(id0 == "nodemeta")
3070 p.X = stoi(fn.next(","));
3071 p.Y = stoi(fn.next(","));
3072 p.Z = stoi(fn.next(","));
3073 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3074 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3075 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3076 if (lcm->getOwner() != player->getName())
3083 if(disable_action == false)
3085 // Feed action to player inventory
3093 UpdateCrafting(player->peer_id);
3094 SendInventory(player->peer_id);
3099 infostream<<"TOSERVER_INVENTORY_ACTION: "
3100 <<"InventoryAction::deSerialize() returned NULL"
3104 else if(command == TOSERVER_CHAT_MESSAGE)
3112 std::string datastring((char*)&data[2], datasize-2);
3113 std::istringstream is(datastring, std::ios_base::binary);
3116 is.read((char*)buf, 2);
3117 u16 len = readU16(buf);
3119 std::wstring message;
3120 for(u16 i=0; i<len; i++)
3122 is.read((char*)buf, 2);
3123 message += (wchar_t)readU16(buf);
3126 // Get player name of this client
3127 std::wstring name = narrow_to_wide(player->getName());
3129 // Line to send to players
3131 // Whether to send to the player that sent the line
3132 bool send_to_sender = false;
3133 // Whether to send to other players
3134 bool send_to_others = false;
3136 // Local player gets all privileges regardless of
3137 // what's set on their account.
3138 u64 privs = getPlayerPrivs(player);
3141 if(message[0] == L'/')
3143 size_t strip_size = 1;
3144 if (message[1] == L'#') // support old-style commans
3146 message = message.substr(strip_size);
3148 WStrfnd f1(message);
3149 f1.next(L" "); // Skip over /#whatever
3150 std::wstring paramstring = f1.next(L"");
3152 ServerCommandContext *ctx = new ServerCommandContext(
3153 str_split(message, L' '),
3160 std::wstring reply(processServerCommand(ctx));
3161 send_to_sender = ctx->flags & SEND_TO_SENDER;
3162 send_to_others = ctx->flags & SEND_TO_OTHERS;
3164 if (ctx->flags & SEND_NO_PREFIX)
3167 line += L"Server: " + reply;
3174 if(privs & PRIV_SHOUT)
3180 send_to_others = true;
3184 line += L"Server: You are not allowed to shout";
3185 send_to_sender = true;
3192 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3195 Send the message to clients
3197 for(core::map<u16, RemoteClient*>::Iterator
3198 i = m_clients.getIterator();
3199 i.atEnd() == false; i++)
3201 // Get client and check that it is valid
3202 RemoteClient *client = i.getNode()->getValue();
3203 assert(client->peer_id == i.getNode()->getKey());
3204 if(client->serialization_version == SER_FMT_VER_INVALID)
3208 bool sender_selected = (peer_id == client->peer_id);
3209 if(sender_selected == true && send_to_sender == false)
3211 if(sender_selected == false && send_to_others == false)
3214 SendChatMessage(client->peer_id, line);
3218 else if(command == TOSERVER_DAMAGE)
3220 std::string datastring((char*)&data[2], datasize-2);
3221 std::istringstream is(datastring, std::ios_base::binary);
3222 u8 damage = readU8(is);
3224 if(g_settings->getBool("enable_damage"))
3226 actionstream<<player->getName()<<" damaged by "
3227 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3230 HandlePlayerHP(player, damage);
3234 SendPlayerHP(player);
3237 else if(command == TOSERVER_PASSWORD)
3240 [0] u16 TOSERVER_PASSWORD
3241 [2] u8[28] old password
3242 [30] u8[28] new password
3245 if(datasize != 2+PASSWORD_SIZE*2)
3247 /*char password[PASSWORD_SIZE];
3248 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3249 password[i] = data[2+i];
3250 password[PASSWORD_SIZE-1] = 0;*/
3252 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3260 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3262 char c = data[2+PASSWORD_SIZE+i];
3268 infostream<<"Server: Client requests a password change from "
3269 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3271 std::string playername = player->getName();
3273 if(m_authmanager.exists(playername) == false)
3275 infostream<<"Server: playername not found in authmanager"<<std::endl;
3276 // Wrong old password supplied!!
3277 SendChatMessage(peer_id, L"playername not found in authmanager");
3281 std::string checkpwd = m_authmanager.getPassword(playername);
3283 if(oldpwd != checkpwd)
3285 infostream<<"Server: invalid old password"<<std::endl;
3286 // Wrong old password supplied!!
3287 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3291 actionstream<<player->getName()<<" changes password"<<std::endl;
3293 m_authmanager.setPassword(playername, newpwd);
3295 infostream<<"Server: password change successful for "<<playername
3297 SendChatMessage(peer_id, L"Password change successful");
3299 else if(command == TOSERVER_PLAYERITEM)
3304 u16 item = readU16(&data[2]);
3305 player->wieldItem(item);
3306 SendWieldedItem(player);
3308 else if(command == TOSERVER_RESPAWN)
3313 RespawnPlayer(player);
3315 actionstream<<player->getName()<<" respawns at "
3316 <<PP(player->getPosition()/BS)<<std::endl;
3320 infostream<<"Server::ProcessData(): Ignoring "
3321 "unknown command "<<command<<std::endl;
3325 catch(SendFailedException &e)
3327 errorstream<<"Server::ProcessData(): SendFailedException: "
3333 void Server::onMapEditEvent(MapEditEvent *event)
3335 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3336 if(m_ignore_map_edit_events)
3338 MapEditEvent *e = event->clone();
3339 m_unsent_map_edit_queue.push_back(e);
3342 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3344 if(id == "current_player")
3346 assert(c->current_player);
3347 return &(c->current_player->inventory);
3351 std::string id0 = fn.next(":");
3353 if(id0 == "nodemeta")
3356 p.X = stoi(fn.next(","));
3357 p.Y = stoi(fn.next(","));
3358 p.Z = stoi(fn.next(","));
3359 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3361 return meta->getInventory();
3362 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3363 <<"no metadata found"<<std::endl;
3367 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3370 void Server::inventoryModified(InventoryContext *c, std::string id)
3372 if(id == "current_player")
3374 assert(c->current_player);
3376 UpdateCrafting(c->current_player->peer_id);
3377 SendInventory(c->current_player->peer_id);
3382 std::string id0 = fn.next(":");
3384 if(id0 == "nodemeta")
3387 p.X = stoi(fn.next(","));
3388 p.Y = stoi(fn.next(","));
3389 p.Z = stoi(fn.next(","));
3390 v3s16 blockpos = getNodeBlockPos(p);
3392 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3394 meta->inventoryModified();
3396 for(core::map<u16, RemoteClient*>::Iterator
3397 i = m_clients.getIterator();
3398 i.atEnd()==false; i++)
3400 RemoteClient *client = i.getNode()->getValue();
3401 client->SetBlockNotSent(blockpos);
3407 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3410 core::list<PlayerInfo> Server::getPlayerInfo()
3412 DSTACK(__FUNCTION_NAME);
3413 JMutexAutoLock envlock(m_env_mutex);
3414 JMutexAutoLock conlock(m_con_mutex);
3416 core::list<PlayerInfo> list;
3418 core::list<Player*> players = m_env.getPlayers();
3420 core::list<Player*>::Iterator i;
3421 for(i = players.begin();
3422 i != players.end(); i++)
3426 Player *player = *i;
3429 // Copy info from connection to info struct
3430 info.id = player->peer_id;
3431 info.address = m_con.GetPeerAddress(player->peer_id);
3432 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3434 catch(con::PeerNotFoundException &e)
3436 // Set dummy peer info
3438 info.address = Address(0,0,0,0,0);
3442 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3443 info.position = player->getPosition();
3445 list.push_back(info);
3452 void Server::peerAdded(con::Peer *peer)
3454 DSTACK(__FUNCTION_NAME);
3455 infostream<<"Server::peerAdded(): peer->id="
3456 <<peer->id<<std::endl;
3459 c.type = PEER_ADDED;
3460 c.peer_id = peer->id;
3462 m_peer_change_queue.push_back(c);
3465 void Server::deletingPeer(con::Peer *peer, bool timeout)
3467 DSTACK(__FUNCTION_NAME);
3468 infostream<<"Server::deletingPeer(): peer->id="
3469 <<peer->id<<", timeout="<<timeout<<std::endl;
3472 c.type = PEER_REMOVED;
3473 c.peer_id = peer->id;
3474 c.timeout = timeout;
3475 m_peer_change_queue.push_back(c);
3482 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3484 DSTACK(__FUNCTION_NAME);
3485 std::ostringstream os(std::ios_base::binary);
3487 writeU16(os, TOCLIENT_HP);
3491 std::string s = os.str();
3492 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3494 con.Send(peer_id, 0, data, true);
3497 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3498 const std::wstring &reason)
3500 DSTACK(__FUNCTION_NAME);
3501 std::ostringstream os(std::ios_base::binary);
3503 writeU16(os, TOCLIENT_ACCESS_DENIED);
3504 os<<serializeWideString(reason);
3507 std::string s = os.str();
3508 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3510 con.Send(peer_id, 0, data, true);
3513 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3514 bool set_camera_point_target, v3f camera_point_target)
3516 DSTACK(__FUNCTION_NAME);
3517 std::ostringstream os(std::ios_base::binary);
3519 writeU16(os, TOCLIENT_DEATHSCREEN);
3520 writeU8(os, set_camera_point_target);
3521 writeV3F1000(os, camera_point_target);
3524 std::string s = os.str();
3525 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3527 con.Send(peer_id, 0, data, true);
3531 Non-static send methods
3534 void Server::SendObjectData(float dtime)
3536 DSTACK(__FUNCTION_NAME);
3538 core::map<v3s16, bool> stepped_blocks;
3540 for(core::map<u16, RemoteClient*>::Iterator
3541 i = m_clients.getIterator();
3542 i.atEnd() == false; i++)
3544 u16 peer_id = i.getNode()->getKey();
3545 RemoteClient *client = i.getNode()->getValue();
3546 assert(client->peer_id == peer_id);
3548 if(client->serialization_version == SER_FMT_VER_INVALID)
3551 client->SendObjectData(this, dtime, stepped_blocks);
3555 void Server::SendPlayerInfos()
3557 DSTACK(__FUNCTION_NAME);
3559 //JMutexAutoLock envlock(m_env_mutex);
3561 // Get connected players
3562 core::list<Player*> players = m_env.getPlayers(true);
3564 u32 player_count = players.getSize();
3565 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3567 SharedBuffer<u8> data(datasize);
3568 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3571 core::list<Player*>::Iterator i;
3572 for(i = players.begin();
3573 i != players.end(); i++)
3575 Player *player = *i;
3577 /*infostream<<"Server sending player info for player with "
3578 "peer_id="<<player->peer_id<<std::endl;*/
3580 writeU16(&data[start], player->peer_id);
3581 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3582 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3583 start += 2+PLAYERNAME_SIZE;
3586 //JMutexAutoLock conlock(m_con_mutex);
3589 m_con.SendToAll(0, data, true);
3592 void Server::SendInventory(u16 peer_id)
3594 DSTACK(__FUNCTION_NAME);
3596 Player* player = m_env.getPlayer(peer_id);
3603 std::ostringstream os;
3604 //os.imbue(std::locale("C"));
3606 player->inventory.serialize(os);
3608 std::string s = os.str();
3610 SharedBuffer<u8> data(s.size()+2);
3611 writeU16(&data[0], TOCLIENT_INVENTORY);
3612 memcpy(&data[2], s.c_str(), s.size());
3615 m_con.Send(peer_id, 0, data, true);
3618 std::string getWieldedItemString(const Player *player)
3620 const InventoryItem *item = player->getWieldItem();
3622 return std::string("");
3623 std::ostringstream os(std::ios_base::binary);
3624 item->serialize(os);
3628 void Server::SendWieldedItem(const Player* player)
3630 DSTACK(__FUNCTION_NAME);
3634 std::ostringstream os(std::ios_base::binary);
3636 writeU16(os, TOCLIENT_PLAYERITEM);
3638 writeU16(os, player->peer_id);
3639 os<<serializeString(getWieldedItemString(player));
3642 std::string s = os.str();
3643 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3645 m_con.SendToAll(0, data, true);
3648 void Server::SendPlayerItems()
3650 DSTACK(__FUNCTION_NAME);
3652 std::ostringstream os(std::ios_base::binary);
3653 core::list<Player *> players = m_env.getPlayers(true);
3655 writeU16(os, TOCLIENT_PLAYERITEM);
3656 writeU16(os, players.size());
3657 core::list<Player *>::Iterator i;
3658 for(i = players.begin(); i != players.end(); ++i)
3661 writeU16(os, p->peer_id);
3662 os<<serializeString(getWieldedItemString(p));
3666 std::string s = os.str();
3667 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3669 m_con.SendToAll(0, data, true);
3672 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3674 DSTACK(__FUNCTION_NAME);
3676 std::ostringstream os(std::ios_base::binary);
3680 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3681 os.write((char*)buf, 2);
3684 writeU16(buf, message.size());
3685 os.write((char*)buf, 2);
3688 for(u32 i=0; i<message.size(); i++)
3692 os.write((char*)buf, 2);
3696 std::string s = os.str();
3697 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3699 m_con.Send(peer_id, 0, data, true);
3702 void Server::BroadcastChatMessage(const std::wstring &message)
3704 for(core::map<u16, RemoteClient*>::Iterator
3705 i = m_clients.getIterator();
3706 i.atEnd() == false; i++)
3708 // Get client and check that it is valid
3709 RemoteClient *client = i.getNode()->getValue();
3710 assert(client->peer_id == i.getNode()->getKey());
3711 if(client->serialization_version == SER_FMT_VER_INVALID)
3714 SendChatMessage(client->peer_id, message);
3718 void Server::SendPlayerHP(Player *player)
3720 SendHP(m_con, player->peer_id, player->hp);
3723 void Server::SendMovePlayer(Player *player)
3725 DSTACK(__FUNCTION_NAME);
3726 std::ostringstream os(std::ios_base::binary);
3728 writeU16(os, TOCLIENT_MOVE_PLAYER);
3729 writeV3F1000(os, player->getPosition());
3730 writeF1000(os, player->getPitch());
3731 writeF1000(os, player->getYaw());
3734 v3f pos = player->getPosition();
3735 f32 pitch = player->getPitch();
3736 f32 yaw = player->getYaw();
3737 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3738 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3745 std::string s = os.str();
3746 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3748 m_con.Send(player->peer_id, 0, data, true);
3751 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3752 core::list<u16> *far_players, float far_d_nodes)
3754 float maxd = far_d_nodes*BS;
3755 v3f p_f = intToFloat(p, BS);
3759 SharedBuffer<u8> reply(replysize);
3760 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3761 writeS16(&reply[2], p.X);
3762 writeS16(&reply[4], p.Y);
3763 writeS16(&reply[6], p.Z);
3765 for(core::map<u16, RemoteClient*>::Iterator
3766 i = m_clients.getIterator();
3767 i.atEnd() == false; i++)
3769 // Get client and check that it is valid
3770 RemoteClient *client = i.getNode()->getValue();
3771 assert(client->peer_id == i.getNode()->getKey());
3772 if(client->serialization_version == SER_FMT_VER_INVALID)
3775 // Don't send if it's the same one
3776 if(client->peer_id == ignore_id)
3782 Player *player = m_env.getPlayer(client->peer_id);
3785 // If player is far away, only set modified blocks not sent
3786 v3f player_pos = player->getPosition();
3787 if(player_pos.getDistanceFrom(p_f) > maxd)
3789 far_players->push_back(client->peer_id);
3796 m_con.Send(client->peer_id, 0, reply, true);
3800 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3801 core::list<u16> *far_players, float far_d_nodes)
3803 float maxd = far_d_nodes*BS;
3804 v3f p_f = intToFloat(p, BS);
3806 for(core::map<u16, RemoteClient*>::Iterator
3807 i = m_clients.getIterator();
3808 i.atEnd() == false; i++)
3810 // Get client and check that it is valid
3811 RemoteClient *client = i.getNode()->getValue();
3812 assert(client->peer_id == i.getNode()->getKey());
3813 if(client->serialization_version == SER_FMT_VER_INVALID)
3816 // Don't send if it's the same one
3817 if(client->peer_id == ignore_id)
3823 Player *player = m_env.getPlayer(client->peer_id);
3826 // If player is far away, only set modified blocks not sent
3827 v3f player_pos = player->getPosition();
3828 if(player_pos.getDistanceFrom(p_f) > maxd)
3830 far_players->push_back(client->peer_id);
3837 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3838 SharedBuffer<u8> reply(replysize);
3839 writeU16(&reply[0], TOCLIENT_ADDNODE);
3840 writeS16(&reply[2], p.X);
3841 writeS16(&reply[4], p.Y);
3842 writeS16(&reply[6], p.Z);
3843 n.serialize(&reply[8], client->serialization_version);
3846 m_con.Send(client->peer_id, 0, reply, true);
3850 void Server::setBlockNotSent(v3s16 p)
3852 for(core::map<u16, RemoteClient*>::Iterator
3853 i = m_clients.getIterator();
3854 i.atEnd()==false; i++)
3856 RemoteClient *client = i.getNode()->getValue();
3857 client->SetBlockNotSent(p);
3861 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3863 DSTACK(__FUNCTION_NAME);
3865 v3s16 p = block->getPos();
3869 bool completely_air = true;
3870 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3871 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3872 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3874 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3876 completely_air = false;
3877 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3882 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3884 infostream<<"[completely air] ";
3885 infostream<<std::endl;
3889 Create a packet with the block in the right format
3892 std::ostringstream os(std::ios_base::binary);
3893 block->serialize(os, ver);
3894 std::string s = os.str();
3895 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3897 u32 replysize = 8 + blockdata.getSize();
3898 SharedBuffer<u8> reply(replysize);
3899 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3900 writeS16(&reply[2], p.X);
3901 writeS16(&reply[4], p.Y);
3902 writeS16(&reply[6], p.Z);
3903 memcpy(&reply[8], *blockdata, blockdata.getSize());
3905 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3906 <<": \tpacket size: "<<replysize<<std::endl;*/
3911 m_con.Send(peer_id, 1, reply, true);
3914 void Server::SendBlocks(float dtime)
3916 DSTACK(__FUNCTION_NAME);
3918 JMutexAutoLock envlock(m_env_mutex);
3919 JMutexAutoLock conlock(m_con_mutex);
3921 //TimeTaker timer("Server::SendBlocks");
3923 core::array<PrioritySortedBlockTransfer> queue;
3925 s32 total_sending = 0;
3928 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3930 for(core::map<u16, RemoteClient*>::Iterator
3931 i = m_clients.getIterator();
3932 i.atEnd() == false; i++)
3934 RemoteClient *client = i.getNode()->getValue();
3935 assert(client->peer_id == i.getNode()->getKey());
3937 total_sending += client->SendingCount();
3939 if(client->serialization_version == SER_FMT_VER_INVALID)
3942 client->GetNextBlocks(this, dtime, queue);
3947 // Lowest priority number comes first.
3948 // Lowest is most important.
3951 for(u32 i=0; i<queue.size(); i++)
3953 //TODO: Calculate limit dynamically
3954 if(total_sending >= g_settings->getS32
3955 ("max_simultaneous_block_sends_server_total"))
3958 PrioritySortedBlockTransfer q = queue[i];
3960 MapBlock *block = NULL;
3963 block = m_env.getMap().getBlockNoCreate(q.pos);
3965 catch(InvalidPositionException &e)
3970 RemoteClient *client = getClient(q.peer_id);
3972 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3974 client->SentBlock(q.pos);
3984 void Server::HandlePlayerHP(Player *player, s16 damage)
3986 if(player->hp > damage)
3988 player->hp -= damage;
3989 SendPlayerHP(player);
3993 infostream<<"Server::HandlePlayerHP(): Player "
3994 <<player->getName()<<" dies"<<std::endl;
3998 //TODO: Throw items around
4000 // Handle players that are not connected
4001 if(player->peer_id == PEER_ID_INEXISTENT){
4002 RespawnPlayer(player);
4006 SendPlayerHP(player);
4008 RemoteClient *client = getClient(player->peer_id);
4009 if(client->net_proto_version >= 3)
4011 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4015 RespawnPlayer(player);
4020 void Server::RespawnPlayer(Player *player)
4022 v3f pos = findSpawnPos(m_env.getServerMap());
4023 player->setPosition(pos);
4025 SendMovePlayer(player);
4026 SendPlayerHP(player);
4029 void Server::UpdateCrafting(u16 peer_id)
4031 DSTACK(__FUNCTION_NAME);
4033 Player* player = m_env.getPlayer(peer_id);
4037 Calculate crafting stuff
4039 if(g_settings->getBool("creative_mode") == false)
4041 InventoryList *clist = player->inventory.getList("craft");
4042 InventoryList *rlist = player->inventory.getList("craftresult");
4044 if(rlist && rlist->getUsedSlots() == 0)
4045 player->craftresult_is_preview = true;
4047 if(rlist && player->craftresult_is_preview)
4049 rlist->clearItems();
4051 if(clist && rlist && player->craftresult_is_preview)
4053 InventoryItem *items[9];
4054 for(u16 i=0; i<9; i++)
4056 items[i] = clist->getItem(i);
4059 // Get result of crafting grid
4060 InventoryItem *result = craft_get_result(items);
4062 rlist->addItem(result);
4065 } // if creative_mode == false
4068 RemoteClient* Server::getClient(u16 peer_id)
4070 DSTACK(__FUNCTION_NAME);
4071 //JMutexAutoLock lock(m_con_mutex);
4072 core::map<u16, RemoteClient*>::Node *n;
4073 n = m_clients.find(peer_id);
4074 // A client should exist for all peers
4076 return n->getValue();
4079 std::wstring Server::getStatusString()
4081 std::wostringstream os(std::ios_base::binary);
4084 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4086 os<<L", uptime="<<m_uptime.get();
4087 // Information about clients
4089 for(core::map<u16, RemoteClient*>::Iterator
4090 i = m_clients.getIterator();
4091 i.atEnd() == false; i++)
4093 // Get client and check that it is valid
4094 RemoteClient *client = i.getNode()->getValue();
4095 assert(client->peer_id == i.getNode()->getKey());
4096 if(client->serialization_version == SER_FMT_VER_INVALID)
4099 Player *player = m_env.getPlayer(client->peer_id);
4100 // Get name of player
4101 std::wstring name = L"unknown";
4103 name = narrow_to_wide(player->getName());
4104 // Add name to information string
4108 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4109 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4110 if(g_settings->get("motd") != "")
4111 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4115 // Saves g_settings to configpath given at initialization
4116 void Server::saveConfig()
4118 if(m_configpath != "")
4119 g_settings->updateConfigFile(m_configpath.c_str());
4122 void Server::notifyPlayer(const char *name, const std::wstring msg)
4124 Player *player = m_env.getPlayer(name);
4127 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4130 void Server::notifyPlayers(const std::wstring msg)
4132 BroadcastChatMessage(msg);
4135 v3f findSpawnPos(ServerMap &map)
4137 //return v3f(50,50,50)*BS;
4142 nodepos = v2s16(0,0);
4147 // Try to find a good place a few times
4148 for(s32 i=0; i<1000; i++)
4151 // We're going to try to throw the player to this position
4152 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4153 -range + (myrand()%(range*2)));
4154 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4155 // Get ground height at point (fallbacks to heightmap function)
4156 s16 groundheight = map.findGroundLevel(nodepos2d);
4157 // Don't go underwater
4158 if(groundheight < WATER_LEVEL)
4160 //infostream<<"-> Underwater"<<std::endl;
4163 // Don't go to high places
4164 if(groundheight > WATER_LEVEL + 4)
4166 //infostream<<"-> Underwater"<<std::endl;
4170 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4171 bool is_good = false;
4173 for(s32 i=0; i<10; i++){
4174 v3s16 blockpos = getNodeBlockPos(nodepos);
4175 map.emergeBlock(blockpos, true);
4176 MapNode n = map.getNodeNoEx(nodepos);
4177 if(n.getContent() == CONTENT_AIR){
4188 // Found a good place
4189 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4195 return intToFloat(nodepos, BS);
4198 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4201 Try to get an existing player
4203 Player *player = m_env.getPlayer(name);
4206 // If player is already connected, cancel
4207 if(player->peer_id != 0)
4209 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4214 player->peer_id = peer_id;
4216 // Reset inventory to creative if in creative mode
4217 if(g_settings->getBool("creative_mode"))
4219 // Warning: double code below
4220 // Backup actual inventory
4221 player->inventory_backup = new Inventory();
4222 *(player->inventory_backup) = player->inventory;
4223 // Set creative inventory
4224 craft_set_creative_inventory(player);
4231 If player with the wanted peer_id already exists, cancel.
4233 if(m_env.getPlayer(peer_id) != NULL)
4235 infostream<<"emergePlayer(): Player with wrong name but same"
4236 " peer_id already exists"<<std::endl;
4244 player = new ServerRemotePlayer();
4245 //player->peer_id = c.peer_id;
4246 //player->peer_id = PEER_ID_INEXISTENT;
4247 player->peer_id = peer_id;
4248 player->updateName(name);
4249 m_authmanager.add(name);
4250 m_authmanager.setPassword(name, password);
4251 m_authmanager.setPrivs(name,
4252 stringToPrivs(g_settings->get("default_privs")));
4258 infostream<<"Server: Finding spawn place for player \""
4259 <<player->getName()<<"\""<<std::endl;
4261 v3f pos = findSpawnPos(m_env.getServerMap());
4263 player->setPosition(pos);
4266 Add player to environment
4269 m_env.addPlayer(player);
4272 Add stuff to inventory
4275 if(g_settings->getBool("creative_mode"))
4277 // Warning: double code above
4278 // Backup actual inventory
4279 player->inventory_backup = new Inventory();
4280 *(player->inventory_backup) = player->inventory;
4281 // Set creative inventory
4282 craft_set_creative_inventory(player);
4284 else if(g_settings->getBool("give_initial_stuff"))
4286 craft_give_initial_stuff(player);
4291 } // create new player
4294 void Server::handlePeerChange(PeerChange &c)
4296 JMutexAutoLock envlock(m_env_mutex);
4297 JMutexAutoLock conlock(m_con_mutex);
4299 if(c.type == PEER_ADDED)
4306 core::map<u16, RemoteClient*>::Node *n;
4307 n = m_clients.find(c.peer_id);
4308 // The client shouldn't already exist
4312 RemoteClient *client = new RemoteClient();
4313 client->peer_id = c.peer_id;
4314 m_clients.insert(client->peer_id, client);
4317 else if(c.type == PEER_REMOVED)
4324 core::map<u16, RemoteClient*>::Node *n;
4325 n = m_clients.find(c.peer_id);
4326 // The client should exist
4330 Mark objects to be not known by the client
4332 RemoteClient *client = n->getValue();
4334 for(core::map<u16, bool>::Iterator
4335 i = client->m_known_objects.getIterator();
4336 i.atEnd()==false; i++)
4339 u16 id = i.getNode()->getKey();
4340 ServerActiveObject* obj = m_env.getActiveObject(id);
4342 if(obj && obj->m_known_by_count > 0)
4343 obj->m_known_by_count--;
4346 // Collect information about leaving in chat
4347 std::wstring message;
4349 Player *player = m_env.getPlayer(c.peer_id);
4352 std::wstring name = narrow_to_wide(player->getName());
4355 message += L" left game";
4357 message += L" (timed out)";
4363 m_env.removePlayer(c.peer_id);
4366 // Set player client disconnected
4368 Player *player = m_env.getPlayer(c.peer_id);
4370 player->peer_id = 0;
4377 std::ostringstream os(std::ios_base::binary);
4378 for(core::map<u16, RemoteClient*>::Iterator
4379 i = m_clients.getIterator();
4380 i.atEnd() == false; i++)
4382 RemoteClient *client = i.getNode()->getValue();
4383 assert(client->peer_id == i.getNode()->getKey());
4384 if(client->serialization_version == SER_FMT_VER_INVALID)
4387 Player *player = m_env.getPlayer(client->peer_id);
4390 // Get name of player
4391 os<<player->getName()<<" ";
4394 actionstream<<player->getName()<<" "
4395 <<(c.timeout?"times out.":"leaves game.")
4396 <<" List of players: "
4397 <<os.str()<<std::endl;
4402 delete m_clients[c.peer_id];
4403 m_clients.remove(c.peer_id);
4405 // Send player info to all remaining clients
4408 // Send leave chat message to all remaining clients
4409 BroadcastChatMessage(message);
4418 void Server::handlePeerChanges()
4420 while(m_peer_change_queue.size() > 0)
4422 PeerChange c = m_peer_change_queue.pop_front();
4424 infostream<<"Server: Handling peer change: "
4425 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4428 handlePeerChange(c);
4432 u64 Server::getPlayerPrivs(Player *player)
4436 std::string playername = player->getName();
4437 // Local player gets all privileges regardless of
4438 // what's set on their account.
4439 if(g_settings->get("name") == playername)
4445 return getPlayerAuthPrivs(playername);
4449 void dedicated_server_loop(Server &server, bool &kill)
4451 DSTACK(__FUNCTION_NAME);
4453 infostream<<DTIME<<std::endl;
4454 infostream<<"========================"<<std::endl;
4455 infostream<<"Running dedicated server"<<std::endl;
4456 infostream<<"========================"<<std::endl;
4457 infostream<<std::endl;
4459 IntervalLimiter m_profiler_interval;
4463 // This is kind of a hack but can be done like this
4464 // because server.step() is very light
4466 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4471 if(server.getShutdownRequested() || kill)
4473 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4480 float profiler_print_interval =
4481 g_settings->getFloat("profiler_print_interval");
4482 if(profiler_print_interval != 0)
4484 if(m_profiler_interval.step(0.030, profiler_print_interval))
4486 infostream<<"Profiler:"<<std::endl;
4487 g_profiler->print(infostream);
4488 g_profiler->clear();
4495 static int counter = 0;
4501 core::list<PlayerInfo> list = server.getPlayerInfo();
4502 core::list<PlayerInfo>::Iterator i;
4503 static u32 sum_old = 0;
4504 u32 sum = PIChecksum(list);
4507 infostream<<DTIME<<"Player info:"<<std::endl;
4508 for(i=list.begin(); i!=list.end(); i++)
4510 i->PrintLine(&infostream);