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.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
30 #include "materials.h"
33 #include "servercommand.h"
35 #include "content_mapnode.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
48 #include "content_abm.h"
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
55 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
57 class MapEditEventIgnorer
60 MapEditEventIgnorer(bool *flag):
69 ~MapEditEventIgnorer()
82 void * ServerThread::Thread()
86 log_register_thread("ServerThread");
88 DSTACK(__FUNCTION_NAME);
90 BEGIN_DEBUG_EXCEPTION_HANDLER
95 //TimeTaker timer("AsyncRunStep() + Receive()");
98 //TimeTaker timer("AsyncRunStep()");
99 m_server->AsyncRunStep();
102 //infostream<<"Running m_server->Receive()"<<std::endl;
105 catch(con::NoIncomingDataException &e)
108 catch(con::PeerNotFoundException &e)
110 infostream<<"Server: PeerNotFoundException"<<std::endl;
114 END_DEBUG_EXCEPTION_HANDLER(errorstream)
119 void * EmergeThread::Thread()
123 log_register_thread("EmergeThread");
125 DSTACK(__FUNCTION_NAME);
127 BEGIN_DEBUG_EXCEPTION_HANDLER
129 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
132 Get block info from queue, emerge them and send them
135 After queue is empty, exit.
139 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
143 SharedPtr<QueuedBlockEmerge> q(qptr);
149 Do not generate over-limit
151 if(blockpos_over_limit(p))
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 MapBlock *block = NULL;
196 bool got_block = true;
197 core::map<v3s16, MapBlock*> modified_blocks;
200 Try to fetch block from memory or disk.
201 If not found and asked to generate, initialize generator.
204 bool started_generate = false;
205 mapgen::BlockMakeData data;
208 JMutexAutoLock envlock(m_server->m_env_mutex);
210 // Load sector if it isn't loaded
211 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
212 map.loadSectorMeta(p2d);
214 // Attempt to load block
215 block = map.getBlockNoCreateNoEx(p);
216 if(!block || block->isDummy() || !block->isGenerated())
218 if(enable_mapgen_debug_info)
219 infostream<<"EmergeThread: not in memory, "
220 <<"attempting to load from disk"<<std::endl;
222 block = map.loadBlock(p);
225 // If could not load and allowed to generate, start generation
226 // inside this same envlock
227 if(only_from_disk == false &&
228 (block == NULL || block->isGenerated() == false)){
229 if(enable_mapgen_debug_info)
230 infostream<<"EmergeThread: generating"<<std::endl;
231 started_generate = true;
233 map.initBlockMake(&data, p);
238 If generator was initialized, generate now when envlock is free.
243 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
245 TimeTaker t("mapgen::make_block()");
247 mapgen::make_block(&data);
249 if(enable_mapgen_debug_info == false)
250 t.stop(true); // Hide output
254 // Lock environment again to access the map
255 JMutexAutoLock envlock(m_server->m_env_mutex);
257 ScopeProfiler sp(g_profiler, "EmergeThread: after "
258 "mapgen::make_block (envlock)", SPT_AVG);
260 // Blit data back on map, update lighting, add mobs and
261 // whatever this does
262 map.finishBlockMake(&data, modified_blocks);
265 block = map.getBlockNoCreateNoEx(p);
267 // If block doesn't exist, don't try doing anything with it
268 // This happens if the block is not in generation boundaries
273 Do some post-generate stuff
276 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
277 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
278 scriptapi_environment_on_generated(m_server->m_lua,
281 if(enable_mapgen_debug_info)
282 infostream<<"EmergeThread: ended up with: "
283 <<analyze_block(block)<<std::endl;
286 Ignore map edit events, they will not need to be
287 sent to anybody because the block hasn't been sent
290 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
292 // Activate objects and stuff
293 m_server->m_env->activateBlock(block, 0);
301 Set sent status of modified blocks on clients
304 // NOTE: Server's clients are also behind the connection mutex
305 JMutexAutoLock lock(m_server->m_con_mutex);
308 Add the originally fetched block to the modified list
312 modified_blocks.insert(p, block);
316 Set the modified blocks unsent for all the clients
319 for(core::map<u16, RemoteClient*>::Iterator
320 i = m_server->m_clients.getIterator();
321 i.atEnd() == false; i++)
323 RemoteClient *client = i.getNode()->getValue();
325 if(modified_blocks.size() > 0)
327 // Remove block from sent history
328 client->SetBlocksNotSent(modified_blocks);
334 END_DEBUG_EXCEPTION_HANDLER(errorstream)
336 log_deregister_thread();
341 void RemoteClient::GetNextBlocks(Server *server, float dtime,
342 core::array<PrioritySortedBlockTransfer> &dest)
344 DSTACK(__FUNCTION_NAME);
347 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
350 m_nothing_to_send_pause_timer -= dtime;
351 m_nearest_unsent_reset_timer += dtime;
353 if(m_nothing_to_send_pause_timer >= 0)
358 // Won't send anything if already sending
359 if(m_blocks_sending.size() >= g_settings->getU16
360 ("max_simultaneous_block_sends_per_client"))
362 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
366 //TimeTaker timer("RemoteClient::GetNextBlocks");
368 Player *player = server->m_env->getPlayer(peer_id);
370 assert(player != NULL);
372 v3f playerpos = player->getPosition();
373 v3f playerspeed = player->getSpeed();
374 v3f playerspeeddir(0,0,0);
375 if(playerspeed.getLength() > 1.0*BS)
376 playerspeeddir = playerspeed / playerspeed.getLength();
377 // Predict to next block
378 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
380 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
382 v3s16 center = getNodeBlockPos(center_nodepos);
384 // Camera position and direction
385 v3f camera_pos = player->getEyePosition();
386 v3f camera_dir = v3f(0,0,1);
387 camera_dir.rotateYZBy(player->getPitch());
388 camera_dir.rotateXZBy(player->getYaw());
390 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
391 <<camera_dir.Z<<")"<<std::endl;*/
394 Get the starting value of the block finder radius.
397 if(m_last_center != center)
399 m_nearest_unsent_d = 0;
400 m_last_center = center;
403 /*infostream<<"m_nearest_unsent_reset_timer="
404 <<m_nearest_unsent_reset_timer<<std::endl;*/
406 // Reset periodically to workaround for some bugs or stuff
407 if(m_nearest_unsent_reset_timer > 20.0)
409 m_nearest_unsent_reset_timer = 0;
410 m_nearest_unsent_d = 0;
411 //infostream<<"Resetting m_nearest_unsent_d for "
412 // <<server->getPlayerName(peer_id)<<std::endl;
415 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
416 s16 d_start = m_nearest_unsent_d;
418 //infostream<<"d_start="<<d_start<<std::endl;
420 u16 max_simul_sends_setting = g_settings->getU16
421 ("max_simultaneous_block_sends_per_client");
422 u16 max_simul_sends_usually = max_simul_sends_setting;
425 Check the time from last addNode/removeNode.
427 Decrease send rate if player is building stuff.
429 m_time_from_building += dtime;
430 if(m_time_from_building < g_settings->getFloat(
431 "full_block_send_enable_min_time_from_building"))
433 max_simul_sends_usually
434 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
438 Number of blocks sending + number of blocks selected for sending
440 u32 num_blocks_selected = m_blocks_sending.size();
443 next time d will be continued from the d from which the nearest
444 unsent block was found this time.
446 This is because not necessarily any of the blocks found this
447 time are actually sent.
449 s32 new_nearest_unsent_d = -1;
451 s16 d_max = g_settings->getS16("max_block_send_distance");
452 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
454 // Don't loop very much at a time
455 s16 max_d_increment_at_time = 2;
456 if(d_max > d_start + max_d_increment_at_time)
457 d_max = d_start + max_d_increment_at_time;
458 /*if(d_max_gen > d_start+2)
459 d_max_gen = d_start+2;*/
461 //infostream<<"Starting from "<<d_start<<std::endl;
463 s32 nearest_emerged_d = -1;
464 s32 nearest_emergefull_d = -1;
465 s32 nearest_sent_d = -1;
466 bool queue_is_full = false;
469 for(d = d_start; d <= d_max; d++)
471 /*errorstream<<"checking d="<<d<<" for "
472 <<server->getPlayerName(peer_id)<<std::endl;*/
473 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
476 If m_nearest_unsent_d was changed by the EmergeThread
477 (it can change it to 0 through SetBlockNotSent),
479 Else update m_nearest_unsent_d
481 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
483 d = m_nearest_unsent_d;
484 last_nearest_unsent_d = m_nearest_unsent_d;
488 Get the border/face dot coordinates of a "d-radiused"
491 core::list<v3s16> list;
492 getFacePositions(list, d);
494 core::list<v3s16>::Iterator li;
495 for(li=list.begin(); li!=list.end(); li++)
497 v3s16 p = *li + center;
501 - Don't allow too many simultaneous transfers
502 - EXCEPT when the blocks are very close
504 Also, don't send blocks that are already flying.
507 // Start with the usual maximum
508 u16 max_simul_dynamic = max_simul_sends_usually;
510 // If block is very close, allow full maximum
511 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
512 max_simul_dynamic = max_simul_sends_setting;
514 // Don't select too many blocks for sending
515 if(num_blocks_selected >= max_simul_dynamic)
517 queue_is_full = true;
518 goto queue_full_break;
521 // Don't send blocks that are currently being transferred
522 if(m_blocks_sending.find(p) != NULL)
528 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
529 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
536 // If this is true, inexistent block will be made from scratch
537 bool generate = d <= d_max_gen;
540 /*// Limit the generating area vertically to 2/3
541 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
544 // Limit the send area vertically to 1/2
545 if(abs(p.Y - center.Y) > d_max / 2)
551 If block is far away, don't generate it unless it is
557 // Block center y in nodes
558 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
559 // Don't generate if it's very high or very low
560 if(y < -64 || y > 64)
564 v2s16 p2d_nodes_center(
568 // Get ground height in nodes
569 s16 gh = server->m_env->getServerMap().findGroundLevel(
572 // If differs a lot, don't generate
573 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
575 // Actually, don't even send it
581 //infostream<<"d="<<d<<std::endl;
584 Don't generate or send if not in sight
585 FIXME This only works if the client uses a small enough
586 FOV setting. The default of 72 degrees is fine.
589 float camera_fov = (72.0*PI/180) * 4./3.;
590 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
596 Don't send already sent blocks
599 if(m_blocks_sent.find(p) != NULL)
606 Check if map has this block
608 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
610 bool surely_not_found_on_disk = false;
611 bool block_is_invalid = false;
614 // Reset usage timer, this block will be of use in the future.
615 block->resetUsageTimer();
617 // Block is dummy if data doesn't exist.
618 // It means it has been not found from disk and not generated
621 surely_not_found_on_disk = true;
624 // Block is valid if lighting is up-to-date and data exists
625 if(block->isValid() == false)
627 block_is_invalid = true;
630 /*if(block->isFullyGenerated() == false)
632 block_is_invalid = true;
637 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
638 v2s16 chunkpos = map->sector_to_chunk(p2d);
639 if(map->chunkNonVolatile(chunkpos) == false)
640 block_is_invalid = true;
642 if(block->isGenerated() == false)
643 block_is_invalid = true;
646 If block is not close, don't send it unless it is near
649 Block is near ground level if night-time mesh
650 differs from day-time mesh.
654 if(block->dayNightDiffed() == false)
661 If block has been marked to not exist on disk (dummy)
662 and generating new ones is not wanted, skip block.
664 if(generate == false && surely_not_found_on_disk == true)
671 Add inexistent block to emerge queue.
673 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
675 //TODO: Get value from somewhere
676 // Allow only one block in emerge queue
677 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
678 // Allow two blocks in queue per client
679 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
681 // Make it more responsive when needing to generate stuff
682 if(surely_not_found_on_disk)
684 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
686 //infostream<<"Adding block to emerge queue"<<std::endl;
688 // Add it to the emerge queue and trigger the thread
691 if(generate == false)
692 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
694 server->m_emerge_queue.addBlock(peer_id, p, flags);
695 server->m_emergethread.trigger();
697 if(nearest_emerged_d == -1)
698 nearest_emerged_d = d;
700 if(nearest_emergefull_d == -1)
701 nearest_emergefull_d = d;
708 if(nearest_sent_d == -1)
712 Add block to send queue
715 /*errorstream<<"sending from d="<<d<<" to "
716 <<server->getPlayerName(peer_id)<<std::endl;*/
718 PrioritySortedBlockTransfer q((float)d, p, peer_id);
722 num_blocks_selected += 1;
727 //infostream<<"Stopped at "<<d<<std::endl;
729 // If nothing was found for sending and nothing was queued for
730 // emerging, continue next time browsing from here
731 if(nearest_emerged_d != -1){
732 new_nearest_unsent_d = nearest_emerged_d;
733 } else if(nearest_emergefull_d != -1){
734 new_nearest_unsent_d = nearest_emergefull_d;
736 if(d > g_settings->getS16("max_block_send_distance")){
737 new_nearest_unsent_d = 0;
738 m_nothing_to_send_pause_timer = 2.0;
739 /*infostream<<"GetNextBlocks(): d wrapped around for "
740 <<server->getPlayerName(peer_id)
741 <<"; setting to 0 and pausing"<<std::endl;*/
743 if(nearest_sent_d != -1)
744 new_nearest_unsent_d = nearest_sent_d;
746 new_nearest_unsent_d = d;
750 if(new_nearest_unsent_d != -1)
751 m_nearest_unsent_d = new_nearest_unsent_d;
753 /*timer_result = timer.stop(true);
754 if(timer_result != 0)
755 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
758 void RemoteClient::GotBlock(v3s16 p)
760 if(m_blocks_sending.find(p) != NULL)
761 m_blocks_sending.remove(p);
764 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
765 " m_blocks_sending"<<std::endl;*/
766 m_excess_gotblocks++;
768 m_blocks_sent.insert(p, true);
771 void RemoteClient::SentBlock(v3s16 p)
773 if(m_blocks_sending.find(p) == NULL)
774 m_blocks_sending.insert(p, 0.0);
776 infostream<<"RemoteClient::SentBlock(): Sent block"
777 " already in m_blocks_sending"<<std::endl;
780 void RemoteClient::SetBlockNotSent(v3s16 p)
782 m_nearest_unsent_d = 0;
784 if(m_blocks_sending.find(p) != NULL)
785 m_blocks_sending.remove(p);
786 if(m_blocks_sent.find(p) != NULL)
787 m_blocks_sent.remove(p);
790 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
792 m_nearest_unsent_d = 0;
794 for(core::map<v3s16, MapBlock*>::Iterator
795 i = blocks.getIterator();
796 i.atEnd()==false; i++)
798 v3s16 p = i.getNode()->getKey();
800 if(m_blocks_sending.find(p) != NULL)
801 m_blocks_sending.remove(p);
802 if(m_blocks_sent.find(p) != NULL)
803 m_blocks_sent.remove(p);
811 PlayerInfo::PlayerInfo()
817 void PlayerInfo::PrintLine(std::ostream *s)
820 (*s)<<"\""<<name<<"\" ("
821 <<(position.X/10)<<","<<(position.Y/10)
822 <<","<<(position.Z/10)<<") ";
824 (*s)<<" avg_rtt="<<avg_rtt;
828 u32 PIChecksum(core::list<PlayerInfo> &l)
830 core::list<PlayerInfo>::Iterator i;
833 for(i=l.begin(); i!=l.end(); i++)
835 checksum += a * (i->id+1);
836 checksum ^= 0x435aafcd;
847 std::string mapsavedir,
848 std::string configpath
851 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
852 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
853 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
855 m_itemdef(createItemDefManager()),
856 m_nodedef(createNodeDefManager()),
857 m_craftdef(createCraftDefManager()),
859 m_emergethread(this),
861 m_time_of_day_send_timer(0),
863 m_mapsavedir(mapsavedir),
864 m_configpath(configpath),
865 m_shutdown_requested(false),
866 m_ignore_map_edit_events(false),
867 m_ignore_map_edit_events_peer_id(0)
869 m_liquid_transform_timer = 0.0;
870 m_print_info_timer = 0.0;
871 m_objectdata_timer = 0.0;
872 m_emergethread_trigger_timer = 0.0;
873 m_savemap_timer = 0.0;
877 m_step_dtime_mutex.Init();
880 JMutexAutoLock envlock(m_env_mutex);
881 JMutexAutoLock conlock(m_con_mutex);
883 // Path to builtin.lua
884 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
886 // Add default global mod search path
887 m_modspaths.push_front(porting::path_data + DIR_DELIM + "mods");
888 // Add world mod search path
889 m_modspaths.push_front(mapsavedir + DIR_DELIM + "worldmods");
890 // Add user mod search path
891 m_modspaths.push_front(porting::path_userdata + DIR_DELIM + "usermods");
893 // Print out mod search paths
894 infostream<<"Mod search paths:"<<std::endl;
895 for(core::list<std::string>::Iterator i = m_modspaths.begin();
896 i != m_modspaths.end(); i++){
897 std::string modspath = *i;
898 infostream<<" "<<modspath<<std::endl;
901 // Initialize scripting
903 infostream<<"Server: Initializing scripting"<<std::endl;
904 m_lua = script_init();
907 scriptapi_export(m_lua, this);
908 // Load and run builtin.lua
909 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
911 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
913 errorstream<<"Server: Failed to load and run "
914 <<builtinpath<<std::endl;
915 throw ModError("Failed to load and run "+builtinpath);
917 // Load and run "mod" scripts
918 m_mods = getMods(m_modspaths);
919 for(core::list<ModSpec>::Iterator i = m_mods.begin();
920 i != m_mods.end(); i++){
921 const ModSpec &mod = *i;
922 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
923 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
924 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
926 errorstream<<"Server: Failed to load and run "
927 <<scriptpath<<std::endl;
928 throw ModError("Failed to load and run "+scriptpath);
932 // Read Textures and calculate sha1 sums
935 // Apply item aliases in the node definition manager
936 m_nodedef->updateAliases(m_itemdef);
938 // Initialize Environment
940 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
943 // Give environment reference to scripting api
944 scriptapi_add_environment(m_lua, m_env);
946 // Register us to receive map edit events
947 m_env->getMap().addEventReceiver(this);
949 // If file exists, load environment metadata
950 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
952 infostream<<"Server: Loading environment metadata"<<std::endl;
953 m_env->loadMeta(m_mapsavedir);
957 infostream<<"Server: Loading players"<<std::endl;
958 m_env->deSerializePlayers(m_mapsavedir);
961 Add some test ActiveBlockModifiers to environment
963 add_legacy_abms(m_env, m_nodedef);
968 infostream<<"Server::~Server()"<<std::endl;
971 Send shutdown message
974 JMutexAutoLock conlock(m_con_mutex);
976 std::wstring line = L"*** Server shutting down";
979 Send the message to clients
981 for(core::map<u16, RemoteClient*>::Iterator
982 i = m_clients.getIterator();
983 i.atEnd() == false; i++)
985 // Get client and check that it is valid
986 RemoteClient *client = i.getNode()->getValue();
987 assert(client->peer_id == i.getNode()->getKey());
988 if(client->serialization_version == SER_FMT_VER_INVALID)
992 SendChatMessage(client->peer_id, line);
994 catch(con::PeerNotFoundException &e)
1000 JMutexAutoLock envlock(m_env_mutex);
1005 infostream<<"Server: Saving players"<<std::endl;
1006 m_env->serializePlayers(m_mapsavedir);
1009 Save environment metadata
1011 infostream<<"Server: Saving environment metadata"<<std::endl;
1012 m_env->saveMeta(m_mapsavedir);
1024 JMutexAutoLock clientslock(m_con_mutex);
1026 for(core::map<u16, RemoteClient*>::Iterator
1027 i = m_clients.getIterator();
1028 i.atEnd() == false; i++)
1031 // NOTE: These are removed by env destructor
1033 u16 peer_id = i.getNode()->getKey();
1034 JMutexAutoLock envlock(m_env_mutex);
1035 m_env->removePlayer(peer_id);
1039 delete i.getNode()->getValue();
1043 // Delete Environment
1050 // Deinitialize scripting
1051 infostream<<"Server: Deinitializing scripting"<<std::endl;
1052 script_deinit(m_lua);
1055 void Server::start(unsigned short port)
1057 DSTACK(__FUNCTION_NAME);
1058 // Stop thread if already running
1061 // Initialize connection
1062 m_con.SetTimeoutMs(30);
1066 m_thread.setRun(true);
1069 infostream<<"Server: Started on port "<<port<<std::endl;
1074 DSTACK(__FUNCTION_NAME);
1076 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1078 // Stop threads (set run=false first so both start stopping)
1079 m_thread.setRun(false);
1080 m_emergethread.setRun(false);
1082 m_emergethread.stop();
1084 infostream<<"Server: Threads stopped"<<std::endl;
1087 void Server::step(float dtime)
1089 DSTACK(__FUNCTION_NAME);
1094 JMutexAutoLock lock(m_step_dtime_mutex);
1095 m_step_dtime += dtime;
1099 void Server::AsyncRunStep()
1101 DSTACK(__FUNCTION_NAME);
1103 g_profiler->add("Server::AsyncRunStep (num)", 1);
1107 JMutexAutoLock lock1(m_step_dtime_mutex);
1108 dtime = m_step_dtime;
1112 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1113 // Send blocks to clients
1120 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1122 //infostream<<"Server steps "<<dtime<<std::endl;
1123 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1126 JMutexAutoLock lock1(m_step_dtime_mutex);
1127 m_step_dtime -= dtime;
1134 m_uptime.set(m_uptime.get() + dtime);
1138 // Process connection's timeouts
1139 JMutexAutoLock lock2(m_con_mutex);
1140 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1141 m_con.RunTimeouts(dtime);
1145 // This has to be called so that the client list gets synced
1146 // with the peer list of the connection
1147 handlePeerChanges();
1151 Update m_time_of_day and overall game time
1154 JMutexAutoLock envlock(m_env_mutex);
1156 m_time_counter += dtime;
1157 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1158 u32 units = (u32)(m_time_counter*speed);
1159 m_time_counter -= (f32)units / speed;
1161 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1163 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1166 Send to clients at constant intervals
1169 m_time_of_day_send_timer -= dtime;
1170 if(m_time_of_day_send_timer < 0.0)
1172 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1174 //JMutexAutoLock envlock(m_env_mutex);
1175 JMutexAutoLock conlock(m_con_mutex);
1177 for(core::map<u16, RemoteClient*>::Iterator
1178 i = m_clients.getIterator();
1179 i.atEnd() == false; i++)
1181 RemoteClient *client = i.getNode()->getValue();
1182 //Player *player = m_env->getPlayer(client->peer_id);
1184 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1185 m_env->getTimeOfDay());
1187 m_con.Send(client->peer_id, 0, data, true);
1193 JMutexAutoLock lock(m_env_mutex);
1195 ScopeProfiler sp(g_profiler, "SEnv step");
1196 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1200 const float map_timer_and_unload_dtime = 2.92;
1201 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1203 JMutexAutoLock lock(m_env_mutex);
1204 // Run Map's timers and unload unused data
1205 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1206 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1207 g_settings->getFloat("server_unload_unused_data_timeout"));
1218 JMutexAutoLock lock(m_env_mutex);
1219 JMutexAutoLock lock2(m_con_mutex);
1221 ScopeProfiler sp(g_profiler, "Server: handle players");
1223 //float player_max_speed = BS * 4.0; // Normal speed
1224 float player_max_speed = BS * 20; // Fast speed
1225 float player_max_speed_up = BS * 20;
1227 player_max_speed *= 2.5; // Tolerance
1228 player_max_speed_up *= 2.5;
1230 for(core::map<u16, RemoteClient*>::Iterator
1231 i = m_clients.getIterator();
1232 i.atEnd() == false; i++)
1234 RemoteClient *client = i.getNode()->getValue();
1235 ServerRemotePlayer *player =
1236 static_cast<ServerRemotePlayer*>
1237 (m_env->getPlayer(client->peer_id));
1242 Check player movements
1244 NOTE: Actually the server should handle player physics like the
1245 client does and compare player's position to what is calculated
1246 on our side. This is required when eg. players fly due to an
1249 player->m_last_good_position_age += dtime;
1250 if(player->m_last_good_position_age >= 2.0){
1251 float age = player->m_last_good_position_age;
1252 v3f diff = (player->getPosition() - player->m_last_good_position);
1253 float d_vert = diff.Y;
1255 float d_horiz = diff.getLength();
1256 /*infostream<<player->getName()<<"'s horizontal speed is "
1257 <<(d_horiz/age)<<std::endl;*/
1258 if(d_horiz <= age * player_max_speed &&
1259 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1260 player->m_last_good_position = player->getPosition();
1262 actionstream<<"Player "<<player->getName()
1263 <<" moved too fast; resetting position"
1265 player->setPosition(player->m_last_good_position);
1266 SendMovePlayer(player);
1268 player->m_last_good_position_age = 0;
1272 Handle player HPs (die if hp=0)
1274 HandlePlayerHP(player, 0);
1277 Send player inventories and HPs if necessary
1279 if(player->m_inventory_not_sent){
1280 UpdateCrafting(player->peer_id);
1281 SendInventory(player->peer_id);
1283 if(player->m_hp_not_sent){
1284 SendPlayerHP(player);
1288 Add to environment if is not in respawn screen
1290 if(!player->m_is_in_environment && !player->m_respawn_active){
1291 player->m_removed = false;
1293 m_env->addActiveObject(player);
1298 /* Transform liquids */
1299 m_liquid_transform_timer += dtime;
1300 if(m_liquid_transform_timer >= 1.00)
1302 m_liquid_transform_timer -= 1.00;
1304 JMutexAutoLock lock(m_env_mutex);
1306 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1308 core::map<v3s16, MapBlock*> modified_blocks;
1309 m_env->getMap().transformLiquids(modified_blocks);
1314 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1315 ServerMap &map = ((ServerMap&)m_env->getMap());
1316 map.updateLighting(modified_blocks, lighting_modified_blocks);
1318 // Add blocks modified by lighting to modified_blocks
1319 for(core::map<v3s16, MapBlock*>::Iterator
1320 i = lighting_modified_blocks.getIterator();
1321 i.atEnd() == false; i++)
1323 MapBlock *block = i.getNode()->getValue();
1324 modified_blocks.insert(block->getPos(), block);
1328 Set the modified blocks unsent for all the clients
1331 JMutexAutoLock lock2(m_con_mutex);
1333 for(core::map<u16, RemoteClient*>::Iterator
1334 i = m_clients.getIterator();
1335 i.atEnd() == false; i++)
1337 RemoteClient *client = i.getNode()->getValue();
1339 if(modified_blocks.size() > 0)
1341 // Remove block from sent history
1342 client->SetBlocksNotSent(modified_blocks);
1347 // Periodically print some info
1349 float &counter = m_print_info_timer;
1355 JMutexAutoLock lock2(m_con_mutex);
1357 if(m_clients.size() != 0)
1358 infostream<<"Players:"<<std::endl;
1359 for(core::map<u16, RemoteClient*>::Iterator
1360 i = m_clients.getIterator();
1361 i.atEnd() == false; i++)
1363 //u16 peer_id = i.getNode()->getKey();
1364 RemoteClient *client = i.getNode()->getValue();
1365 Player *player = m_env->getPlayer(client->peer_id);
1368 infostream<<"* "<<player->getName()<<"\t";
1369 client->PrintInfo(infostream);
1374 //if(g_settings->getBool("enable_experimental"))
1378 Check added and deleted active objects
1381 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1382 JMutexAutoLock envlock(m_env_mutex);
1383 JMutexAutoLock conlock(m_con_mutex);
1385 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1387 // Radius inside which objects are active
1388 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1389 radius *= MAP_BLOCKSIZE;
1391 for(core::map<u16, RemoteClient*>::Iterator
1392 i = m_clients.getIterator();
1393 i.atEnd() == false; i++)
1395 RemoteClient *client = i.getNode()->getValue();
1397 // If definitions and textures have not been sent, don't
1398 // send objects either
1399 if(!client->definitions_sent)
1402 Player *player = m_env->getPlayer(client->peer_id);
1405 // This can happen if the client timeouts somehow
1406 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1408 <<" has no associated player"<<std::endl;*/
1411 v3s16 pos = floatToInt(player->getPosition(), BS);
1413 core::map<u16, bool> removed_objects;
1414 core::map<u16, bool> added_objects;
1415 m_env->getRemovedActiveObjects(pos, radius,
1416 client->m_known_objects, removed_objects);
1417 m_env->getAddedActiveObjects(pos, radius,
1418 client->m_known_objects, added_objects);
1420 // Ignore if nothing happened
1421 if(removed_objects.size() == 0 && added_objects.size() == 0)
1423 //infostream<<"active objects: none changed"<<std::endl;
1427 std::string data_buffer;
1431 // Handle removed objects
1432 writeU16((u8*)buf, removed_objects.size());
1433 data_buffer.append(buf, 2);
1434 for(core::map<u16, bool>::Iterator
1435 i = removed_objects.getIterator();
1436 i.atEnd()==false; i++)
1439 u16 id = i.getNode()->getKey();
1440 ServerActiveObject* obj = m_env->getActiveObject(id);
1442 // Add to data buffer for sending
1443 writeU16((u8*)buf, i.getNode()->getKey());
1444 data_buffer.append(buf, 2);
1446 // Remove from known objects
1447 client->m_known_objects.remove(i.getNode()->getKey());
1449 if(obj && obj->m_known_by_count > 0)
1450 obj->m_known_by_count--;
1453 // Handle added objects
1454 writeU16((u8*)buf, added_objects.size());
1455 data_buffer.append(buf, 2);
1456 for(core::map<u16, bool>::Iterator
1457 i = added_objects.getIterator();
1458 i.atEnd()==false; i++)
1461 u16 id = i.getNode()->getKey();
1462 ServerActiveObject* obj = m_env->getActiveObject(id);
1465 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1467 infostream<<"WARNING: "<<__FUNCTION_NAME
1468 <<": NULL object"<<std::endl;
1470 type = obj->getType();
1472 // Add to data buffer for sending
1473 writeU16((u8*)buf, id);
1474 data_buffer.append(buf, 2);
1475 writeU8((u8*)buf, type);
1476 data_buffer.append(buf, 1);
1479 data_buffer.append(serializeLongString(
1480 obj->getClientInitializationData()));
1482 data_buffer.append(serializeLongString(""));
1484 // Add to known objects
1485 client->m_known_objects.insert(i.getNode()->getKey(), false);
1488 obj->m_known_by_count++;
1492 SharedBuffer<u8> reply(2 + data_buffer.size());
1493 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1494 memcpy((char*)&reply[2], data_buffer.c_str(),
1495 data_buffer.size());
1497 m_con.Send(client->peer_id, 0, reply, true);
1499 infostream<<"Server: Sent object remove/add: "
1500 <<removed_objects.size()<<" removed, "
1501 <<added_objects.size()<<" added, "
1502 <<"packet size is "<<reply.getSize()<<std::endl;
1507 Collect a list of all the objects known by the clients
1508 and report it back to the environment.
1511 core::map<u16, bool> all_known_objects;
1513 for(core::map<u16, RemoteClient*>::Iterator
1514 i = m_clients.getIterator();
1515 i.atEnd() == false; i++)
1517 RemoteClient *client = i.getNode()->getValue();
1518 // Go through all known objects of client
1519 for(core::map<u16, bool>::Iterator
1520 i = client->m_known_objects.getIterator();
1521 i.atEnd()==false; i++)
1523 u16 id = i.getNode()->getKey();
1524 all_known_objects[id] = true;
1528 m_env->setKnownActiveObjects(whatever);
1534 Send object messages
1537 JMutexAutoLock envlock(m_env_mutex);
1538 JMutexAutoLock conlock(m_con_mutex);
1540 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1543 // Value = data sent by object
1544 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1546 // Get active object messages from environment
1549 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1553 core::list<ActiveObjectMessage>* message_list = NULL;
1554 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1555 n = buffered_messages.find(aom.id);
1558 message_list = new core::list<ActiveObjectMessage>;
1559 buffered_messages.insert(aom.id, message_list);
1563 message_list = n->getValue();
1565 message_list->push_back(aom);
1568 // Route data to every client
1569 for(core::map<u16, RemoteClient*>::Iterator
1570 i = m_clients.getIterator();
1571 i.atEnd()==false; i++)
1573 RemoteClient *client = i.getNode()->getValue();
1574 std::string reliable_data;
1575 std::string unreliable_data;
1576 // Go through all objects in message buffer
1577 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1578 j = buffered_messages.getIterator();
1579 j.atEnd()==false; j++)
1581 // If object is not known by client, skip it
1582 u16 id = j.getNode()->getKey();
1583 if(client->m_known_objects.find(id) == NULL)
1585 // Get message list of object
1586 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1587 // Go through every message
1588 for(core::list<ActiveObjectMessage>::Iterator
1589 k = list->begin(); k != list->end(); k++)
1591 // Compose the full new data with header
1592 ActiveObjectMessage aom = *k;
1593 std::string new_data;
1596 writeU16((u8*)&buf[0], aom.id);
1597 new_data.append(buf, 2);
1599 new_data += serializeString(aom.datastring);
1600 // Add data to buffer
1602 reliable_data += new_data;
1604 unreliable_data += new_data;
1608 reliable_data and unreliable_data are now ready.
1611 if(reliable_data.size() > 0)
1613 SharedBuffer<u8> reply(2 + reliable_data.size());
1614 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1615 memcpy((char*)&reply[2], reliable_data.c_str(),
1616 reliable_data.size());
1618 m_con.Send(client->peer_id, 0, reply, true);
1620 if(unreliable_data.size() > 0)
1622 SharedBuffer<u8> reply(2 + unreliable_data.size());
1623 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1624 memcpy((char*)&reply[2], unreliable_data.c_str(),
1625 unreliable_data.size());
1626 // Send as unreliable
1627 m_con.Send(client->peer_id, 0, reply, false);
1630 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1632 infostream<<"Server: Size of object message data: "
1633 <<"reliable: "<<reliable_data.size()
1634 <<", unreliable: "<<unreliable_data.size()
1639 // Clear buffered_messages
1640 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1641 i = buffered_messages.getIterator();
1642 i.atEnd()==false; i++)
1644 delete i.getNode()->getValue();
1648 } // enable_experimental
1651 Send queued-for-sending map edit events.
1654 // Don't send too many at a time
1657 // Single change sending is disabled if queue size is not small
1658 bool disable_single_change_sending = false;
1659 if(m_unsent_map_edit_queue.size() >= 4)
1660 disable_single_change_sending = true;
1662 bool got_any_events = false;
1664 // We'll log the amount of each
1667 while(m_unsent_map_edit_queue.size() != 0)
1669 got_any_events = true;
1671 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1673 // Players far away from the change are stored here.
1674 // Instead of sending the changes, MapBlocks are set not sent
1676 core::list<u16> far_players;
1678 if(event->type == MEET_ADDNODE)
1680 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1681 prof.add("MEET_ADDNODE", 1);
1682 if(disable_single_change_sending)
1683 sendAddNode(event->p, event->n, event->already_known_by_peer,
1686 sendAddNode(event->p, event->n, event->already_known_by_peer,
1689 else if(event->type == MEET_REMOVENODE)
1691 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1692 prof.add("MEET_REMOVENODE", 1);
1693 if(disable_single_change_sending)
1694 sendRemoveNode(event->p, event->already_known_by_peer,
1697 sendRemoveNode(event->p, event->already_known_by_peer,
1700 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1702 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1703 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1704 setBlockNotSent(event->p);
1706 else if(event->type == MEET_OTHER)
1708 infostream<<"Server: MEET_OTHER"<<std::endl;
1709 prof.add("MEET_OTHER", 1);
1710 for(core::map<v3s16, bool>::Iterator
1711 i = event->modified_blocks.getIterator();
1712 i.atEnd()==false; i++)
1714 v3s16 p = i.getNode()->getKey();
1720 prof.add("unknown", 1);
1721 infostream<<"WARNING: Server: Unknown MapEditEvent "
1722 <<((u32)event->type)<<std::endl;
1726 Set blocks not sent to far players
1728 if(far_players.size() > 0)
1730 // Convert list format to that wanted by SetBlocksNotSent
1731 core::map<v3s16, MapBlock*> modified_blocks2;
1732 for(core::map<v3s16, bool>::Iterator
1733 i = event->modified_blocks.getIterator();
1734 i.atEnd()==false; i++)
1736 v3s16 p = i.getNode()->getKey();
1737 modified_blocks2.insert(p,
1738 m_env->getMap().getBlockNoCreateNoEx(p));
1740 // Set blocks not sent
1741 for(core::list<u16>::Iterator
1742 i = far_players.begin();
1743 i != far_players.end(); i++)
1746 RemoteClient *client = getClient(peer_id);
1749 client->SetBlocksNotSent(modified_blocks2);
1755 /*// Don't send too many at a time
1757 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1763 infostream<<"Server: MapEditEvents:"<<std::endl;
1764 prof.print(infostream);
1770 Trigger emergethread (it somehow gets to a non-triggered but
1771 bysy state sometimes)
1774 float &counter = m_emergethread_trigger_timer;
1780 m_emergethread.trigger();
1784 // Save map, players and auth stuff
1786 float &counter = m_savemap_timer;
1788 if(counter >= g_settings->getFloat("server_map_save_interval"))
1792 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1795 if(m_authmanager.isModified())
1796 m_authmanager.save();
1799 if(m_banmanager.isModified())
1800 m_banmanager.save();
1803 JMutexAutoLock lock(m_env_mutex);
1805 // Save changed parts of map
1806 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1809 m_env->serializePlayers(m_mapsavedir);
1811 // Save environment metadata
1812 m_env->saveMeta(m_mapsavedir);
1817 void Server::Receive()
1819 DSTACK(__FUNCTION_NAME);
1820 SharedBuffer<u8> data;
1825 JMutexAutoLock conlock(m_con_mutex);
1826 datasize = m_con.Receive(peer_id, data);
1829 // This has to be called so that the client list gets synced
1830 // with the peer list of the connection
1831 handlePeerChanges();
1833 ProcessData(*data, datasize, peer_id);
1835 catch(con::InvalidIncomingDataException &e)
1837 infostream<<"Server::Receive(): "
1838 "InvalidIncomingDataException: what()="
1839 <<e.what()<<std::endl;
1841 catch(con::PeerNotFoundException &e)
1843 //NOTE: This is not needed anymore
1845 // The peer has been disconnected.
1846 // Find the associated player and remove it.
1848 /*JMutexAutoLock envlock(m_env_mutex);
1850 infostream<<"ServerThread: peer_id="<<peer_id
1851 <<" has apparently closed connection. "
1852 <<"Removing player."<<std::endl;
1854 m_env->removePlayer(peer_id);*/
1858 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1860 DSTACK(__FUNCTION_NAME);
1861 // Environment is locked first.
1862 JMutexAutoLock envlock(m_env_mutex);
1863 JMutexAutoLock conlock(m_con_mutex);
1866 Address address = m_con.GetPeerAddress(peer_id);
1868 // drop player if is ip is banned
1869 if(m_banmanager.isIpBanned(address.serializeString())){
1870 SendAccessDenied(m_con, peer_id,
1871 L"Your ip is banned. Banned name was "
1872 +narrow_to_wide(m_banmanager.getBanName(
1873 address.serializeString())));
1874 m_con.DeletePeer(peer_id);
1878 catch(con::PeerNotFoundException &e)
1880 infostream<<"Server::ProcessData(): Cancelling: peer "
1881 <<peer_id<<" not found"<<std::endl;
1885 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1893 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1895 if(command == TOSERVER_INIT)
1897 // [0] u16 TOSERVER_INIT
1898 // [2] u8 SER_FMT_VER_HIGHEST
1899 // [3] u8[20] player_name
1900 // [23] u8[28] password <--- can be sent without this, from old versions
1902 if(datasize < 2+1+PLAYERNAME_SIZE)
1905 infostream<<"Server: Got TOSERVER_INIT from "
1906 <<peer_id<<std::endl;
1908 // First byte after command is maximum supported
1909 // serialization version
1910 u8 client_max = data[2];
1911 u8 our_max = SER_FMT_VER_HIGHEST;
1912 // Use the highest version supported by both
1913 u8 deployed = core::min_(client_max, our_max);
1914 // If it's lower than the lowest supported, give up.
1915 if(deployed < SER_FMT_VER_LOWEST)
1916 deployed = SER_FMT_VER_INVALID;
1918 //peer->serialization_version = deployed;
1919 getClient(peer_id)->pending_serialization_version = deployed;
1921 if(deployed == SER_FMT_VER_INVALID)
1923 infostream<<"Server: Cannot negotiate "
1924 "serialization version with peer "
1925 <<peer_id<<std::endl;
1926 SendAccessDenied(m_con, peer_id, std::wstring(
1927 L"Your client's version is not supported.\n"
1928 L"Server version is ")
1929 + narrow_to_wide(VERSION_STRING) + L"."
1935 Read and check network protocol version
1938 u16 net_proto_version = 0;
1939 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1941 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1944 getClient(peer_id)->net_proto_version = net_proto_version;
1946 if(net_proto_version == 0)
1948 SendAccessDenied(m_con, peer_id, std::wstring(
1949 L"Your client's version is not supported.\n"
1950 L"Server version is ")
1951 + narrow_to_wide(VERSION_STRING) + L"."
1956 if(g_settings->getBool("strict_protocol_version_checking"))
1958 if(net_proto_version != PROTOCOL_VERSION)
1960 SendAccessDenied(m_con, peer_id, std::wstring(
1961 L"Your client's version is not supported.\n"
1962 L"Server version is ")
1963 + narrow_to_wide(VERSION_STRING) + L",\n"
1964 + L"server's PROTOCOL_VERSION is "
1965 + narrow_to_wide(itos(PROTOCOL_VERSION))
1966 + L", client's PROTOCOL_VERSION is "
1967 + narrow_to_wide(itos(net_proto_version))
1978 char playername[PLAYERNAME_SIZE];
1979 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1981 playername[i] = data[3+i];
1983 playername[PLAYERNAME_SIZE-1] = 0;
1985 if(playername[0]=='\0')
1987 infostream<<"Server: Player has empty name"<<std::endl;
1988 SendAccessDenied(m_con, peer_id,
1993 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1995 infostream<<"Server: Player has invalid name"<<std::endl;
1996 SendAccessDenied(m_con, peer_id,
1997 L"Name contains unallowed characters");
2002 char password[PASSWORD_SIZE];
2003 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2005 // old version - assume blank password
2010 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2012 password[i] = data[23+i];
2014 password[PASSWORD_SIZE-1] = 0;
2017 // Add player to auth manager
2018 if(m_authmanager.exists(playername) == false)
2020 std::wstring default_password =
2021 narrow_to_wide(g_settings->get("default_password"));
2022 std::string translated_default_password =
2023 translatePassword(playername, default_password);
2025 // If default_password is empty, allow any initial password
2026 if (default_password.length() == 0)
2027 translated_default_password = password;
2029 infostream<<"Server: adding player "<<playername
2030 <<" to auth manager"<<std::endl;
2031 m_authmanager.add(playername);
2032 m_authmanager.setPassword(playername, translated_default_password);
2033 m_authmanager.setPrivs(playername,
2034 stringToPrivs(g_settings->get("default_privs")));
2035 m_authmanager.save();
2038 std::string checkpwd = m_authmanager.getPassword(playername);
2040 /*infostream<<"Server: Client gave password '"<<password
2041 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2043 if(password != checkpwd)
2045 infostream<<"Server: peer_id="<<peer_id
2046 <<": supplied invalid password for "
2047 <<playername<<std::endl;
2048 SendAccessDenied(m_con, peer_id, L"Invalid password");
2052 // Enforce user limit.
2053 // Don't enforce for users that have some admin right
2054 if(m_clients.size() >= g_settings->getU16("max_users") &&
2055 (m_authmanager.getPrivs(playername)
2056 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2057 playername != g_settings->get("name"))
2059 SendAccessDenied(m_con, peer_id, L"Too many users.");
2064 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2066 // If failed, cancel
2069 infostream<<"Server: peer_id="<<peer_id
2070 <<": failed to emerge player"<<std::endl;
2075 Answer with a TOCLIENT_INIT
2078 SharedBuffer<u8> reply(2+1+6+8);
2079 writeU16(&reply[0], TOCLIENT_INIT);
2080 writeU8(&reply[2], deployed);
2081 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2082 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2085 m_con.Send(peer_id, 0, reply, true);
2089 Send complete position information
2091 SendMovePlayer(player);
2096 if(command == TOSERVER_INIT2)
2098 infostream<<"Server: Got TOSERVER_INIT2 from "
2099 <<peer_id<<std::endl;
2102 getClient(peer_id)->serialization_version
2103 = getClient(peer_id)->pending_serialization_version;
2106 Send some initialization data
2109 // Send item definitions
2110 SendItemDef(m_con, peer_id, m_itemdef);
2112 // Send node definitions
2113 SendNodeDef(m_con, peer_id, m_nodedef);
2115 // Send texture announcement
2116 SendTextureAnnouncement(peer_id);
2118 // Send player info to all players
2119 //SendPlayerInfos();
2121 // Send inventory to player
2122 UpdateCrafting(peer_id);
2123 SendInventory(peer_id);
2125 // Send player items to all players
2128 Player *player = m_env->getPlayer(peer_id);
2131 SendPlayerHP(player);
2135 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2136 m_env->getTimeOfDay());
2137 m_con.Send(peer_id, 0, data, true);
2140 // Send information about server to player in chat
2141 SendChatMessage(peer_id, getStatusString());
2143 // Send information about joining in chat
2145 std::wstring name = L"unknown";
2146 Player *player = m_env->getPlayer(peer_id);
2148 name = narrow_to_wide(player->getName());
2150 std::wstring message;
2153 message += L" joined game";
2154 BroadcastChatMessage(message);
2157 // Warnings about protocol version can be issued here
2158 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2160 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2164 Check HP, respawn if necessary
2166 HandlePlayerHP(player, 0);
2172 std::ostringstream os(std::ios_base::binary);
2173 for(core::map<u16, RemoteClient*>::Iterator
2174 i = m_clients.getIterator();
2175 i.atEnd() == false; i++)
2177 RemoteClient *client = i.getNode()->getValue();
2178 assert(client->peer_id == i.getNode()->getKey());
2179 if(client->serialization_version == SER_FMT_VER_INVALID)
2182 Player *player = m_env->getPlayer(client->peer_id);
2185 // Get name of player
2186 os<<player->getName()<<" ";
2189 actionstream<<player->getName()<<" joins game. List of players: "
2190 <<os.str()<<std::endl;
2196 if(peer_ser_ver == SER_FMT_VER_INVALID)
2198 infostream<<"Server::ProcessData(): Cancelling: Peer"
2199 " serialization format invalid or not initialized."
2200 " Skipping incoming command="<<command<<std::endl;
2204 Player *player = m_env->getPlayer(peer_id);
2205 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2208 infostream<<"Server::ProcessData(): Cancelling: "
2209 "No player for peer_id="<<peer_id
2213 if(command == TOSERVER_PLAYERPOS)
2215 if(datasize < 2+12+12+4+4)
2219 v3s32 ps = readV3S32(&data[start+2]);
2220 v3s32 ss = readV3S32(&data[start+2+12]);
2221 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2222 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2223 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2224 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2225 pitch = wrapDegrees(pitch);
2226 yaw = wrapDegrees(yaw);
2228 player->setPosition(position);
2229 player->setSpeed(speed);
2230 player->setPitch(pitch);
2231 player->setYaw(yaw);
2233 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2234 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2235 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2237 else if(command == TOSERVER_GOTBLOCKS)
2250 u16 count = data[2];
2251 for(u16 i=0; i<count; i++)
2253 if((s16)datasize < 2+1+(i+1)*6)
2254 throw con::InvalidIncomingDataException
2255 ("GOTBLOCKS length is too short");
2256 v3s16 p = readV3S16(&data[2+1+i*6]);
2257 /*infostream<<"Server: GOTBLOCKS ("
2258 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2259 RemoteClient *client = getClient(peer_id);
2260 client->GotBlock(p);
2263 else if(command == TOSERVER_DELETEDBLOCKS)
2276 u16 count = data[2];
2277 for(u16 i=0; i<count; i++)
2279 if((s16)datasize < 2+1+(i+1)*6)
2280 throw con::InvalidIncomingDataException
2281 ("DELETEDBLOCKS length is too short");
2282 v3s16 p = readV3S16(&data[2+1+i*6]);
2283 /*infostream<<"Server: DELETEDBLOCKS ("
2284 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2285 RemoteClient *client = getClient(peer_id);
2286 client->SetBlockNotSent(p);
2289 else if(command == TOSERVER_CLICK_OBJECT)
2291 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2294 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2296 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2299 else if(command == TOSERVER_GROUND_ACTION)
2301 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2305 else if(command == TOSERVER_RELEASE)
2307 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2310 else if(command == TOSERVER_SIGNTEXT)
2312 infostream<<"Server: SIGNTEXT not supported anymore"
2316 else if(command == TOSERVER_SIGNNODETEXT)
2318 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2326 std::string datastring((char*)&data[2], datasize-2);
2327 std::istringstream is(datastring, std::ios_base::binary);
2330 is.read((char*)buf, 6);
2331 v3s16 p = readV3S16(buf);
2332 is.read((char*)buf, 2);
2333 u16 textlen = readU16(buf);
2335 for(u16 i=0; i<textlen; i++)
2337 is.read((char*)buf, 1);
2338 text += (char)buf[0];
2341 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2345 meta->setText(text);
2347 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2348 <<" at "<<PP(p)<<std::endl;
2350 v3s16 blockpos = getNodeBlockPos(p);
2351 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2354 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2358 setBlockNotSent(blockpos);
2360 else if(command == TOSERVER_INVENTORY_ACTION)
2362 // Strip command and create a stream
2363 std::string datastring((char*)&data[2], datasize-2);
2364 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2365 std::istringstream is(datastring, std::ios_base::binary);
2367 InventoryAction *a = InventoryAction::deSerialize(is);
2370 infostream<<"TOSERVER_INVENTORY_ACTION: "
2371 <<"InventoryAction::deSerialize() returned NULL"
2377 Handle restrictions and special cases of the move action
2379 if(a->getType() == IACTION_MOVE)
2381 InventoryList *rlist = player->inventory.getList("craftresult");
2383 InventoryList *clist = player->inventory.getList("craft");
2385 InventoryList *mlist = player->inventory.getList("main");
2388 IMoveAction *ma = (IMoveAction*)a;
2390 ma->from_inv.applyCurrentPlayer(player->getName());
2391 ma->to_inv.applyCurrentPlayer(player->getName());
2393 bool from_inv_is_current_player =
2394 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2395 (ma->from_inv.name == player->getName());
2397 bool to_inv_is_current_player =
2398 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2399 (ma->to_inv.name == player->getName());
2402 Disable moving items into craftresult from elsewhere
2404 if(to_inv_is_current_player
2405 && ma->to_list == "craftresult"
2406 && (!from_inv_is_current_player
2407 || ma->from_list != "craftresult"))
2409 infostream<<"Ignoring IMoveAction from "
2410 <<(ma->from_inv.dump())<<":"<<ma->from_list
2411 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2412 <<" because dst is craftresult"
2413 <<" and src isn't craftresult"<<std::endl;
2419 Handle crafting (source is craftresult, which is preview)
2421 if(from_inv_is_current_player
2422 && ma->from_list == "craftresult"
2423 && player->craftresult_is_preview
2424 && g_settings->getBool("creative_mode") == false)
2426 ItemStack crafting_result;
2427 bool crafting_possible = GetCraftingResult(peer_id,
2428 crafting_result, false);
2431 If the craftresult is placed on itself,
2432 crafting takes place and result is moved
2435 if(crafting_possible
2436 && to_inv_is_current_player
2437 && ma->to_list == "craftresult")
2439 if(mlist->roomForItem(crafting_result))
2441 actionstream<<player->getName()
2443 <<crafting_result.getItemString()
2446 // Decrement crafting materials
2447 GetCraftingResult(peer_id, crafting_result, true);
2448 mlist->addItem(crafting_result);
2449 rlist->clearItems();
2450 player->craftresult_is_preview = true;
2451 srp->m_inventory_not_sent = true;
2456 Otherwise, if the destination is part of
2457 the same player's inventory, crafting
2458 takes place normally.
2460 else if(crafting_possible
2461 && to_inv_is_current_player)
2463 InventoryList *list = player->inventory.getList(ma->to_list);
2464 if(list && list->itemFits(ma->to_i, crafting_result))
2466 actionstream<<player->getName()
2468 <<crafting_result.getItemString()
2471 // Decrement crafting materials
2472 GetCraftingResult(peer_id, crafting_result, true);
2473 list->addItem(ma->to_i, crafting_result);
2474 rlist->clearItems();
2475 player->craftresult_is_preview = true;
2476 srp->m_inventory_not_sent = true;
2480 // Do not apply the action normally.
2489 // Disallow moving items in elsewhere than player's inventory
2490 // if not allowed to interact
2491 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
2492 && (from_inv_is_current_player
2493 || to_inv_is_current_player))
2495 infostream<<"Cannot move outside of player's inventory: "
2496 <<"No interact privilege"<<std::endl;
2501 // If player is not an admin, check for ownership of src and dst
2502 if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
2504 std::string owner_from = getInventoryOwner(ma->from_inv);
2505 if(owner_from != "" && owner_from != player->getName())
2507 infostream<<"WARNING: "<<player->getName()
2508 <<" tried to access an inventory that"
2509 <<" belongs to "<<owner_from<<std::endl;
2514 std::string owner_to = getInventoryOwner(ma->to_inv);
2515 if(owner_to != "" && owner_to != player->getName())
2517 infostream<<"WARNING: "<<player->getName()
2518 <<" tried to access an inventory that"
2519 <<" belongs to "<<owner_to<<std::endl;
2526 Handle restrictions and special cases of the drop action
2528 else if(a->getType() == IACTION_DROP)
2530 IDropAction *da = (IDropAction*)a;
2532 da->from_inv.applyCurrentPlayer(player->getName());
2534 // Disallow dropping items if not allowed to interact
2535 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2540 // If player is not an admin, check for ownership
2541 else if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
2543 std::string owner_from = getInventoryOwner(da->from_inv);
2544 if(owner_from != "" && owner_from != player->getName())
2546 infostream<<"WARNING: "<<player->getName()
2547 <<" tried to access an inventory that"
2548 <<" belongs to "<<owner_from<<std::endl;
2556 a->apply(this, srp);
2560 else if(command == TOSERVER_CHAT_MESSAGE)
2568 std::string datastring((char*)&data[2], datasize-2);
2569 std::istringstream is(datastring, std::ios_base::binary);
2572 is.read((char*)buf, 2);
2573 u16 len = readU16(buf);
2575 std::wstring message;
2576 for(u16 i=0; i<len; i++)
2578 is.read((char*)buf, 2);
2579 message += (wchar_t)readU16(buf);
2582 // Get player name of this client
2583 std::wstring name = narrow_to_wide(player->getName());
2586 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2587 wide_to_narrow(message));
2588 // If script ate the message, don't proceed
2592 // Line to send to players
2594 // Whether to send to the player that sent the line
2595 bool send_to_sender = false;
2596 // Whether to send to other players
2597 bool send_to_others = false;
2599 // Local player gets all privileges regardless of
2600 // what's set on their account.
2601 u64 privs = getPlayerPrivs(player);
2604 if(message[0] == L'/')
2606 size_t strip_size = 1;
2607 if (message[1] == L'#') // support old-style commans
2609 message = message.substr(strip_size);
2611 WStrfnd f1(message);
2612 f1.next(L" "); // Skip over /#whatever
2613 std::wstring paramstring = f1.next(L"");
2615 ServerCommandContext *ctx = new ServerCommandContext(
2616 str_split(message, L' '),
2623 std::wstring reply(processServerCommand(ctx));
2624 send_to_sender = ctx->flags & SEND_TO_SENDER;
2625 send_to_others = ctx->flags & SEND_TO_OTHERS;
2627 if (ctx->flags & SEND_NO_PREFIX)
2630 line += L"Server: " + reply;
2637 if(privs & PRIV_SHOUT)
2643 send_to_others = true;
2647 line += L"Server: You are not allowed to shout";
2648 send_to_sender = true;
2655 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2658 Send the message to clients
2660 for(core::map<u16, RemoteClient*>::Iterator
2661 i = m_clients.getIterator();
2662 i.atEnd() == false; i++)
2664 // Get client and check that it is valid
2665 RemoteClient *client = i.getNode()->getValue();
2666 assert(client->peer_id == i.getNode()->getKey());
2667 if(client->serialization_version == SER_FMT_VER_INVALID)
2671 bool sender_selected = (peer_id == client->peer_id);
2672 if(sender_selected == true && send_to_sender == false)
2674 if(sender_selected == false && send_to_others == false)
2677 SendChatMessage(client->peer_id, line);
2681 else if(command == TOSERVER_DAMAGE)
2683 std::string datastring((char*)&data[2], datasize-2);
2684 std::istringstream is(datastring, std::ios_base::binary);
2685 u8 damage = readU8(is);
2687 if(g_settings->getBool("enable_damage"))
2689 actionstream<<player->getName()<<" damaged by "
2690 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2693 HandlePlayerHP(player, damage);
2697 SendPlayerHP(player);
2700 else if(command == TOSERVER_PASSWORD)
2703 [0] u16 TOSERVER_PASSWORD
2704 [2] u8[28] old password
2705 [30] u8[28] new password
2708 if(datasize != 2+PASSWORD_SIZE*2)
2710 /*char password[PASSWORD_SIZE];
2711 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2712 password[i] = data[2+i];
2713 password[PASSWORD_SIZE-1] = 0;*/
2715 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2723 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2725 char c = data[2+PASSWORD_SIZE+i];
2731 infostream<<"Server: Client requests a password change from "
2732 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2734 std::string playername = player->getName();
2736 if(m_authmanager.exists(playername) == false)
2738 infostream<<"Server: playername not found in authmanager"<<std::endl;
2739 // Wrong old password supplied!!
2740 SendChatMessage(peer_id, L"playername not found in authmanager");
2744 std::string checkpwd = m_authmanager.getPassword(playername);
2746 if(oldpwd != checkpwd)
2748 infostream<<"Server: invalid old password"<<std::endl;
2749 // Wrong old password supplied!!
2750 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2754 actionstream<<player->getName()<<" changes password"<<std::endl;
2756 m_authmanager.setPassword(playername, newpwd);
2758 infostream<<"Server: password change successful for "<<playername
2760 SendChatMessage(peer_id, L"Password change successful");
2762 else if(command == TOSERVER_PLAYERITEM)
2767 u16 item = readU16(&data[2]);
2768 srp->setWieldIndex(item);
2769 SendWieldedItem(srp);
2771 else if(command == TOSERVER_RESPAWN)
2776 srp->m_respawn_active = false;
2778 RespawnPlayer(player);
2780 actionstream<<player->getName()<<" respawns at "
2781 <<PP(player->getPosition()/BS)<<std::endl;
2783 // ActiveObject is added to environment in AsyncRunStep after
2784 // the previous addition has been succesfully removed
2786 else if(command == TOSERVER_REQUEST_TEXTURES) {
2787 std::string datastring((char*)&data[2], datasize-2);
2788 std::istringstream is(datastring, std::ios_base::binary);
2790 infostream<<"TOSERVER_REQUEST_TEXTURES: "<<std::endl;
2792 core::list<TextureRequest> tosend;
2794 u16 numtextures = readU16(is);
2796 for(int i = 0; i < numtextures; i++) {
2798 std::string name = deSerializeString(is);
2800 tosend.push_back(TextureRequest(name));
2801 infostream<<"TOSERVER_REQUEST_TEXTURES: requested texture " << name <<std::endl;
2804 SendTexturesRequested(peer_id, tosend);
2806 // Now the client should know about everything
2807 // (definitions and textures)
2808 getClient(peer_id)->definitions_sent = true;
2810 else if(command == TOSERVER_INTERACT)
2812 std::string datastring((char*)&data[2], datasize-2);
2813 std::istringstream is(datastring, std::ios_base::binary);
2819 [5] u32 length of the next item
2820 [9] serialized PointedThing
2822 0: start digging (from undersurface) or use
2823 1: stop digging (all parameters ignored)
2824 2: digging completed
2825 3: place block or item (to abovesurface)
2828 u8 action = readU8(is);
2829 u16 item_i = readU16(is);
2830 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2831 PointedThing pointed;
2832 pointed.deSerialize(tmp_is);
2834 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2836 v3f player_pos = srp->m_last_good_position;
2838 // Update wielded item
2839 if(srp->getWieldIndex() != item_i)
2841 srp->setWieldIndex(item_i);
2842 SendWieldedItem(srp);
2845 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2846 v3s16 p_under = pointed.node_undersurface;
2847 v3s16 p_above = pointed.node_abovesurface;
2849 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2850 ServerActiveObject *pointed_object = NULL;
2851 if(pointed.type == POINTEDTHING_OBJECT)
2853 pointed_object = m_env->getActiveObject(pointed.object_id);
2854 if(pointed_object == NULL)
2856 infostream<<"TOSERVER_INTERACT: "
2857 "pointed object is NULL"<<std::endl;
2863 v3f pointed_pos_under = player_pos;
2864 v3f pointed_pos_above = player_pos;
2865 if(pointed.type == POINTEDTHING_NODE)
2867 pointed_pos_under = intToFloat(p_under, BS);
2868 pointed_pos_above = intToFloat(p_above, BS);
2870 else if(pointed.type == POINTEDTHING_OBJECT)
2872 pointed_pos_under = pointed_object->getBasePosition();
2873 pointed_pos_above = pointed_pos_under;
2877 Check that target is reasonably close
2878 (only when digging or placing things)
2880 if(action == 0 || action == 2 || action == 3)
2882 float d = player_pos.getDistanceFrom(pointed_pos_under);
2883 float max_d = BS * 10; // Just some large enough value
2885 actionstream<<"Player "<<player->getName()
2886 <<" tried to access "<<pointed.dump()
2888 <<"d="<<d<<", max_d="<<max_d
2889 <<". ignoring."<<std::endl;
2890 // Re-send block to revert change on client-side
2891 RemoteClient *client = getClient(peer_id);
2892 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2893 client->SetBlockNotSent(blockpos);
2900 Make sure the player is allowed to do it
2902 bool interact_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
2905 infostream<<"Ignoring interaction from player "<<player->getName()
2906 <<" because privileges are "<<getPlayerPrivs(player)
2908 // NOTE: no return; here, fall through
2912 0: start digging or punch object
2916 if(pointed.type == POINTEDTHING_NODE)
2919 NOTE: This can be used in the future to check if
2920 somebody is cheating, by checking the timing.
2922 bool cannot_punch_node = !interact_priv;
2924 MapNode n(CONTENT_IGNORE);
2928 n = m_env->getMap().getNode(p_under);
2930 catch(InvalidPositionException &e)
2932 infostream<<"Server: Not punching: Node not found."
2933 <<" Adding block to emerge queue."
2935 m_emerge_queue.addBlock(peer_id,
2936 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2937 cannot_punch_node = true;
2940 if(cannot_punch_node)
2946 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2948 else if(pointed.type == POINTEDTHING_OBJECT)
2953 // Skip if object has been removed
2954 if(pointed_object->m_removed)
2957 actionstream<<player->getName()<<" punches object "
2958 <<pointed.object_id<<std::endl;
2961 pointed_object->punch(srp, srp->m_time_from_last_punch);
2962 srp->m_time_from_last_punch = 0;
2970 else if(action == 1)
2975 2: Digging completed
2977 else if(action == 2)
2979 // Only complete digging of nodes
2980 if(pointed.type != POINTEDTHING_NODE)
2983 // Mandatory parameter; actually used for nothing
2984 core::map<v3s16, MapBlock*> modified_blocks;
2986 content_t material = CONTENT_IGNORE;
2987 u8 mineral = MINERAL_NONE;
2989 bool cannot_remove_node = !interact_priv;
2991 MapNode n(CONTENT_IGNORE);
2994 n = m_env->getMap().getNode(p_under);
2996 mineral = n.getMineral(m_nodedef);
2997 // Get material at position
2998 material = n.getContent();
2999 // If not yet cancelled
3000 if(cannot_remove_node == false)
3002 // If it's not diggable, do nothing
3003 if(m_nodedef->get(material).diggable == false)
3005 infostream<<"Server: Not finishing digging: "
3006 <<"Node not diggable"
3008 cannot_remove_node = true;
3011 // If not yet cancelled
3012 if(cannot_remove_node == false)
3014 // Get node metadata
3015 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3016 if(meta && meta->nodeRemovalDisabled() == true)
3018 infostream<<"Server: Not finishing digging: "
3019 <<"Node metadata disables removal"
3021 cannot_remove_node = true;
3025 catch(InvalidPositionException &e)
3027 infostream<<"Server: Not finishing digging: Node not found."
3028 <<" Adding block to emerge queue."
3030 m_emerge_queue.addBlock(peer_id,
3031 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3032 cannot_remove_node = true;
3036 If node can't be removed, set block to be re-sent to
3039 if(cannot_remove_node)
3041 infostream<<"Server: Not finishing digging."<<std::endl;
3043 // Client probably has wrong data.
3044 // Set block not sent, so that client will get
3046 infostream<<"Client "<<peer_id<<" tried to dig "
3047 <<"node; but node cannot be removed."
3048 <<" setting MapBlock not sent."<<std::endl;
3049 RemoteClient *client = getClient(peer_id);
3050 v3s16 blockpos = getNodeBlockPos(p_under);
3051 client->SetBlockNotSent(blockpos);
3056 actionstream<<player->getName()<<" digs "<<PP(p_under)
3057 <<", gets material "<<(int)material<<", mineral "
3058 <<(int)mineral<<std::endl;
3061 Send the removal to all close-by players.
3062 - If other player is close, send REMOVENODE
3063 - Otherwise set blocks not sent
3065 core::list<u16> far_players;
3066 sendRemoveNode(p_under, peer_id, &far_players, 30);
3069 Update and send inventory
3072 if(g_settings->getBool("creative_mode") == false)
3077 InventoryList *mlist = player->inventory.getList("main");
3080 ItemStack &item = mlist->getItem(item_i);
3082 // Get digging properties for material and tool
3083 ToolDiggingProperties tp =
3084 item.getToolDiggingProperties(m_itemdef);
3085 DiggingProperties prop =
3086 getDiggingProperties(material, &tp, m_nodedef);
3087 item.addWear(prop.wear, m_itemdef);
3088 srp->m_inventory_not_sent = true;
3092 Add dug item to inventory
3097 if(mineral != MINERAL_NONE)
3098 item = getDiggedMineralItem(mineral, this);
3103 const std::string &dug_s = m_nodedef->get(material).dug_item;
3106 item.deSerialize(dug_s, m_itemdef);
3112 // Add a item to inventory
3113 player->inventory.addItem("main", item);
3114 srp->m_inventory_not_sent = true;
3120 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3121 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3122 if(extra_dug_s != "" && extra_rarity != 0
3123 && myrand() % extra_rarity == 0)
3125 item.deSerialize(extra_dug_s, m_itemdef);
3131 // Add a item to inventory
3132 player->inventory.addItem("main", item);
3133 srp->m_inventory_not_sent = true;
3139 (this takes some time so it is done after the quick stuff)
3142 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3144 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3147 Set blocks not sent to far players
3149 for(core::list<u16>::Iterator
3150 i = far_players.begin();
3151 i != far_players.end(); i++)
3154 RemoteClient *client = getClient(peer_id);
3157 client->SetBlocksNotSent(modified_blocks);
3163 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3167 3: place block or right-click object
3169 else if(action == 3)
3173 infostream<<"Not allowing player "
3174 <<player->getName()<<" to place item: "
3175 <<"no interact privileges"<<std::endl;
3179 ItemStack item = srp->getWieldedItem();
3181 if(pointed.type == POINTEDTHING_OBJECT)
3183 // Right click object
3185 // Skip if object has been removed
3186 if(pointed_object->m_removed)
3189 actionstream<<player->getName()<<" right-clicks object "
3190 <<pointed.object_id<<std::endl;
3193 pointed_object->rightClick(srp);
3195 else if(scriptapi_item_on_place(m_lua,
3196 item, srp, pointed))
3198 // Placement was handled in lua
3200 // Apply returned ItemStack
3201 if(g_settings->getBool("creative_mode") == false)
3202 srp->setWieldedItem(item);
3204 else if(pointed.type == POINTEDTHING_NODE &&
3205 item.getDefinition(m_itemdef).type == ITEM_NODE)
3207 bool cannot_place_node = !interact_priv;
3210 // Don't add a node if this is not a free space
3211 MapNode n2 = m_env->getMap().getNode(p_above);
3212 if(m_nodedef->get(n2).buildable_to == false)
3214 infostream<<"Client "<<peer_id<<" tried to place"
3215 <<" node in invalid position."<<std::endl;
3216 cannot_place_node = true;
3219 catch(InvalidPositionException &e)
3221 infostream<<"Server: Ignoring ADDNODE: Node not found"
3222 <<" Adding block to emerge queue."
3224 m_emerge_queue.addBlock(peer_id,
3225 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3226 cannot_place_node = true;
3229 if(cannot_place_node)
3231 // Client probably has wrong data.
3232 // Set block not sent, so that client will get
3234 RemoteClient *client = getClient(peer_id);
3235 v3s16 blockpos = getNodeBlockPos(p_above);
3236 client->SetBlockNotSent(blockpos);
3240 // Reset build time counter
3241 getClient(peer_id)->m_time_from_building = 0.0;
3244 MapNode n(m_nodedef, item.name, 0, 0);
3246 actionstream<<player->getName()<<" places material "
3248 <<" at "<<PP(p_under)<<std::endl;
3250 // Calculate direction for wall mounted stuff
3251 if(m_nodedef->get(n).wall_mounted)
3252 n.param2 = packDir(p_under - p_above);
3254 // Calculate the direction for furnaces and chests and stuff
3255 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3257 v3f playerpos = player->getPosition();
3258 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3259 blockpos = blockpos.normalize();
3261 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3275 Send to all close-by players
3277 core::list<u16> far_players;
3278 sendAddNode(p_above, n, 0, &far_players, 30);
3283 if(g_settings->getBool("creative_mode") == false)
3285 // Remove from inventory and send inventory
3287 srp->setWieldedItem(item);
3293 This takes some time so it is done after the quick stuff
3295 core::map<v3s16, MapBlock*> modified_blocks;
3297 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3299 std::string p_name = std::string(player->getName());
3300 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3303 Set blocks not sent to far players
3305 for(core::list<u16>::Iterator
3306 i = far_players.begin();
3307 i != far_players.end(); i++)
3310 RemoteClient *client = getClient(peer_id);
3313 client->SetBlocksNotSent(modified_blocks);
3319 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3327 else if(action == 4)
3329 // Requires interact privs
3332 infostream<<"Not allowing player to use item: "
3333 "no interact privileges"<<std::endl;
3337 ItemStack item = srp->getWieldedItem();
3339 actionstream<<player->getName()<<" uses "<<item.name
3340 <<", pointing at "<<pointed.dump()<<std::endl;
3342 if(scriptapi_item_on_use(m_lua,
3343 item, srp, pointed))
3345 // Apply returned ItemStack
3346 if(g_settings->getBool("creative_mode") == false)
3347 srp->setWieldedItem(item);
3353 Catch invalid actions
3357 infostream<<"WARNING: Server: Invalid action "
3358 <<action<<std::endl;
3363 infostream<<"Server::ProcessData(): Ignoring "
3364 "unknown command "<<command<<std::endl;
3368 catch(SendFailedException &e)
3370 errorstream<<"Server::ProcessData(): SendFailedException: "
3376 void Server::onMapEditEvent(MapEditEvent *event)
3378 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3379 if(m_ignore_map_edit_events)
3381 MapEditEvent *e = event->clone();
3382 m_unsent_map_edit_queue.push_back(e);
3385 Inventory* Server::getInventory(const InventoryLocation &loc)
3388 case InventoryLocation::UNDEFINED:
3391 case InventoryLocation::CURRENT_PLAYER:
3394 case InventoryLocation::PLAYER:
3396 Player *player = m_env->getPlayer(loc.name.c_str());
3399 return &player->inventory;
3402 case InventoryLocation::NODEMETA:
3404 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3407 return meta->getInventory();
3415 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3418 case InventoryLocation::UNDEFINED:
3421 case InventoryLocation::CURRENT_PLAYER:
3424 case InventoryLocation::PLAYER:
3429 case InventoryLocation::NODEMETA:
3431 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3434 return meta->getOwner();
3442 void Server::setInventoryModified(const InventoryLocation &loc)
3445 case InventoryLocation::UNDEFINED:
3448 case InventoryLocation::PLAYER:
3450 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>
3451 (m_env->getPlayer(loc.name.c_str()));
3454 srp->m_inventory_not_sent = true;
3457 case InventoryLocation::NODEMETA:
3459 v3s16 blockpos = getNodeBlockPos(loc.p);
3461 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3463 meta->inventoryModified();
3465 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3467 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3469 setBlockNotSent(blockpos);
3477 core::list<PlayerInfo> Server::getPlayerInfo()
3479 DSTACK(__FUNCTION_NAME);
3480 JMutexAutoLock envlock(m_env_mutex);
3481 JMutexAutoLock conlock(m_con_mutex);
3483 core::list<PlayerInfo> list;
3485 core::list<Player*> players = m_env->getPlayers();
3487 core::list<Player*>::Iterator i;
3488 for(i = players.begin();
3489 i != players.end(); i++)
3493 Player *player = *i;
3496 // Copy info from connection to info struct
3497 info.id = player->peer_id;
3498 info.address = m_con.GetPeerAddress(player->peer_id);
3499 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3501 catch(con::PeerNotFoundException &e)
3503 // Set dummy peer info
3505 info.address = Address(0,0,0,0,0);
3509 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3510 info.position = player->getPosition();
3512 list.push_back(info);
3519 void Server::peerAdded(con::Peer *peer)
3521 DSTACK(__FUNCTION_NAME);
3522 infostream<<"Server::peerAdded(): peer->id="
3523 <<peer->id<<std::endl;
3526 c.type = PEER_ADDED;
3527 c.peer_id = peer->id;
3529 m_peer_change_queue.push_back(c);
3532 void Server::deletingPeer(con::Peer *peer, bool timeout)
3534 DSTACK(__FUNCTION_NAME);
3535 infostream<<"Server::deletingPeer(): peer->id="
3536 <<peer->id<<", timeout="<<timeout<<std::endl;
3539 c.type = PEER_REMOVED;
3540 c.peer_id = peer->id;
3541 c.timeout = timeout;
3542 m_peer_change_queue.push_back(c);
3549 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3551 DSTACK(__FUNCTION_NAME);
3552 std::ostringstream os(std::ios_base::binary);
3554 writeU16(os, TOCLIENT_HP);
3558 std::string s = os.str();
3559 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3561 con.Send(peer_id, 0, data, true);
3564 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3565 const std::wstring &reason)
3567 DSTACK(__FUNCTION_NAME);
3568 std::ostringstream os(std::ios_base::binary);
3570 writeU16(os, TOCLIENT_ACCESS_DENIED);
3571 os<<serializeWideString(reason);
3574 std::string s = os.str();
3575 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3577 con.Send(peer_id, 0, data, true);
3580 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3581 bool set_camera_point_target, v3f camera_point_target)
3583 DSTACK(__FUNCTION_NAME);
3584 std::ostringstream os(std::ios_base::binary);
3586 writeU16(os, TOCLIENT_DEATHSCREEN);
3587 writeU8(os, set_camera_point_target);
3588 writeV3F1000(os, camera_point_target);
3591 std::string s = os.str();
3592 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3594 con.Send(peer_id, 0, data, true);
3597 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3598 IItemDefManager *itemdef)
3600 DSTACK(__FUNCTION_NAME);
3601 std::ostringstream os(std::ios_base::binary);
3605 u32 length of the next item
3606 zlib-compressed serialized ItemDefManager
3608 writeU16(os, TOCLIENT_ITEMDEF);
3609 std::ostringstream tmp_os(std::ios::binary);
3610 itemdef->serialize(tmp_os);
3611 std::ostringstream tmp_os2(std::ios::binary);
3612 compressZlib(tmp_os.str(), tmp_os2);
3613 os<<serializeLongString(tmp_os2.str());
3616 std::string s = os.str();
3617 infostream<<"Server::SendItemDef(): Sending item definitions: size="
3618 <<s.size()<<std::endl;
3619 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3621 con.Send(peer_id, 0, data, true);
3624 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3625 INodeDefManager *nodedef)
3627 DSTACK(__FUNCTION_NAME);
3628 std::ostringstream os(std::ios_base::binary);
3632 u32 length of the next item
3633 zlib-compressed serialized NodeDefManager
3635 writeU16(os, TOCLIENT_NODEDEF);
3636 std::ostringstream tmp_os(std::ios::binary);
3637 nodedef->serialize(tmp_os);
3638 std::ostringstream tmp_os2(std::ios::binary);
3639 compressZlib(tmp_os.str(), tmp_os2);
3640 os<<serializeLongString(tmp_os2.str());
3643 std::string s = os.str();
3644 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3645 <<s.size()<<std::endl;
3646 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3648 con.Send(peer_id, 0, data, true);
3652 Non-static send methods
3655 void Server::SendInventory(u16 peer_id)
3657 DSTACK(__FUNCTION_NAME);
3659 ServerRemotePlayer* player =
3660 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3663 player->m_inventory_not_sent = false;
3669 std::ostringstream os;
3670 //os.imbue(std::locale("C"));
3672 player->inventory.serialize(os);
3674 std::string s = os.str();
3676 SharedBuffer<u8> data(s.size()+2);
3677 writeU16(&data[0], TOCLIENT_INVENTORY);
3678 memcpy(&data[2], s.c_str(), s.size());
3681 m_con.Send(peer_id, 0, data, true);
3684 void Server::SendWieldedItem(const ServerRemotePlayer* srp)
3686 DSTACK(__FUNCTION_NAME);
3690 std::ostringstream os(std::ios_base::binary);
3692 writeU16(os, TOCLIENT_PLAYERITEM);
3694 writeU16(os, srp->peer_id);
3695 os<<serializeString(srp->getWieldedItem().getItemString());
3698 std::string s = os.str();
3699 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3701 m_con.SendToAll(0, data, true);
3704 void Server::SendPlayerItems()
3706 DSTACK(__FUNCTION_NAME);
3708 std::ostringstream os(std::ios_base::binary);
3709 core::list<Player *> players = m_env->getPlayers(true);
3711 writeU16(os, TOCLIENT_PLAYERITEM);
3712 writeU16(os, players.size());
3713 core::list<Player *>::Iterator i;
3714 for(i = players.begin(); i != players.end(); ++i)
3717 ServerRemotePlayer *srp =
3718 static_cast<ServerRemotePlayer*>(p);
3719 writeU16(os, p->peer_id);
3720 os<<serializeString(srp->getWieldedItem().getItemString());
3724 std::string s = os.str();
3725 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3727 m_con.SendToAll(0, data, true);
3730 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3732 DSTACK(__FUNCTION_NAME);
3734 std::ostringstream os(std::ios_base::binary);
3738 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3739 os.write((char*)buf, 2);
3742 writeU16(buf, message.size());
3743 os.write((char*)buf, 2);
3746 for(u32 i=0; i<message.size(); i++)
3750 os.write((char*)buf, 2);
3754 std::string s = os.str();
3755 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3757 m_con.Send(peer_id, 0, data, true);
3760 void Server::BroadcastChatMessage(const std::wstring &message)
3762 for(core::map<u16, RemoteClient*>::Iterator
3763 i = m_clients.getIterator();
3764 i.atEnd() == false; i++)
3766 // Get client and check that it is valid
3767 RemoteClient *client = i.getNode()->getValue();
3768 assert(client->peer_id == i.getNode()->getKey());
3769 if(client->serialization_version == SER_FMT_VER_INVALID)
3772 SendChatMessage(client->peer_id, message);
3776 void Server::SendPlayerHP(Player *player)
3778 SendHP(m_con, player->peer_id, player->hp);
3779 static_cast<ServerRemotePlayer*>(player)->m_hp_not_sent = false;
3782 void Server::SendMovePlayer(Player *player)
3784 DSTACK(__FUNCTION_NAME);
3785 std::ostringstream os(std::ios_base::binary);
3787 writeU16(os, TOCLIENT_MOVE_PLAYER);
3788 writeV3F1000(os, player->getPosition());
3789 writeF1000(os, player->getPitch());
3790 writeF1000(os, player->getYaw());
3793 v3f pos = player->getPosition();
3794 f32 pitch = player->getPitch();
3795 f32 yaw = player->getYaw();
3796 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3797 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3804 std::string s = os.str();
3805 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3807 m_con.Send(player->peer_id, 0, data, true);
3810 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3811 core::list<u16> *far_players, float far_d_nodes)
3813 float maxd = far_d_nodes*BS;
3814 v3f p_f = intToFloat(p, BS);
3818 SharedBuffer<u8> reply(replysize);
3819 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3820 writeS16(&reply[2], p.X);
3821 writeS16(&reply[4], p.Y);
3822 writeS16(&reply[6], p.Z);
3824 for(core::map<u16, RemoteClient*>::Iterator
3825 i = m_clients.getIterator();
3826 i.atEnd() == false; i++)
3828 // Get client and check that it is valid
3829 RemoteClient *client = i.getNode()->getValue();
3830 assert(client->peer_id == i.getNode()->getKey());
3831 if(client->serialization_version == SER_FMT_VER_INVALID)
3834 // Don't send if it's the same one
3835 if(client->peer_id == ignore_id)
3841 Player *player = m_env->getPlayer(client->peer_id);
3844 // If player is far away, only set modified blocks not sent
3845 v3f player_pos = player->getPosition();
3846 if(player_pos.getDistanceFrom(p_f) > maxd)
3848 far_players->push_back(client->peer_id);
3855 m_con.Send(client->peer_id, 0, reply, true);
3859 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3860 core::list<u16> *far_players, float far_d_nodes)
3862 float maxd = far_d_nodes*BS;
3863 v3f p_f = intToFloat(p, BS);
3865 for(core::map<u16, RemoteClient*>::Iterator
3866 i = m_clients.getIterator();
3867 i.atEnd() == false; i++)
3869 // Get client and check that it is valid
3870 RemoteClient *client = i.getNode()->getValue();
3871 assert(client->peer_id == i.getNode()->getKey());
3872 if(client->serialization_version == SER_FMT_VER_INVALID)
3875 // Don't send if it's the same one
3876 if(client->peer_id == ignore_id)
3882 Player *player = m_env->getPlayer(client->peer_id);
3885 // If player is far away, only set modified blocks not sent
3886 v3f player_pos = player->getPosition();
3887 if(player_pos.getDistanceFrom(p_f) > maxd)
3889 far_players->push_back(client->peer_id);
3896 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3897 SharedBuffer<u8> reply(replysize);
3898 writeU16(&reply[0], TOCLIENT_ADDNODE);
3899 writeS16(&reply[2], p.X);
3900 writeS16(&reply[4], p.Y);
3901 writeS16(&reply[6], p.Z);
3902 n.serialize(&reply[8], client->serialization_version);
3905 m_con.Send(client->peer_id, 0, reply, true);
3909 void Server::setBlockNotSent(v3s16 p)
3911 for(core::map<u16, RemoteClient*>::Iterator
3912 i = m_clients.getIterator();
3913 i.atEnd()==false; i++)
3915 RemoteClient *client = i.getNode()->getValue();
3916 client->SetBlockNotSent(p);
3920 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3922 DSTACK(__FUNCTION_NAME);
3924 v3s16 p = block->getPos();
3928 bool completely_air = true;
3929 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3930 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3931 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3933 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3935 completely_air = false;
3936 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3941 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3943 infostream<<"[completely air] ";
3944 infostream<<std::endl;
3948 Create a packet with the block in the right format
3951 std::ostringstream os(std::ios_base::binary);
3952 block->serialize(os, ver);
3953 std::string s = os.str();
3954 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3956 u32 replysize = 8 + blockdata.getSize();
3957 SharedBuffer<u8> reply(replysize);
3958 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3959 writeS16(&reply[2], p.X);
3960 writeS16(&reply[4], p.Y);
3961 writeS16(&reply[6], p.Z);
3962 memcpy(&reply[8], *blockdata, blockdata.getSize());
3964 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3965 <<": \tpacket size: "<<replysize<<std::endl;*/
3970 m_con.Send(peer_id, 1, reply, true);
3973 void Server::SendBlocks(float dtime)
3975 DSTACK(__FUNCTION_NAME);
3977 JMutexAutoLock envlock(m_env_mutex);
3978 JMutexAutoLock conlock(m_con_mutex);
3980 //TimeTaker timer("Server::SendBlocks");
3982 core::array<PrioritySortedBlockTransfer> queue;
3984 s32 total_sending = 0;
3987 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3989 for(core::map<u16, RemoteClient*>::Iterator
3990 i = m_clients.getIterator();
3991 i.atEnd() == false; i++)
3993 RemoteClient *client = i.getNode()->getValue();
3994 assert(client->peer_id == i.getNode()->getKey());
3996 // If definitions and textures have not been sent, don't
3997 // send MapBlocks either
3998 if(!client->definitions_sent)
4001 total_sending += client->SendingCount();
4003 if(client->serialization_version == SER_FMT_VER_INVALID)
4006 client->GetNextBlocks(this, dtime, queue);
4011 // Lowest priority number comes first.
4012 // Lowest is most important.
4015 for(u32 i=0; i<queue.size(); i++)
4017 //TODO: Calculate limit dynamically
4018 if(total_sending >= g_settings->getS32
4019 ("max_simultaneous_block_sends_server_total"))
4022 PrioritySortedBlockTransfer q = queue[i];
4024 MapBlock *block = NULL;
4027 block = m_env->getMap().getBlockNoCreate(q.pos);
4029 catch(InvalidPositionException &e)
4034 RemoteClient *client = getClient(q.peer_id);
4036 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4038 client->SentBlock(q.pos);
4044 void Server::PrepareTextures() {
4045 DSTACK(__FUNCTION_NAME);
4047 infostream<<"Server::PrepareTextures(): Calculate sha1 sums of textures"<<std::endl;
4049 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4050 i != m_mods.end(); i++){
4051 const ModSpec &mod = *i;
4052 std::string texturepath = mod.path + DIR_DELIM + "textures";
4053 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4054 for(u32 j=0; j<dirlist.size(); j++){
4055 if(dirlist[j].dir) // Ignode dirs
4057 std::string tname = dirlist[j].name;
4058 // if name contains illegal characters, ignore the texture
4059 if(!string_allowed(tname, TEXTURENAME_ALLOWED_CHARS)){
4060 errorstream<<"Server: ignoring illegal texture name: \""
4061 <<tname<<"\""<<std::endl;
4064 std::string tpath = texturepath + DIR_DELIM + tname;
4066 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4067 if(fis.good() == false){
4068 errorstream<<"Server::PrepareTextures(): Could not open \""
4069 <<tname<<"\" for reading"<<std::endl;
4072 std::ostringstream tmp_os(std::ios_base::binary);
4076 fis.read(buf, 1024);
4077 std::streamsize len = fis.gcount();
4078 tmp_os.write(buf, len);
4087 errorstream<<"Server::PrepareTextures(): Failed to read \""
4088 <<tname<<"\""<<std::endl;
4091 if(tmp_os.str().length() == 0){
4092 errorstream<<"Server::PrepareTextures(): Empty file \""
4093 <<tpath<<"\""<<std::endl;
4098 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4100 unsigned char *digest = sha1.getDigest();
4101 std::string digest_string = base64_encode(digest, 20);
4106 this->m_Textures[tname] = TextureInformation(tpath,digest_string);
4107 infostream<<"Server::PrepareTextures(): added sha1 for "<< tname <<std::endl;
4112 struct SendableTextureAnnouncement
4115 std::string sha1_digest;
4117 SendableTextureAnnouncement(const std::string name_="",
4118 const std::string sha1_digest_=""):
4120 sha1_digest(sha1_digest_)
4125 void Server::SendTextureAnnouncement(u16 peer_id){
4126 DSTACK(__FUNCTION_NAME);
4128 infostream<<"Server::SendTextureAnnouncement()"<<std::endl;
4130 core::list<SendableTextureAnnouncement> texture_announcements;
4132 for (std::map<std::string,TextureInformation>::iterator i = m_Textures.begin();i != m_Textures.end(); i++ ) {
4135 texture_announcements.push_back(
4136 SendableTextureAnnouncement(i->first, i->second.sha1_digest));
4139 //send announcements
4143 u32 number of textures
4147 u16 length of digest string
4151 std::ostringstream os(std::ios_base::binary);
4153 writeU16(os, TOCLIENT_ANNOUNCE_TEXTURES);
4154 writeU16(os, texture_announcements.size());
4156 for(core::list<SendableTextureAnnouncement>::Iterator
4157 j = texture_announcements.begin();
4158 j != texture_announcements.end(); j++){
4159 os<<serializeString(j->name);
4160 os<<serializeString(j->sha1_digest);
4164 std::string s = os.str();
4165 infostream<<"Server::SendTextureAnnouncement(): Send to client"<<std::endl;
4166 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4169 m_con.Send(peer_id, 0, data, true);
4173 struct SendableTexture
4179 SendableTexture(const std::string &name_="", const std::string path_="",
4180 const std::string &data_=""):
4187 void Server::SendTexturesRequested(u16 peer_id,core::list<TextureRequest> tosend) {
4188 DSTACK(__FUNCTION_NAME);
4190 infostream<<"Server::SendTexturesRequested(): Sending textures to client"<<std::endl;
4194 // Put 5kB in one bunch (this is not accurate)
4195 u32 bytes_per_bunch = 5000;
4197 core::array< core::list<SendableTexture> > texture_bunches;
4198 texture_bunches.push_back(core::list<SendableTexture>());
4200 u32 texture_size_bunch_total = 0;
4202 for(core::list<TextureRequest>::Iterator i = tosend.begin(); i != tosend.end(); i++) {
4203 if(m_Textures.find(i->name) == m_Textures.end()){
4204 errorstream<<"Server::SendTexturesRequested(): Client asked for "
4205 <<"unknown texture \""<<(i->name)<<"\""<<std::endl;
4209 //TODO get path + name
4210 std::string tpath = m_Textures[(*i).name].path;
4213 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4214 if(fis.good() == false){
4215 errorstream<<"Server::SendTexturesRequested(): Could not open \""
4216 <<tpath<<"\" for reading"<<std::endl;
4219 std::ostringstream tmp_os(std::ios_base::binary);
4223 fis.read(buf, 1024);
4224 std::streamsize len = fis.gcount();
4225 tmp_os.write(buf, len);
4226 texture_size_bunch_total += len;
4235 errorstream<<"Server::SendTexturesRequested(): Failed to read \""
4236 <<(*i).name<<"\""<<std::endl;
4239 /*infostream<<"Server::SendTexturesRequested(): Loaded \""
4240 <<tname<<"\""<<std::endl;*/
4242 texture_bunches[texture_bunches.size()-1].push_back(
4243 SendableTexture((*i).name, tpath, tmp_os.str()));
4245 // Start next bunch if got enough data
4246 if(texture_size_bunch_total >= bytes_per_bunch){
4247 texture_bunches.push_back(core::list<SendableTexture>());
4248 texture_size_bunch_total = 0;
4253 /* Create and send packets */
4255 u32 num_bunches = texture_bunches.size();
4256 for(u32 i=0; i<num_bunches; i++)
4260 u16 total number of texture bunches
4261 u16 index of this bunch
4262 u32 number of textures in this bunch
4270 std::ostringstream os(std::ios_base::binary);
4272 writeU16(os, TOCLIENT_TEXTURES);
4273 writeU16(os, num_bunches);
4275 writeU32(os, texture_bunches[i].size());
4277 for(core::list<SendableTexture>::Iterator
4278 j = texture_bunches[i].begin();
4279 j != texture_bunches[i].end(); j++){
4280 os<<serializeString(j->name);
4281 os<<serializeLongString(j->data);
4285 std::string s = os.str();
4286 infostream<<"Server::SendTexturesRequested(): bunch "<<i<<"/"<<num_bunches
4287 <<" textures="<<texture_bunches[i].size()
4288 <<" size=" <<s.size()<<std::endl;
4289 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4291 m_con.Send(peer_id, 0, data, true);
4301 void Server::HandlePlayerHP(Player *player, s16 damage)
4303 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4305 if(srp->m_respawn_active)
4308 if(player->hp > damage)
4311 player->hp -= damage;
4312 SendPlayerHP(player);
4317 infostream<<"Server::HandlePlayerHP(): Player "
4318 <<player->getName()<<" dies"<<std::endl;
4322 // Trigger scripted stuff
4323 scriptapi_on_dieplayer(m_lua, srp);
4325 // Handle players that are not connected
4326 if(player->peer_id == PEER_ID_INEXISTENT){
4327 RespawnPlayer(player);
4331 SendPlayerHP(player);
4333 RemoteClient *client = getClient(player->peer_id);
4334 if(client->net_proto_version >= 3)
4336 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4337 srp->m_removed = true;
4338 srp->m_respawn_active = true;
4342 RespawnPlayer(player);
4346 void Server::RespawnPlayer(Player *player)
4349 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4350 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4352 v3f pos = findSpawnPos(m_env->getServerMap());
4353 player->setPosition(pos);
4354 srp->m_last_good_position = pos;
4355 srp->m_last_good_position_age = 0;
4357 SendMovePlayer(player);
4358 SendPlayerHP(player);
4361 bool Server::GetCraftingResult(u16 peer_id, ItemStack &result, bool decrementInput)
4363 DSTACK(__FUNCTION_NAME);
4365 Player* player = m_env->getPlayer(peer_id);
4368 // Get the crafting InventoryList of the player in which we will operate
4369 InventoryList *clist = player->inventory.getList("craft");
4372 // Mangle crafting grid to an another format
4374 ci.method = CRAFT_METHOD_NORMAL;
4376 for(u16 i=0; i<9; i++)
4378 ci.items.push_back(clist->getItem(i));
4381 // Find out what is crafted and add it to result item slot
4383 bool found = m_craftdef->getCraftResult(ci, co, decrementInput, this);
4385 result.deSerialize(co.item, m_itemdef);
4389 // CraftInput has been changed, apply changes in clist
4390 for(u16 i=0; i<9; i++)
4392 clist->changeItem(i, ci.items[i]);
4399 void Server::UpdateCrafting(u16 peer_id)
4401 DSTACK(__FUNCTION_NAME);
4403 Player* player = m_env->getPlayer(peer_id);
4405 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4407 // No crafting in creative mode
4408 if(g_settings->getBool("creative_mode"))
4411 // Get the InventoryLists of the player in which we will operate
4412 InventoryList *clist = player->inventory.getList("craft");
4414 InventoryList *rlist = player->inventory.getList("craftresult");
4416 InventoryList *mlist = player->inventory.getList("main");
4419 // If the result list is not a preview and is not empty, try to
4420 // throw the item into main list
4421 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4423 // Grab item out of craftresult
4424 ItemStack item = rlist->changeItem(0, ItemStack());
4425 // Try to put in main
4426 ItemStack leftover = mlist->addItem(item);
4427 // If there are leftovers, put them back to craftresult
4428 rlist->addItem(leftover);
4429 // Inventory was modified
4430 srp->m_inventory_not_sent = true;
4433 // If result list is empty, we will make it preview what would be
4435 if(rlist->getUsedSlots() == 0)
4436 player->craftresult_is_preview = true;
4438 // If it is a preview, find out what is the crafting result
4440 if(player->craftresult_is_preview)
4442 // Clear the possible old preview in it
4443 rlist->clearItems();
4445 // Put the new preview in
4446 ItemStack crafting_result;
4447 if(GetCraftingResult(peer_id, crafting_result, false))
4448 rlist->addItem(crafting_result);
4452 RemoteClient* Server::getClient(u16 peer_id)
4454 DSTACK(__FUNCTION_NAME);
4455 //JMutexAutoLock lock(m_con_mutex);
4456 core::map<u16, RemoteClient*>::Node *n;
4457 n = m_clients.find(peer_id);
4458 // A client should exist for all peers
4460 return n->getValue();
4463 std::wstring Server::getStatusString()
4465 std::wostringstream os(std::ios_base::binary);
4468 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4470 os<<L", uptime="<<m_uptime.get();
4471 // Information about clients
4473 for(core::map<u16, RemoteClient*>::Iterator
4474 i = m_clients.getIterator();
4475 i.atEnd() == false; i++)
4477 // Get client and check that it is valid
4478 RemoteClient *client = i.getNode()->getValue();
4479 assert(client->peer_id == i.getNode()->getKey());
4480 if(client->serialization_version == SER_FMT_VER_INVALID)
4483 Player *player = m_env->getPlayer(client->peer_id);
4484 // Get name of player
4485 std::wstring name = L"unknown";
4487 name = narrow_to_wide(player->getName());
4488 // Add name to information string
4492 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4493 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4494 if(g_settings->get("motd") != "")
4495 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4499 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4501 // Add player to auth manager
4502 if(m_authmanager.exists(name) == false)
4504 infostream<<"Server: adding player "<<name
4505 <<" to auth manager"<<std::endl;
4506 m_authmanager.add(name);
4507 m_authmanager.setPrivs(name,
4508 stringToPrivs(g_settings->get("default_privs")));
4510 // Change password and save
4511 m_authmanager.setPassword(name, translatePassword(name, password));
4512 m_authmanager.save();
4515 // Saves g_settings to configpath given at initialization
4516 void Server::saveConfig()
4518 if(m_configpath != "")
4519 g_settings->updateConfigFile(m_configpath.c_str());
4522 void Server::notifyPlayer(const char *name, const std::wstring msg)
4524 Player *player = m_env->getPlayer(name);
4527 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4530 void Server::notifyPlayers(const std::wstring msg)
4532 BroadcastChatMessage(msg);
4535 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4539 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4540 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4543 // IGameDef interface
4545 IItemDefManager* Server::getItemDefManager()
4549 INodeDefManager* Server::getNodeDefManager()
4553 ICraftDefManager* Server::getCraftDefManager()
4557 ITextureSource* Server::getTextureSource()
4561 u16 Server::allocateUnknownNodeId(const std::string &name)
4563 return m_nodedef->allocateDummy(name);
4566 IWritableItemDefManager* Server::getWritableItemDefManager()
4570 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4574 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4579 const ModSpec* Server::getModSpec(const std::string &modname)
4581 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4582 i != m_mods.end(); i++){
4583 const ModSpec &mod = *i;
4584 if(mod.name == modname)
4590 v3f findSpawnPos(ServerMap &map)
4592 //return v3f(50,50,50)*BS;
4597 nodepos = v2s16(0,0);
4602 // Try to find a good place a few times
4603 for(s32 i=0; i<1000; i++)
4606 // We're going to try to throw the player to this position
4607 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4608 -range + (myrand()%(range*2)));
4609 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4610 // Get ground height at point (fallbacks to heightmap function)
4611 s16 groundheight = map.findGroundLevel(nodepos2d);
4612 // Don't go underwater
4613 if(groundheight < WATER_LEVEL)
4615 //infostream<<"-> Underwater"<<std::endl;
4618 // Don't go to high places
4619 if(groundheight > WATER_LEVEL + 4)
4621 //infostream<<"-> Underwater"<<std::endl;
4625 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4626 bool is_good = false;
4628 for(s32 i=0; i<10; i++){
4629 v3s16 blockpos = getNodeBlockPos(nodepos);
4630 map.emergeBlock(blockpos, true);
4631 MapNode n = map.getNodeNoEx(nodepos);
4632 if(n.getContent() == CONTENT_AIR){
4643 // Found a good place
4644 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4650 return intToFloat(nodepos, BS);
4653 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4656 Try to get an existing player
4658 ServerRemotePlayer *player =
4659 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4662 // If player is already connected, cancel
4663 if(player->peer_id != 0)
4665 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4670 player->peer_id = peer_id;
4672 // Reset inventory to creative if in creative mode
4673 if(g_settings->getBool("creative_mode"))
4675 // Warning: double code below
4676 // Backup actual inventory
4677 player->inventory_backup = new Inventory(m_itemdef);
4678 *(player->inventory_backup) = player->inventory;
4679 // Set creative inventory
4680 player->resetInventory();
4681 scriptapi_get_creative_inventory(m_lua, player);
4688 If player with the wanted peer_id already exists, cancel.
4690 if(m_env->getPlayer(peer_id) != NULL)
4692 infostream<<"emergePlayer(): Player with wrong name but same"
4693 " peer_id already exists"<<std::endl;
4701 /* Set player position */
4703 infostream<<"Server: Finding spawn place for player \""
4704 <<name<<"\""<<std::endl;
4706 v3f pos = findSpawnPos(m_env->getServerMap());
4708 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4710 /* Add player to environment */
4711 m_env->addPlayer(player);
4714 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4715 scriptapi_on_newplayer(m_lua, srp);
4717 /* Add stuff to inventory */
4718 if(g_settings->getBool("creative_mode"))
4720 // Warning: double code above
4721 // Backup actual inventory
4722 player->inventory_backup = new Inventory(m_itemdef);
4723 *(player->inventory_backup) = player->inventory;
4724 // Set creative inventory
4725 player->resetInventory();
4726 scriptapi_get_creative_inventory(m_lua, player);
4731 } // create new player
4734 void Server::handlePeerChange(PeerChange &c)
4736 JMutexAutoLock envlock(m_env_mutex);
4737 JMutexAutoLock conlock(m_con_mutex);
4739 if(c.type == PEER_ADDED)
4746 core::map<u16, RemoteClient*>::Node *n;
4747 n = m_clients.find(c.peer_id);
4748 // The client shouldn't already exist
4752 RemoteClient *client = new RemoteClient();
4753 client->peer_id = c.peer_id;
4754 m_clients.insert(client->peer_id, client);
4757 else if(c.type == PEER_REMOVED)
4764 core::map<u16, RemoteClient*>::Node *n;
4765 n = m_clients.find(c.peer_id);
4766 // The client should exist
4770 Mark objects to be not known by the client
4772 RemoteClient *client = n->getValue();
4774 for(core::map<u16, bool>::Iterator
4775 i = client->m_known_objects.getIterator();
4776 i.atEnd()==false; i++)
4779 u16 id = i.getNode()->getKey();
4780 ServerActiveObject* obj = m_env->getActiveObject(id);
4782 if(obj && obj->m_known_by_count > 0)
4783 obj->m_known_by_count--;
4786 ServerRemotePlayer* player =
4787 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4789 // Collect information about leaving in chat
4790 std::wstring message;
4794 std::wstring name = narrow_to_wide(player->getName());
4797 message += L" left game";
4799 message += L" (timed out)";
4803 // Remove from environment
4805 player->m_removed = true;
4807 // Set player client disconnected
4809 player->peer_id = 0;
4817 std::ostringstream os(std::ios_base::binary);
4818 for(core::map<u16, RemoteClient*>::Iterator
4819 i = m_clients.getIterator();
4820 i.atEnd() == false; i++)
4822 RemoteClient *client = i.getNode()->getValue();
4823 assert(client->peer_id == i.getNode()->getKey());
4824 if(client->serialization_version == SER_FMT_VER_INVALID)
4827 Player *player = m_env->getPlayer(client->peer_id);
4830 // Get name of player
4831 os<<player->getName()<<" ";
4834 actionstream<<player->getName()<<" "
4835 <<(c.timeout?"times out.":"leaves game.")
4836 <<" List of players: "
4837 <<os.str()<<std::endl;
4842 delete m_clients[c.peer_id];
4843 m_clients.remove(c.peer_id);
4845 // Send player info to all remaining clients
4846 //SendPlayerInfos();
4848 // Send leave chat message to all remaining clients
4849 if(message.length() != 0)
4850 BroadcastChatMessage(message);
4859 void Server::handlePeerChanges()
4861 while(m_peer_change_queue.size() > 0)
4863 PeerChange c = m_peer_change_queue.pop_front();
4865 infostream<<"Server: Handling peer change: "
4866 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4869 handlePeerChange(c);
4873 u64 Server::getPlayerPrivs(Player *player)
4877 std::string playername = player->getName();
4878 // Local player gets all privileges regardless of
4879 // what's set on their account.
4880 if(g_settings->get("name") == playername)
4886 return getPlayerAuthPrivs(playername);
4890 void dedicated_server_loop(Server &server, bool &kill)
4892 DSTACK(__FUNCTION_NAME);
4894 infostream<<DTIME<<std::endl;
4895 infostream<<"========================"<<std::endl;
4896 infostream<<"Running dedicated server"<<std::endl;
4897 infostream<<"========================"<<std::endl;
4898 infostream<<std::endl;
4900 IntervalLimiter m_profiler_interval;
4904 // This is kind of a hack but can be done like this
4905 // because server.step() is very light
4907 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4912 if(server.getShutdownRequested() || kill)
4914 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4921 float profiler_print_interval =
4922 g_settings->getFloat("profiler_print_interval");
4923 if(profiler_print_interval != 0)
4925 if(m_profiler_interval.step(0.030, profiler_print_interval))
4927 infostream<<"Profiler:"<<std::endl;
4928 g_profiler->print(infostream);
4929 g_profiler->clear();
4936 static int counter = 0;
4942 core::list<PlayerInfo> list = server.getPlayerInfo();
4943 core::list<PlayerInfo>::Iterator i;
4944 static u32 sum_old = 0;
4945 u32 sum = PIChecksum(list);
4948 infostream<<DTIME<<"Player info:"<<std::endl;
4949 for(i=list.begin(); i!=list.end(); i++)
4951 i->PrintLine(&infostream);