3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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"
33 #include "serverobject.h"
38 #include "scriptapi.h"
45 #include "content_mapnode.h"
46 #include "content_nodemeta.h"
47 #include "content_abm.h"
48 #include "content_sao.h"
53 #include "sound.h" // dummySoundManager
54 #include "event_manager.h"
56 #include "serverlist.h"
57 #include "util/string.h"
58 #include "util/pointedthing.h"
59 #include "util/mathconstants.h"
61 #include "util/serialize.h"
62 #include "defaultsettings.h"
64 void * ServerThread::Thread()
68 log_register_thread("ServerThread");
70 DSTACK(__FUNCTION_NAME);
72 BEGIN_DEBUG_EXCEPTION_HANDLER
77 //TimeTaker timer("AsyncRunStep() + Receive()");
80 //TimeTaker timer("AsyncRunStep()");
81 m_server->AsyncRunStep();
84 //infostream<<"Running m_server->Receive()"<<std::endl;
87 catch(con::NoIncomingDataException &e)
90 catch(con::PeerNotFoundException &e)
92 infostream<<"Server: PeerNotFoundException"<<std::endl;
94 catch(con::ConnectionBindFailed &e)
96 m_server->setAsyncFatalError(e.what());
100 m_server->setAsyncFatalError(e.what());
104 END_DEBUG_EXCEPTION_HANDLER(errorstream)
109 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
111 if(pos_exists) *pos_exists = false;
116 if(pos_exists) *pos_exists = true;
121 ServerActiveObject *sao = env->getActiveObject(object);
124 if(pos_exists) *pos_exists = true;
125 return sao->getBasePosition(); }
130 void RemoteClient::GetNextBlocks(Server *server, float dtime,
131 std::vector<PrioritySortedBlockTransfer> &dest)
133 DSTACK(__FUNCTION_NAME);
136 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
139 m_nothing_to_send_pause_timer -= dtime;
140 m_nearest_unsent_reset_timer += dtime;
142 if(m_nothing_to_send_pause_timer >= 0)
145 Player *player = server->m_env->getPlayer(peer_id);
146 // This can happen sometimes; clients and players are not in perfect sync.
150 // Won't send anything if already sending
151 if(m_blocks_sending.size() >= g_settings->getU16
152 ("max_simultaneous_block_sends_per_client"))
154 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
158 //TimeTaker timer("RemoteClient::GetNextBlocks");
160 v3f playerpos = player->getPosition();
161 v3f playerspeed = player->getSpeed();
162 v3f playerspeeddir(0,0,0);
163 if(playerspeed.getLength() > 1.0*BS)
164 playerspeeddir = playerspeed / playerspeed.getLength();
165 // Predict to next block
166 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
168 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
170 v3s16 center = getNodeBlockPos(center_nodepos);
172 // Camera position and direction
173 v3f camera_pos = player->getEyePosition();
174 v3f camera_dir = v3f(0,0,1);
175 camera_dir.rotateYZBy(player->getPitch());
176 camera_dir.rotateXZBy(player->getYaw());
178 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
179 <<camera_dir.Z<<")"<<std::endl;*/
182 Get the starting value of the block finder radius.
185 if(m_last_center != center)
187 m_nearest_unsent_d = 0;
188 m_last_center = center;
191 /*infostream<<"m_nearest_unsent_reset_timer="
192 <<m_nearest_unsent_reset_timer<<std::endl;*/
194 // Reset periodically to workaround for some bugs or stuff
195 if(m_nearest_unsent_reset_timer > 20.0)
197 m_nearest_unsent_reset_timer = 0;
198 m_nearest_unsent_d = 0;
199 //infostream<<"Resetting m_nearest_unsent_d for "
200 // <<server->getPlayerName(peer_id)<<std::endl;
203 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
204 s16 d_start = m_nearest_unsent_d;
206 //infostream<<"d_start="<<d_start<<std::endl;
208 u16 max_simul_sends_setting = g_settings->getU16
209 ("max_simultaneous_block_sends_per_client");
210 u16 max_simul_sends_usually = max_simul_sends_setting;
213 Check the time from last addNode/removeNode.
215 Decrease send rate if player is building stuff.
217 m_time_from_building += dtime;
218 if(m_time_from_building < g_settings->getFloat(
219 "full_block_send_enable_min_time_from_building"))
221 max_simul_sends_usually
222 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
226 Number of blocks sending + number of blocks selected for sending
228 u32 num_blocks_selected = m_blocks_sending.size();
231 next time d will be continued from the d from which the nearest
232 unsent block was found this time.
234 This is because not necessarily any of the blocks found this
235 time are actually sent.
237 s32 new_nearest_unsent_d = -1;
239 s16 d_max = g_settings->getS16("max_block_send_distance");
240 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
242 // Don't loop very much at a time
243 s16 max_d_increment_at_time = 2;
244 if(d_max > d_start + max_d_increment_at_time)
245 d_max = d_start + max_d_increment_at_time;
246 /*if(d_max_gen > d_start+2)
247 d_max_gen = d_start+2;*/
249 //infostream<<"Starting from "<<d_start<<std::endl;
251 s32 nearest_emerged_d = -1;
252 s32 nearest_emergefull_d = -1;
253 s32 nearest_sent_d = -1;
254 bool queue_is_full = false;
257 for(d = d_start; d <= d_max; d++)
259 /*errorstream<<"checking d="<<d<<" for "
260 <<server->getPlayerName(peer_id)<<std::endl;*/
261 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
264 If m_nearest_unsent_d was changed by the EmergeThread
265 (it can change it to 0 through SetBlockNotSent),
267 Else update m_nearest_unsent_d
269 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
271 d = m_nearest_unsent_d;
272 last_nearest_unsent_d = m_nearest_unsent_d;
276 Get the border/face dot coordinates of a "d-radiused"
279 std::list<v3s16> list;
280 getFacePositions(list, d);
282 std::list<v3s16>::iterator li;
283 for(li=list.begin(); li!=list.end(); ++li)
285 v3s16 p = *li + center;
289 - Don't allow too many simultaneous transfers
290 - EXCEPT when the blocks are very close
292 Also, don't send blocks that are already flying.
295 // Start with the usual maximum
296 u16 max_simul_dynamic = max_simul_sends_usually;
298 // If block is very close, allow full maximum
299 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
300 max_simul_dynamic = max_simul_sends_setting;
302 // Don't select too many blocks for sending
303 if(num_blocks_selected >= max_simul_dynamic)
305 queue_is_full = true;
306 goto queue_full_break;
309 // Don't send blocks that are currently being transferred
310 if(m_blocks_sending.find(p) != m_blocks_sending.end())
316 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
317 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
318 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
319 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
320 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
321 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
324 // If this is true, inexistent block will be made from scratch
325 bool generate = d <= d_max_gen;
328 /*// Limit the generating area vertically to 2/3
329 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
332 // Limit the send area vertically to 1/2
333 if(abs(p.Y - center.Y) > d_max / 2)
339 If block is far away, don't generate it unless it is
345 // Block center y in nodes
346 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
347 // Don't generate if it's very high or very low
348 if(y < -64 || y > 64)
352 v2s16 p2d_nodes_center(
356 // Get ground height in nodes
357 s16 gh = server->m_env->getServerMap().findGroundLevel(
360 // If differs a lot, don't generate
361 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
363 // Actually, don't even send it
369 //infostream<<"d="<<d<<std::endl;
372 Don't generate or send if not in sight
373 FIXME This only works if the client uses a small enough
374 FOV setting. The default of 72 degrees is fine.
377 float camera_fov = (72.0*M_PI/180) * 4./3.;
378 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
384 Don't send already sent blocks
387 if(m_blocks_sent.find(p) != m_blocks_sent.end())
394 Check if map has this block
396 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
398 bool surely_not_found_on_disk = false;
399 bool block_is_invalid = false;
402 // Reset usage timer, this block will be of use in the future.
403 block->resetUsageTimer();
405 // Block is dummy if data doesn't exist.
406 // It means it has been not found from disk and not generated
409 surely_not_found_on_disk = true;
412 // Block is valid if lighting is up-to-date and data exists
413 if(block->isValid() == false)
415 block_is_invalid = true;
418 /*if(block->isFullyGenerated() == false)
420 block_is_invalid = true;
425 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
426 v2s16 chunkpos = map->sector_to_chunk(p2d);
427 if(map->chunkNonVolatile(chunkpos) == false)
428 block_is_invalid = true;
430 if(block->isGenerated() == false)
431 block_is_invalid = true;
434 If block is not close, don't send it unless it is near
437 Block is near ground level if night-time mesh
438 differs from day-time mesh.
442 if(block->getDayNightDiff() == false)
449 If block has been marked to not exist on disk (dummy)
450 and generating new ones is not wanted, skip block.
452 if(generate == false && surely_not_found_on_disk == true)
459 Add inexistent block to emerge queue.
461 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
463 /* //TODO: Get value from somewhere
464 // Allow only one block in emerge queue
465 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
466 // Allow two blocks in queue per client
467 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
469 // Make it more responsive when needing to generate stuff
470 if(surely_not_found_on_disk)
472 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
474 //infostream<<"Adding block to emerge queue"<<std::endl;
476 // Add it to the emerge queue and trigger the thread
479 if(generate == false)
480 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
482 server->m_emerge_queue.addBlock(peer_id, p, flags);
483 server->m_emergethread.trigger();
485 if(nearest_emerged_d == -1)
486 nearest_emerged_d = d;
488 if(nearest_emergefull_d == -1)
489 nearest_emergefull_d = d;
490 goto queue_full_break;
494 if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) {
495 if (nearest_emerged_d == -1)
496 nearest_emerged_d = d;
498 if (nearest_emergefull_d == -1)
499 nearest_emergefull_d = d;
500 goto queue_full_break;
507 if(nearest_sent_d == -1)
511 Add block to send queue
514 /*errorstream<<"sending from d="<<d<<" to "
515 <<server->getPlayerName(peer_id)<<std::endl;*/
517 PrioritySortedBlockTransfer q((float)d, p, peer_id);
521 num_blocks_selected += 1;
526 //infostream<<"Stopped at "<<d<<std::endl;
528 // If nothing was found for sending and nothing was queued for
529 // emerging, continue next time browsing from here
530 if(nearest_emerged_d != -1){
531 new_nearest_unsent_d = nearest_emerged_d;
532 } else if(nearest_emergefull_d != -1){
533 new_nearest_unsent_d = nearest_emergefull_d;
535 if(d > g_settings->getS16("max_block_send_distance")){
536 new_nearest_unsent_d = 0;
537 m_nothing_to_send_pause_timer = 2.0;
538 /*infostream<<"GetNextBlocks(): d wrapped around for "
539 <<server->getPlayerName(peer_id)
540 <<"; setting to 0 and pausing"<<std::endl;*/
542 if(nearest_sent_d != -1)
543 new_nearest_unsent_d = nearest_sent_d;
545 new_nearest_unsent_d = d;
549 if(new_nearest_unsent_d != -1)
550 m_nearest_unsent_d = new_nearest_unsent_d;
552 /*timer_result = timer.stop(true);
553 if(timer_result != 0)
554 infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
557 void RemoteClient::GotBlock(v3s16 p)
559 if(m_blocks_sending.find(p) != m_blocks_sending.end())
560 m_blocks_sending.erase(p);
563 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
564 " m_blocks_sending"<<std::endl;*/
565 m_excess_gotblocks++;
567 m_blocks_sent.insert(p);
570 void RemoteClient::SentBlock(v3s16 p)
572 if(m_blocks_sending.find(p) == m_blocks_sending.end())
573 m_blocks_sending[p] = 0.0;
575 infostream<<"RemoteClient::SentBlock(): Sent block"
576 " already in m_blocks_sending"<<std::endl;
579 void RemoteClient::SetBlockNotSent(v3s16 p)
581 m_nearest_unsent_d = 0;
583 if(m_blocks_sending.find(p) != m_blocks_sending.end())
584 m_blocks_sending.erase(p);
585 if(m_blocks_sent.find(p) != m_blocks_sent.end())
586 m_blocks_sent.erase(p);
589 void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
591 m_nearest_unsent_d = 0;
593 for(std::map<v3s16, MapBlock*>::iterator
595 i != blocks.end(); ++i)
599 if(m_blocks_sending.find(p) != m_blocks_sending.end())
600 m_blocks_sending.erase(p);
601 if(m_blocks_sent.find(p) != m_blocks_sent.end())
602 m_blocks_sent.erase(p);
610 PlayerInfo::PlayerInfo()
616 void PlayerInfo::PrintLine(std::ostream *s)
619 (*s)<<"\""<<name<<"\" ("
620 <<(position.X/10)<<","<<(position.Y/10)
621 <<","<<(position.Z/10)<<") ";
623 (*s)<<" avg_rtt="<<avg_rtt;
632 const std::string &path_world,
633 const std::string &path_config,
634 const SubgameSpec &gamespec,
635 bool simple_singleplayer_mode
637 m_path_world(path_world),
638 m_path_config(path_config),
639 m_gamespec(gamespec),
640 m_simple_singleplayer_mode(simple_singleplayer_mode),
641 m_async_fatal_error(""),
643 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
644 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
646 m_rollback_sink_enabled(true),
647 m_enable_rollback_recording(false),
650 m_itemdef(createItemDefManager()),
651 m_nodedef(createNodeDefManager()),
652 m_craftdef(createCraftDefManager()),
653 m_event(new EventManager()),
655 m_time_of_day_send_timer(0),
657 m_shutdown_requested(false),
658 m_ignore_map_edit_events(false),
659 m_ignore_map_edit_events_peer_id(0)
661 m_liquid_transform_timer = 0.0;
662 m_liquid_transform_every = 1.0;
663 m_print_info_timer = 0.0;
664 m_masterserver_timer = 0.0;
665 m_objectdata_timer = 0.0;
666 m_emergethread_trigger_timer = 0.0;
667 m_savemap_timer = 0.0;
668 m_clients_number = 0;
672 m_step_dtime_mutex.Init();
676 throw ServerError("Supplied empty world path");
678 if(!gamespec.isValid())
679 throw ServerError("Supplied invalid gamespec");
681 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
682 if(m_simple_singleplayer_mode)
683 infostream<<" in simple singleplayer mode"<<std::endl;
685 infostream<<std::endl;
686 infostream<<"- world: "<<m_path_world<<std::endl;
687 infostream<<"- config: "<<m_path_config<<std::endl;
688 infostream<<"- game: "<<m_gamespec.path<<std::endl;
690 // Initialize default settings and override defaults with those provided
692 set_default_settings(g_settings);
693 Settings gamedefaults;
694 getGameMinetestConfig(gamespec.path, gamedefaults);
695 override_default_settings(g_settings, &gamedefaults);
697 // Create emerge manager
698 m_emerge = new EmergeManager(this);
700 // Create rollback manager
701 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
702 m_rollback = createRollbackManager(rollback_path, this);
704 // Create world if it doesn't exist
705 if(!initializeWorld(m_path_world, m_gamespec.id))
706 throw ServerError("Failed to initialize world");
708 ModConfiguration modconf(m_path_world);
709 m_mods = modconf.getMods();
710 std::list<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
711 // complain about mods with unsatisfied dependencies
712 if(!modconf.isConsistent())
714 for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
715 it != unsatisfied_mods.end(); ++it)
718 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
719 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
720 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
721 errorstream << " \"" << *dep_it << "\"";
722 errorstream << std::endl;
726 Settings worldmt_settings;
727 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
728 worldmt_settings.readConfigFile(worldmt.c_str());
729 std::vector<std::string> names = worldmt_settings.getNames();
730 std::set<std::string> exclude_mod_names;
731 std::set<std::string> load_mod_names;
732 for(std::vector<std::string>::iterator it = names.begin();
733 it != names.end(); ++it)
735 std::string name = *it;
736 if (name.compare(0,9,"load_mod_")==0)
738 if(worldmt_settings.getBool(name))
739 load_mod_names.insert(name.substr(9));
741 exclude_mod_names.insert(name.substr(9));
744 // complain about mods declared to be loaded, but not found
745 for(std::vector<ModSpec>::iterator it = m_mods.begin();
746 it != m_mods.end(); ++it)
747 load_mod_names.erase((*it).name);
748 for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
749 it != unsatisfied_mods.end(); ++it)
750 load_mod_names.erase((*it).name);
751 if(!load_mod_names.empty())
753 errorstream << "The following mods could not be found:";
754 for(std::set<std::string>::iterator it = load_mod_names.begin();
755 it != load_mod_names.end(); ++it)
756 errorstream << " \"" << (*it) << "\"";
757 errorstream << std::endl;
760 // Path to builtin.lua
761 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
764 JMutexAutoLock envlock(m_env_mutex);
765 JMutexAutoLock conlock(m_con_mutex);
767 // Initialize scripting
769 infostream<<"Server: Initializing Lua"<<std::endl;
770 m_lua = script_init();
773 scriptapi_export(m_lua, this);
774 // Load and run builtin.lua
775 infostream<<"Server: Loading builtin.lua [\""
776 <<builtinpath<<"\"]"<<std::endl;
777 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
779 errorstream<<"Server: Failed to load and run "
780 <<builtinpath<<std::endl;
781 throw ModError("Failed to load and run "+builtinpath);
784 infostream<<"Server: Loading mods: ";
785 for(std::vector<ModSpec>::iterator i = m_mods.begin();
786 i != m_mods.end(); i++){
787 const ModSpec &mod = *i;
788 infostream<<mod.name<<" ";
790 infostream<<std::endl;
791 // Load and run "mod" scripts
792 for(std::vector<ModSpec>::iterator i = m_mods.begin();
793 i != m_mods.end(); i++){
794 const ModSpec &mod = *i;
795 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
796 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
797 <<scriptpath<<"\"]"<<std::endl;
798 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
800 errorstream<<"Server: Failed to load and run "
801 <<scriptpath<<std::endl;
802 throw ModError("Failed to load and run "+scriptpath);
806 // Read Textures and calculate sha1 sums
809 // Apply item aliases in the node definition manager
810 m_nodedef->updateAliases(m_itemdef);
812 // Initialize Environment
813 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
814 m_env = new ServerEnvironment(servermap, m_lua, this, this);
816 m_emerge->initMapgens(servermap->getMapgenParams());
818 // Give environment reference to scripting api
819 scriptapi_add_environment(m_lua, m_env);
821 // Register us to receive map edit events
822 servermap->addEventReceiver(this);
824 // If file exists, load environment metadata
825 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
827 infostream<<"Server: Loading environment metadata"<<std::endl;
828 m_env->loadMeta(m_path_world);
832 infostream<<"Server: Loading players"<<std::endl;
833 m_env->deSerializePlayers(m_path_world);
836 Add some test ActiveBlockModifiers to environment
838 add_legacy_abms(m_env, m_nodedef);
840 m_liquid_transform_every = g_settings->getFloat("liquid_update");
845 infostream<<"Server destructing"<<std::endl;
848 Send shutdown message
851 JMutexAutoLock conlock(m_con_mutex);
853 std::wstring line = L"*** Server shutting down";
856 Send the message to clients
858 for(std::map<u16, RemoteClient*>::iterator
859 i = m_clients.begin();
860 i != m_clients.end(); ++i)
862 // Get client and check that it is valid
863 RemoteClient *client = i->second;
864 assert(client->peer_id == i->first);
865 if(client->serialization_version == SER_FMT_VER_INVALID)
869 SendChatMessage(client->peer_id, line);
871 catch(con::PeerNotFoundException &e)
877 JMutexAutoLock envlock(m_env_mutex);
878 JMutexAutoLock conlock(m_con_mutex);
881 Execute script shutdown hooks
883 scriptapi_on_shutdown(m_lua);
887 JMutexAutoLock envlock(m_env_mutex);
892 infostream<<"Server: Saving players"<<std::endl;
893 m_env->serializePlayers(m_path_world);
896 Save environment metadata
898 infostream<<"Server: Saving environment metadata"<<std::endl;
899 m_env->saveMeta(m_path_world);
907 //shutdown all emerge threads first!
914 JMutexAutoLock clientslock(m_con_mutex);
916 for(std::map<u16, RemoteClient*>::iterator
917 i = m_clients.begin();
918 i != m_clients.end(); ++i)
926 // Delete things in the reverse order of creation
934 // Deinitialize scripting
935 infostream<<"Server: Deinitializing scripting"<<std::endl;
936 script_deinit(m_lua);
938 // Delete detached inventories
940 for(std::map<std::string, Inventory*>::iterator
941 i = m_detached_inventories.begin();
942 i != m_detached_inventories.end(); i++){
948 void Server::start(unsigned short port)
950 DSTACK(__FUNCTION_NAME);
951 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
953 // Stop thread if already running
956 // Initialize connection
957 m_con.SetTimeoutMs(30);
961 m_thread.setRun(true);
964 // ASCII art for the win!
966 <<" .__ __ __ "<<std::endl
967 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
968 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
969 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
970 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
971 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
972 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
973 actionstream<<"Server for gameid=\""<<m_gamespec.id
974 <<"\" listening on port "<<port<<"."<<std::endl;
979 DSTACK(__FUNCTION_NAME);
981 infostream<<"Server: Stopping and waiting threads"<<std::endl;
983 // Stop threads (set run=false first so both start stopping)
984 m_thread.setRun(false);
985 //m_emergethread.setRun(false);
987 //m_emergethread.stop();
989 infostream<<"Server: Threads stopped"<<std::endl;
992 void Server::step(float dtime)
994 DSTACK(__FUNCTION_NAME);
999 JMutexAutoLock lock(m_step_dtime_mutex);
1000 m_step_dtime += dtime;
1002 // Throw if fatal error occurred in thread
1003 std::string async_err = m_async_fatal_error.get();
1004 if(async_err != ""){
1005 throw ServerError(async_err);
1009 void Server::AsyncRunStep()
1011 DSTACK(__FUNCTION_NAME);
1013 g_profiler->add("Server::AsyncRunStep (num)", 1);
1017 JMutexAutoLock lock1(m_step_dtime_mutex);
1018 dtime = m_step_dtime;
1022 // Send blocks to clients
1029 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1031 //infostream<<"Server steps "<<dtime<<std::endl;
1032 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1035 JMutexAutoLock lock1(m_step_dtime_mutex);
1036 m_step_dtime -= dtime;
1043 m_uptime.set(m_uptime.get() + dtime);
1047 // Process connection's timeouts
1048 JMutexAutoLock lock2(m_con_mutex);
1049 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1050 m_con.RunTimeouts(dtime);
1054 // This has to be called so that the client list gets synced
1055 // with the peer list of the connection
1056 handlePeerChanges();
1060 Update time of day and overall game time
1063 JMutexAutoLock envlock(m_env_mutex);
1065 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1068 Send to clients at constant intervals
1071 m_time_of_day_send_timer -= dtime;
1072 if(m_time_of_day_send_timer < 0.0)
1074 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1076 //JMutexAutoLock envlock(m_env_mutex);
1077 JMutexAutoLock conlock(m_con_mutex);
1079 for(std::map<u16, RemoteClient*>::iterator
1080 i = m_clients.begin();
1081 i != m_clients.end(); ++i)
1083 RemoteClient *client = i->second;
1084 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1085 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1087 m_con.Send(client->peer_id, 0, data, true);
1093 JMutexAutoLock lock(m_env_mutex);
1095 ScopeProfiler sp(g_profiler, "SEnv step");
1096 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1100 const float map_timer_and_unload_dtime = 2.92;
1101 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1103 JMutexAutoLock lock(m_env_mutex);
1104 // Run Map's timers and unload unused data
1105 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1106 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1107 g_settings->getFloat("server_unload_unused_data_timeout"));
1118 JMutexAutoLock lock(m_env_mutex);
1119 JMutexAutoLock lock2(m_con_mutex);
1121 ScopeProfiler sp(g_profiler, "Server: handle players");
1123 for(std::map<u16, RemoteClient*>::iterator
1124 i = m_clients.begin();
1125 i != m_clients.end(); ++i)
1127 RemoteClient *client = i->second;
1128 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1129 if(playersao == NULL)
1133 Handle player HPs (die if hp=0)
1135 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
1137 if(playersao->getHP() == 0)
1138 DiePlayer(client->peer_id);
1140 SendPlayerHP(client->peer_id);
1144 Send player inventories if necessary
1146 if(playersao->m_moved){
1147 SendMovePlayer(client->peer_id);
1148 playersao->m_moved = false;
1150 if(playersao->m_inventory_not_sent){
1151 UpdateCrafting(client->peer_id);
1152 SendInventory(client->peer_id);
1157 /* Transform liquids */
1158 m_liquid_transform_timer += dtime;
1159 if(m_liquid_transform_timer >= m_liquid_transform_every)
1161 m_liquid_transform_timer -= m_liquid_transform_every;
1163 JMutexAutoLock lock(m_env_mutex);
1165 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1167 std::map<v3s16, MapBlock*> modified_blocks;
1168 m_env->getMap().transformLiquids(modified_blocks);
1173 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1174 ServerMap &map = ((ServerMap&)m_env->getMap());
1175 map.updateLighting(modified_blocks, lighting_modified_blocks);
1177 // Add blocks modified by lighting to modified_blocks
1178 for(core::map<v3s16, MapBlock*>::Iterator
1179 i = lighting_modified_blocks.getIterator();
1180 i.atEnd() == false; i++)
1182 MapBlock *block = i.getNode()->getValue();
1183 modified_blocks.insert(block->getPos(), block);
1187 Set the modified blocks unsent for all the clients
1190 JMutexAutoLock lock2(m_con_mutex);
1192 for(std::map<u16, RemoteClient*>::iterator
1193 i = m_clients.begin();
1194 i != m_clients.end(); ++i)
1196 RemoteClient *client = i->second;
1198 if(modified_blocks.size() > 0)
1200 // Remove block from sent history
1201 client->SetBlocksNotSent(modified_blocks);
1206 // Periodically print some info
1208 float &counter = m_print_info_timer;
1214 JMutexAutoLock lock2(m_con_mutex);
1215 m_clients_number = 0;
1216 if(m_clients.size() != 0)
1217 infostream<<"Players:"<<std::endl;
1218 for(std::map<u16, RemoteClient*>::iterator
1219 i = m_clients.begin();
1220 i != m_clients.end(); ++i)
1222 //u16 peer_id = i.getNode()->getKey();
1223 RemoteClient *client = i->second;
1224 Player *player = m_env->getPlayer(client->peer_id);
1227 infostream<<"* "<<player->getName()<<"\t";
1228 client->PrintInfo(infostream);
1236 // send masterserver announce
1238 float &counter = m_masterserver_timer;
1239 if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
1241 ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id);
1248 //if(g_settings->getBool("enable_experimental"))
1252 Check added and deleted active objects
1255 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1256 JMutexAutoLock envlock(m_env_mutex);
1257 JMutexAutoLock conlock(m_con_mutex);
1259 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1261 // Radius inside which objects are active
1262 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1263 radius *= MAP_BLOCKSIZE;
1265 for(std::map<u16, RemoteClient*>::iterator
1266 i = m_clients.begin();
1267 i != m_clients.end(); ++i)
1269 RemoteClient *client = i->second;
1271 // If definitions and textures have not been sent, don't
1272 // send objects either
1273 if(!client->definitions_sent)
1276 Player *player = m_env->getPlayer(client->peer_id);
1279 // This can happen if the client timeouts somehow
1280 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1282 <<" has no associated player"<<std::endl;*/
1285 v3s16 pos = floatToInt(player->getPosition(), BS);
1287 std::set<u16> removed_objects;
1288 std::set<u16> added_objects;
1289 m_env->getRemovedActiveObjects(pos, radius,
1290 client->m_known_objects, removed_objects);
1291 m_env->getAddedActiveObjects(pos, radius,
1292 client->m_known_objects, added_objects);
1294 // Ignore if nothing happened
1295 if(removed_objects.size() == 0 && added_objects.size() == 0)
1297 //infostream<<"active objects: none changed"<<std::endl;
1301 std::string data_buffer;
1305 // Handle removed objects
1306 writeU16((u8*)buf, removed_objects.size());
1307 data_buffer.append(buf, 2);
1308 for(std::set<u16>::iterator
1309 i = removed_objects.begin();
1310 i != removed_objects.end(); ++i)
1314 ServerActiveObject* obj = m_env->getActiveObject(id);
1316 // Add to data buffer for sending
1317 writeU16((u8*)buf, id);
1318 data_buffer.append(buf, 2);
1320 // Remove from known objects
1321 client->m_known_objects.erase(id);
1323 if(obj && obj->m_known_by_count > 0)
1324 obj->m_known_by_count--;
1327 // Handle added objects
1328 writeU16((u8*)buf, added_objects.size());
1329 data_buffer.append(buf, 2);
1330 for(std::set<u16>::iterator
1331 i = added_objects.begin();
1332 i != added_objects.end(); ++i)
1336 ServerActiveObject* obj = m_env->getActiveObject(id);
1339 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1341 infostream<<"WARNING: "<<__FUNCTION_NAME
1342 <<": NULL object"<<std::endl;
1344 type = obj->getSendType();
1346 // Add to data buffer for sending
1347 writeU16((u8*)buf, id);
1348 data_buffer.append(buf, 2);
1349 writeU8((u8*)buf, type);
1350 data_buffer.append(buf, 1);
1353 data_buffer.append(serializeLongString(
1354 obj->getClientInitializationData(client->net_proto_version)));
1356 data_buffer.append(serializeLongString(""));
1358 // Add to known objects
1359 client->m_known_objects.insert(id);
1362 obj->m_known_by_count++;
1366 SharedBuffer<u8> reply(2 + data_buffer.size());
1367 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1368 memcpy((char*)&reply[2], data_buffer.c_str(),
1369 data_buffer.size());
1371 m_con.Send(client->peer_id, 0, reply, true);
1373 verbosestream<<"Server: Sent object remove/add: "
1374 <<removed_objects.size()<<" removed, "
1375 <<added_objects.size()<<" added, "
1376 <<"packet size is "<<reply.getSize()<<std::endl;
1381 Collect a list of all the objects known by the clients
1382 and report it back to the environment.
1385 core::map<u16, bool> all_known_objects;
1387 for(core::map<u16, RemoteClient*>::Iterator
1388 i = m_clients.getIterator();
1389 i.atEnd() == false; i++)
1391 RemoteClient *client = i.getNode()->getValue();
1392 // Go through all known objects of client
1393 for(core::map<u16, bool>::Iterator
1394 i = client->m_known_objects.getIterator();
1395 i.atEnd()==false; i++)
1397 u16 id = i.getNode()->getKey();
1398 all_known_objects[id] = true;
1402 m_env->setKnownActiveObjects(whatever);
1408 Send object messages
1411 JMutexAutoLock envlock(m_env_mutex);
1412 JMutexAutoLock conlock(m_con_mutex);
1414 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1417 // Value = data sent by object
1418 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
1420 // Get active object messages from environment
1423 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1427 std::list<ActiveObjectMessage>* message_list = NULL;
1428 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
1429 n = buffered_messages.find(aom.id);
1430 if(n == buffered_messages.end())
1432 message_list = new std::list<ActiveObjectMessage>;
1433 buffered_messages[aom.id] = message_list;
1437 message_list = n->second;
1439 message_list->push_back(aom);
1442 // Route data to every client
1443 for(std::map<u16, RemoteClient*>::iterator
1444 i = m_clients.begin();
1445 i != m_clients.end(); ++i)
1447 RemoteClient *client = i->second;
1448 std::string reliable_data;
1449 std::string unreliable_data;
1450 // Go through all objects in message buffer
1451 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1452 j = buffered_messages.begin();
1453 j != buffered_messages.end(); ++j)
1455 // If object is not known by client, skip it
1457 if(client->m_known_objects.find(id) == client->m_known_objects.end())
1459 // Get message list of object
1460 std::list<ActiveObjectMessage>* list = j->second;
1461 // Go through every message
1462 for(std::list<ActiveObjectMessage>::iterator
1463 k = list->begin(); k != list->end(); ++k)
1465 // Compose the full new data with header
1466 ActiveObjectMessage aom = *k;
1467 std::string new_data;
1470 writeU16((u8*)&buf[0], aom.id);
1471 new_data.append(buf, 2);
1473 new_data += serializeString(aom.datastring);
1474 // Add data to buffer
1476 reliable_data += new_data;
1478 unreliable_data += new_data;
1482 reliable_data and unreliable_data are now ready.
1485 if(reliable_data.size() > 0)
1487 SharedBuffer<u8> reply(2 + reliable_data.size());
1488 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1489 memcpy((char*)&reply[2], reliable_data.c_str(),
1490 reliable_data.size());
1492 m_con.Send(client->peer_id, 0, reply, true);
1494 if(unreliable_data.size() > 0)
1496 SharedBuffer<u8> reply(2 + unreliable_data.size());
1497 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1498 memcpy((char*)&reply[2], unreliable_data.c_str(),
1499 unreliable_data.size());
1500 // Send as unreliable
1501 m_con.Send(client->peer_id, 0, reply, false);
1504 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1506 infostream<<"Server: Size of object message data: "
1507 <<"reliable: "<<reliable_data.size()
1508 <<", unreliable: "<<unreliable_data.size()
1513 // Clear buffered_messages
1514 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1515 i = buffered_messages.begin();
1516 i != buffered_messages.end(); ++i)
1522 } // enable_experimental
1525 Send queued-for-sending map edit events.
1528 // We will be accessing the environment and the connection
1529 JMutexAutoLock lock(m_env_mutex);
1530 JMutexAutoLock conlock(m_con_mutex);
1532 // Don't send too many at a time
1535 // Single change sending is disabled if queue size is not small
1536 bool disable_single_change_sending = false;
1537 if(m_unsent_map_edit_queue.size() >= 4)
1538 disable_single_change_sending = true;
1540 int event_count = m_unsent_map_edit_queue.size();
1542 // We'll log the amount of each
1545 while(m_unsent_map_edit_queue.size() != 0)
1547 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1549 // Players far away from the change are stored here.
1550 // Instead of sending the changes, MapBlocks are set not sent
1552 std::list<u16> far_players;
1554 if(event->type == MEET_ADDNODE)
1556 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1557 prof.add("MEET_ADDNODE", 1);
1558 if(disable_single_change_sending)
1559 sendAddNode(event->p, event->n, event->already_known_by_peer,
1562 sendAddNode(event->p, event->n, event->already_known_by_peer,
1565 else if(event->type == MEET_REMOVENODE)
1567 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1568 prof.add("MEET_REMOVENODE", 1);
1569 if(disable_single_change_sending)
1570 sendRemoveNode(event->p, event->already_known_by_peer,
1573 sendRemoveNode(event->p, event->already_known_by_peer,
1576 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1578 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1579 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1580 setBlockNotSent(event->p);
1582 else if(event->type == MEET_OTHER)
1584 infostream<<"Server: MEET_OTHER"<<std::endl;
1585 prof.add("MEET_OTHER", 1);
1586 for(std::set<v3s16>::iterator
1587 i = event->modified_blocks.begin();
1588 i != event->modified_blocks.end(); ++i)
1590 setBlockNotSent(*i);
1595 prof.add("unknown", 1);
1596 infostream<<"WARNING: Server: Unknown MapEditEvent "
1597 <<((u32)event->type)<<std::endl;
1601 Set blocks not sent to far players
1603 if(far_players.size() > 0)
1605 // Convert list format to that wanted by SetBlocksNotSent
1606 std::map<v3s16, MapBlock*> modified_blocks2;
1607 for(std::set<v3s16>::iterator
1608 i = event->modified_blocks.begin();
1609 i != event->modified_blocks.end(); ++i)
1611 modified_blocks2[*i] =
1612 m_env->getMap().getBlockNoCreateNoEx(*i);
1614 // Set blocks not sent
1615 for(std::list<u16>::iterator
1616 i = far_players.begin();
1617 i != far_players.end(); ++i)
1620 RemoteClient *client = getClient(peer_id);
1623 client->SetBlocksNotSent(modified_blocks2);
1629 /*// Don't send too many at a time
1631 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1635 if(event_count >= 5){
1636 infostream<<"Server: MapEditEvents:"<<std::endl;
1637 prof.print(infostream);
1638 } else if(event_count != 0){
1639 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1640 prof.print(verbosestream);
1646 Trigger emergethread (it somehow gets to a non-triggered but
1647 bysy state sometimes)
1650 float &counter = m_emergethread_trigger_timer;
1656 for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
1657 m_emerge->emergethread[i]->trigger();
1659 // Update m_enable_rollback_recording here too
1660 m_enable_rollback_recording =
1661 g_settings->getBool("enable_rollback_recording");
1665 // Save map, players and auth stuff
1667 float &counter = m_savemap_timer;
1669 if(counter >= g_settings->getFloat("server_map_save_interval"))
1672 JMutexAutoLock lock(m_env_mutex);
1674 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1677 if(m_banmanager.isModified())
1678 m_banmanager.save();
1680 // Save changed parts of map
1681 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1684 m_env->serializePlayers(m_path_world);
1686 // Save environment metadata
1687 m_env->saveMeta(m_path_world);
1692 void Server::Receive()
1694 DSTACK(__FUNCTION_NAME);
1695 SharedBuffer<u8> data;
1700 JMutexAutoLock conlock(m_con_mutex);
1701 datasize = m_con.Receive(peer_id, data);
1704 // This has to be called so that the client list gets synced
1705 // with the peer list of the connection
1706 handlePeerChanges();
1708 ProcessData(*data, datasize, peer_id);
1710 catch(con::InvalidIncomingDataException &e)
1712 infostream<<"Server::Receive(): "
1713 "InvalidIncomingDataException: what()="
1714 <<e.what()<<std::endl;
1716 catch(con::PeerNotFoundException &e)
1718 //NOTE: This is not needed anymore
1720 // The peer has been disconnected.
1721 // Find the associated player and remove it.
1723 /*JMutexAutoLock envlock(m_env_mutex);
1725 infostream<<"ServerThread: peer_id="<<peer_id
1726 <<" has apparently closed connection. "
1727 <<"Removing player."<<std::endl;
1729 m_env->removePlayer(peer_id);*/
1733 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1735 DSTACK(__FUNCTION_NAME);
1736 // Environment is locked first.
1737 JMutexAutoLock envlock(m_env_mutex);
1738 JMutexAutoLock conlock(m_con_mutex);
1740 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1743 Address address = m_con.GetPeerAddress(peer_id);
1744 std::string addr_s = address.serializeString();
1746 // drop player if is ip is banned
1747 if(m_banmanager.isIpBanned(addr_s)){
1748 infostream<<"Server: A banned client tried to connect from "
1749 <<addr_s<<"; banned name was "
1750 <<m_banmanager.getBanName(addr_s)<<std::endl;
1751 // This actually doesn't seem to transfer to the client
1752 SendAccessDenied(m_con, peer_id,
1753 L"Your ip is banned. Banned name was "
1754 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1755 m_con.DeletePeer(peer_id);
1759 catch(con::PeerNotFoundException &e)
1761 infostream<<"Server::ProcessData(): Cancelling: peer "
1762 <<peer_id<<" not found"<<std::endl;
1766 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1768 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1776 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1778 if(command == TOSERVER_INIT)
1780 // [0] u16 TOSERVER_INIT
1781 // [2] u8 SER_FMT_VER_HIGHEST
1782 // [3] u8[20] player_name
1783 // [23] u8[28] password <--- can be sent without this, from old versions
1785 if(datasize < 2+1+PLAYERNAME_SIZE)
1788 verbosestream<<"Server: Got TOSERVER_INIT from "
1789 <<peer_id<<std::endl;
1791 // First byte after command is maximum supported
1792 // serialization version
1793 u8 client_max = data[2];
1794 u8 our_max = SER_FMT_VER_HIGHEST;
1795 // Use the highest version supported by both
1796 u8 deployed = std::min(client_max, our_max);
1797 // If it's lower than the lowest supported, give up.
1798 if(deployed < SER_FMT_VER_LOWEST)
1799 deployed = SER_FMT_VER_INVALID;
1801 //peer->serialization_version = deployed;
1802 getClient(peer_id)->pending_serialization_version = deployed;
1804 if(deployed == SER_FMT_VER_INVALID)
1806 actionstream<<"Server: A mismatched client tried to connect from "
1807 <<addr_s<<std::endl;
1808 infostream<<"Server: Cannot negotiate "
1809 "serialization version with peer "
1810 <<peer_id<<std::endl;
1811 SendAccessDenied(m_con, peer_id, std::wstring(
1812 L"Your client's version is not supported.\n"
1813 L"Server version is ")
1814 + narrow_to_wide(VERSION_STRING) + L"."
1820 Read and check network protocol version
1823 u16 min_net_proto_version = 0;
1824 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1825 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1827 // Use same version as minimum and maximum if maximum version field
1828 // doesn't exist (backwards compatibility)
1829 u16 max_net_proto_version = min_net_proto_version;
1830 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1831 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1833 // Start with client's maximum version
1834 u16 net_proto_version = max_net_proto_version;
1836 // Figure out a working version if it is possible at all
1837 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1838 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1840 // If maximum is larger than our maximum, go with our maximum
1841 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1842 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1843 // Else go with client's maximum
1845 net_proto_version = max_net_proto_version;
1848 verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
1849 <<min_net_proto_version<<", max: "<<max_net_proto_version
1850 <<", chosen: "<<net_proto_version<<std::endl;
1852 getClient(peer_id)->net_proto_version = net_proto_version;
1854 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1855 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1857 actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
1859 SendAccessDenied(m_con, peer_id, std::wstring(
1860 L"Your client's version is not supported.\n"
1861 L"Server version is ")
1862 + narrow_to_wide(VERSION_STRING) + L",\n"
1863 + L"server's PROTOCOL_VERSION is "
1864 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1866 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1867 + L", client's PROTOCOL_VERSION is "
1868 + narrow_to_wide(itos(min_net_proto_version))
1870 + narrow_to_wide(itos(max_net_proto_version))
1875 if(g_settings->getBool("strict_protocol_version_checking"))
1877 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1879 actionstream<<"Server: A mismatched (strict) client tried to "
1880 <<"connect from "<<addr_s<<std::endl;
1881 SendAccessDenied(m_con, peer_id, std::wstring(
1882 L"Your client's version is not supported.\n"
1883 L"Server version is ")
1884 + narrow_to_wide(VERSION_STRING) + L",\n"
1885 + L"server's PROTOCOL_VERSION (strict) is "
1886 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1887 + L", client's PROTOCOL_VERSION is "
1888 + narrow_to_wide(itos(min_net_proto_version))
1890 + narrow_to_wide(itos(max_net_proto_version))
1901 char playername[PLAYERNAME_SIZE];
1902 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1904 playername[i] = data[3+i];
1906 playername[PLAYERNAME_SIZE-1] = 0;
1908 if(playername[0]=='\0')
1910 actionstream<<"Server: Player with an empty name "
1911 <<"tried to connect from "<<addr_s<<std::endl;
1912 SendAccessDenied(m_con, peer_id,
1917 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1919 actionstream<<"Server: Player with an invalid name "
1920 <<"tried to connect from "<<addr_s<<std::endl;
1921 SendAccessDenied(m_con, peer_id,
1922 L"Name contains unallowed characters");
1926 infostream<<"Server: New connection: \""<<playername<<"\" from "
1927 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1930 char given_password[PASSWORD_SIZE];
1931 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1933 // old version - assume blank password
1934 given_password[0] = 0;
1938 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1940 given_password[i] = data[23+i];
1942 given_password[PASSWORD_SIZE-1] = 0;
1945 if(!base64_is_valid(given_password)){
1946 infostream<<"Server: "<<playername
1947 <<" supplied invalid password hash"<<std::endl;
1948 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1952 std::string checkpwd; // Password hash to check against
1953 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
1955 // If no authentication info exists for user, create it
1957 if(!isSingleplayer() &&
1958 g_settings->getBool("disallow_empty_password") &&
1959 std::string(given_password) == ""){
1960 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1961 L"disallowed. Set a password and try again.");
1964 std::wstring raw_default_password =
1965 narrow_to_wide(g_settings->get("default_password"));
1966 std::string initial_password =
1967 translatePassword(playername, raw_default_password);
1969 // If default_password is empty, allow any initial password
1970 if (raw_default_password.length() == 0)
1971 initial_password = given_password;
1973 scriptapi_create_auth(m_lua, playername, initial_password);
1976 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
1979 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1983 if(given_password != checkpwd){
1984 infostream<<"Server: peer_id="<<peer_id
1985 <<": supplied invalid password for "
1986 <<playername<<std::endl;
1987 SendAccessDenied(m_con, peer_id, L"Invalid password");
1991 // Do not allow multiple players in simple singleplayer mode.
1992 // This isn't a perfect way to do it, but will suffice for now.
1993 if(m_simple_singleplayer_mode && m_clients.size() > 1){
1994 infostream<<"Server: Not allowing another client to connect in"
1995 <<" simple singleplayer mode"<<std::endl;
1996 SendAccessDenied(m_con, peer_id,
1997 L"Running in simple singleplayer mode.");
2001 // Enforce user limit.
2002 // Don't enforce for users that have some admin right
2003 if(m_clients.size() >= g_settings->getU16("max_users") &&
2004 !checkPriv(playername, "server") &&
2005 !checkPriv(playername, "ban") &&
2006 !checkPriv(playername, "privs") &&
2007 !checkPriv(playername, "password") &&
2008 playername != g_settings->get("name"))
2010 actionstream<<"Server: "<<playername<<" tried to join, but there"
2011 <<" are already max_users="
2012 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2013 SendAccessDenied(m_con, peer_id, L"Too many users.");
2018 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2020 // If failed, cancel
2021 if(playersao == NULL)
2023 errorstream<<"Server: peer_id="<<peer_id
2024 <<": failed to emerge player"<<std::endl;
2029 Answer with a TOCLIENT_INIT
2032 SharedBuffer<u8> reply(2+1+6+8+4);
2033 writeU16(&reply[0], TOCLIENT_INIT);
2034 writeU8(&reply[2], deployed);
2035 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2036 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2037 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2040 m_con.Send(peer_id, 0, reply, true);
2044 Send complete position information
2046 SendMovePlayer(peer_id);
2051 if(command == TOSERVER_INIT2)
2053 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2054 <<peer_id<<std::endl;
2056 Player *player = m_env->getPlayer(peer_id);
2058 verbosestream<<"Server: TOSERVER_INIT2: "
2059 <<"Player not found; ignoring."<<std::endl;
2063 RemoteClient *client = getClient(peer_id);
2064 client->serialization_version =
2065 getClient(peer_id)->pending_serialization_version;
2068 Send some initialization data
2071 infostream<<"Server: Sending content to "
2072 <<getPlayerName(peer_id)<<std::endl;
2074 // Send player movement settings
2075 SendMovement(m_con, peer_id);
2077 // Send item definitions
2078 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2080 // Send node definitions
2081 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2083 // Send media announcement
2084 sendMediaAnnouncement(peer_id);
2087 SendPlayerPrivileges(peer_id);
2089 // Send inventory formspec
2090 SendPlayerInventoryFormspec(peer_id);
2093 UpdateCrafting(peer_id);
2094 SendInventory(peer_id);
2097 if(g_settings->getBool("enable_damage"))
2098 SendPlayerHP(peer_id);
2100 // Send detached inventories
2101 sendDetachedInventories(peer_id);
2103 // Show death screen if necessary
2105 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2109 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2110 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2111 m_con.Send(peer_id, 0, data, true);
2114 // Note things in chat if not in simple singleplayer mode
2115 if(!m_simple_singleplayer_mode)
2117 // Send information about server to player in chat
2118 SendChatMessage(peer_id, getStatusString());
2120 // Send information about joining in chat
2122 std::wstring name = L"unknown";
2123 Player *player = m_env->getPlayer(peer_id);
2125 name = narrow_to_wide(player->getName());
2127 std::wstring message;
2130 message += L" joined the game.";
2131 BroadcastChatMessage(message);
2135 // Warnings about protocol version can be issued here
2136 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2138 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2139 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2146 std::ostringstream os(std::ios_base::binary);
2147 for(std::map<u16, RemoteClient*>::iterator
2148 i = m_clients.begin();
2149 i != m_clients.end(); ++i)
2151 RemoteClient *client = i->second;
2152 assert(client->peer_id == i->first);
2153 if(client->serialization_version == SER_FMT_VER_INVALID)
2156 Player *player = m_env->getPlayer(client->peer_id);
2159 // Get name of player
2160 os<<player->getName()<<" ";
2163 actionstream<<player->getName()<<" joins game. List of players: "
2164 <<os.str()<<std::endl;
2170 if(peer_ser_ver == SER_FMT_VER_INVALID)
2172 infostream<<"Server::ProcessData(): Cancelling: Peer"
2173 " serialization format invalid or not initialized."
2174 " Skipping incoming command="<<command<<std::endl;
2178 Player *player = m_env->getPlayer(peer_id);
2180 infostream<<"Server::ProcessData(): Cancelling: "
2181 "No player for peer_id="<<peer_id
2186 PlayerSAO *playersao = player->getPlayerSAO();
2187 if(playersao == NULL){
2188 infostream<<"Server::ProcessData(): Cancelling: "
2189 "No player object for peer_id="<<peer_id
2194 if(command == TOSERVER_PLAYERPOS)
2196 if(datasize < 2+12+12+4+4)
2200 v3s32 ps = readV3S32(&data[start+2]);
2201 v3s32 ss = readV3S32(&data[start+2+12]);
2202 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2203 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2205 if(datasize >= 2+12+12+4+4+4)
2206 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2207 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2208 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2209 pitch = wrapDegrees(pitch);
2210 yaw = wrapDegrees(yaw);
2212 player->setPosition(position);
2213 player->setSpeed(speed);
2214 player->setPitch(pitch);
2215 player->setYaw(yaw);
2216 player->keyPressed=keyPressed;
2217 player->control.up = (bool)(keyPressed&1);
2218 player->control.down = (bool)(keyPressed&2);
2219 player->control.left = (bool)(keyPressed&4);
2220 player->control.right = (bool)(keyPressed&8);
2221 player->control.jump = (bool)(keyPressed&16);
2222 player->control.aux1 = (bool)(keyPressed&32);
2223 player->control.sneak = (bool)(keyPressed&64);
2224 player->control.LMB = (bool)(keyPressed&128);
2225 player->control.RMB = (bool)(keyPressed&256);
2227 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2228 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2229 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2231 else if(command == TOSERVER_GOTBLOCKS)
2244 u16 count = data[2];
2245 for(u16 i=0; i<count; i++)
2247 if((s16)datasize < 2+1+(i+1)*6)
2248 throw con::InvalidIncomingDataException
2249 ("GOTBLOCKS length is too short");
2250 v3s16 p = readV3S16(&data[2+1+i*6]);
2251 /*infostream<<"Server: GOTBLOCKS ("
2252 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2253 RemoteClient *client = getClient(peer_id);
2254 client->GotBlock(p);
2257 else if(command == TOSERVER_DELETEDBLOCKS)
2270 u16 count = data[2];
2271 for(u16 i=0; i<count; i++)
2273 if((s16)datasize < 2+1+(i+1)*6)
2274 throw con::InvalidIncomingDataException
2275 ("DELETEDBLOCKS length is too short");
2276 v3s16 p = readV3S16(&data[2+1+i*6]);
2277 /*infostream<<"Server: DELETEDBLOCKS ("
2278 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2279 RemoteClient *client = getClient(peer_id);
2280 client->SetBlockNotSent(p);
2283 else if(command == TOSERVER_CLICK_OBJECT)
2285 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2288 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2290 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2293 else if(command == TOSERVER_GROUND_ACTION)
2295 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2299 else if(command == TOSERVER_RELEASE)
2301 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2304 else if(command == TOSERVER_SIGNTEXT)
2306 infostream<<"Server: SIGNTEXT not supported anymore"
2310 else if(command == TOSERVER_SIGNNODETEXT)
2312 infostream<<"Server: SIGNNODETEXT not supported anymore"
2316 else if(command == TOSERVER_INVENTORY_ACTION)
2318 // Strip command and create a stream
2319 std::string datastring((char*)&data[2], datasize-2);
2320 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2321 std::istringstream is(datastring, std::ios_base::binary);
2323 InventoryAction *a = InventoryAction::deSerialize(is);
2326 infostream<<"TOSERVER_INVENTORY_ACTION: "
2327 <<"InventoryAction::deSerialize() returned NULL"
2332 // If something goes wrong, this player is to blame
2333 RollbackScopeActor rollback_scope(m_rollback,
2334 std::string("player:")+player->getName());
2337 Note: Always set inventory not sent, to repair cases
2338 where the client made a bad prediction.
2342 Handle restrictions and special cases of the move action
2344 if(a->getType() == IACTION_MOVE)
2346 IMoveAction *ma = (IMoveAction*)a;
2348 ma->from_inv.applyCurrentPlayer(player->getName());
2349 ma->to_inv.applyCurrentPlayer(player->getName());
2351 setInventoryModified(ma->from_inv);
2352 setInventoryModified(ma->to_inv);
2354 bool from_inv_is_current_player =
2355 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2356 (ma->from_inv.name == player->getName());
2358 bool to_inv_is_current_player =
2359 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2360 (ma->to_inv.name == player->getName());
2363 Disable moving items out of craftpreview
2365 if(ma->from_list == "craftpreview")
2367 infostream<<"Ignoring IMoveAction from "
2368 <<(ma->from_inv.dump())<<":"<<ma->from_list
2369 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2370 <<" because src is "<<ma->from_list<<std::endl;
2376 Disable moving items into craftresult and craftpreview
2378 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2380 infostream<<"Ignoring IMoveAction from "
2381 <<(ma->from_inv.dump())<<":"<<ma->from_list
2382 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2383 <<" because dst is "<<ma->to_list<<std::endl;
2388 // Disallow moving items in elsewhere than player's inventory
2389 // if not allowed to interact
2390 if(!checkPriv(player->getName(), "interact") &&
2391 (!from_inv_is_current_player ||
2392 !to_inv_is_current_player))
2394 infostream<<"Cannot move outside of player's inventory: "
2395 <<"No interact privilege"<<std::endl;
2401 Handle restrictions and special cases of the drop action
2403 else if(a->getType() == IACTION_DROP)
2405 IDropAction *da = (IDropAction*)a;
2407 da->from_inv.applyCurrentPlayer(player->getName());
2409 setInventoryModified(da->from_inv);
2411 // Disallow dropping items if not allowed to interact
2412 if(!checkPriv(player->getName(), "interact"))
2419 Handle restrictions and special cases of the craft action
2421 else if(a->getType() == IACTION_CRAFT)
2423 ICraftAction *ca = (ICraftAction*)a;
2425 ca->craft_inv.applyCurrentPlayer(player->getName());
2427 setInventoryModified(ca->craft_inv);
2429 //bool craft_inv_is_current_player =
2430 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2431 // (ca->craft_inv.name == player->getName());
2433 // Disallow crafting if not allowed to interact
2434 if(!checkPriv(player->getName(), "interact"))
2436 infostream<<"Cannot craft: "
2437 <<"No interact privilege"<<std::endl;
2444 a->apply(this, playersao, this);
2448 else if(command == TOSERVER_CHAT_MESSAGE)
2456 std::string datastring((char*)&data[2], datasize-2);
2457 std::istringstream is(datastring, std::ios_base::binary);
2460 is.read((char*)buf, 2);
2461 u16 len = readU16(buf);
2463 std::wstring message;
2464 for(u16 i=0; i<len; i++)
2466 is.read((char*)buf, 2);
2467 message += (wchar_t)readU16(buf);
2470 // If something goes wrong, this player is to blame
2471 RollbackScopeActor rollback_scope(m_rollback,
2472 std::string("player:")+player->getName());
2474 // Get player name of this client
2475 std::wstring name = narrow_to_wide(player->getName());
2478 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2479 wide_to_narrow(message));
2480 // If script ate the message, don't proceed
2484 // Line to send to players
2486 // Whether to send to the player that sent the line
2487 bool send_to_sender = false;
2488 // Whether to send to other players
2489 bool send_to_others = false;
2491 // Commands are implemented in Lua, so only catch invalid
2492 // commands that were not "eaten" and send an error back
2493 if(message[0] == L'/')
2495 message = message.substr(1);
2496 send_to_sender = true;
2497 if(message.length() == 0)
2498 line += L"-!- Empty command";
2500 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2504 if(checkPriv(player->getName(), "shout")){
2509 send_to_others = true;
2511 line += L"-!- You don't have permission to shout.";
2512 send_to_sender = true;
2519 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2522 Send the message to clients
2524 for(std::map<u16, RemoteClient*>::iterator
2525 i = m_clients.begin();
2526 i != m_clients.end(); ++i)
2528 // Get client and check that it is valid
2529 RemoteClient *client = i->second;
2530 assert(client->peer_id == i->first);
2531 if(client->serialization_version == SER_FMT_VER_INVALID)
2535 bool sender_selected = (peer_id == client->peer_id);
2536 if(sender_selected == true && send_to_sender == false)
2538 if(sender_selected == false && send_to_others == false)
2541 SendChatMessage(client->peer_id, line);
2545 else if(command == TOSERVER_DAMAGE)
2547 std::string datastring((char*)&data[2], datasize-2);
2548 std::istringstream is(datastring, std::ios_base::binary);
2549 u8 damage = readU8(is);
2551 if(g_settings->getBool("enable_damage"))
2553 actionstream<<player->getName()<<" damaged by "
2554 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2557 playersao->setHP(playersao->getHP() - damage);
2559 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2562 if(playersao->m_hp_not_sent)
2563 SendPlayerHP(peer_id);
2566 else if(command == TOSERVER_PASSWORD)
2569 [0] u16 TOSERVER_PASSWORD
2570 [2] u8[28] old password
2571 [30] u8[28] new password
2574 if(datasize != 2+PASSWORD_SIZE*2)
2576 /*char password[PASSWORD_SIZE];
2577 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2578 password[i] = data[2+i];
2579 password[PASSWORD_SIZE-1] = 0;*/
2581 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2589 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2591 char c = data[2+PASSWORD_SIZE+i];
2597 if(!base64_is_valid(newpwd)){
2598 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2599 // Wrong old password supplied!!
2600 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2604 infostream<<"Server: Client requests a password change from "
2605 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2607 std::string playername = player->getName();
2609 std::string checkpwd;
2610 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2612 if(oldpwd != checkpwd)
2614 infostream<<"Server: invalid old password"<<std::endl;
2615 // Wrong old password supplied!!
2616 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2620 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2622 actionstream<<player->getName()<<" changes password"<<std::endl;
2623 SendChatMessage(peer_id, L"Password change successful.");
2625 actionstream<<player->getName()<<" tries to change password but "
2626 <<"it fails"<<std::endl;
2627 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2630 else if(command == TOSERVER_PLAYERITEM)
2635 u16 item = readU16(&data[2]);
2636 playersao->setWieldIndex(item);
2638 else if(command == TOSERVER_RESPAWN)
2640 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2643 RespawnPlayer(peer_id);
2645 actionstream<<player->getName()<<" respawns at "
2646 <<PP(player->getPosition()/BS)<<std::endl;
2648 // ActiveObject is added to environment in AsyncRunStep after
2649 // the previous addition has been succesfully removed
2651 else if(command == TOSERVER_REQUEST_MEDIA) {
2652 std::string datastring((char*)&data[2], datasize-2);
2653 std::istringstream is(datastring, std::ios_base::binary);
2655 std::list<MediaRequest> tosend;
2656 u16 numfiles = readU16(is);
2658 infostream<<"Sending "<<numfiles<<" files to "
2659 <<getPlayerName(peer_id)<<std::endl;
2660 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2662 for(int i = 0; i < numfiles; i++) {
2663 std::string name = deSerializeString(is);
2664 tosend.push_back(MediaRequest(name));
2665 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2669 sendRequestedMedia(peer_id, tosend);
2671 // Now the client should know about everything
2672 // (definitions and files)
2673 getClient(peer_id)->definitions_sent = true;
2675 else if(command == TOSERVER_RECEIVED_MEDIA) {
2676 getClient(peer_id)->definitions_sent = true;
2678 else if(command == TOSERVER_INTERACT)
2680 std::string datastring((char*)&data[2], datasize-2);
2681 std::istringstream is(datastring, std::ios_base::binary);
2687 [5] u32 length of the next item
2688 [9] serialized PointedThing
2690 0: start digging (from undersurface) or use
2691 1: stop digging (all parameters ignored)
2692 2: digging completed
2693 3: place block or item (to abovesurface)
2696 u8 action = readU8(is);
2697 u16 item_i = readU16(is);
2698 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2699 PointedThing pointed;
2700 pointed.deSerialize(tmp_is);
2702 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2703 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2707 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2708 <<" tried to interact, but is dead!"<<std::endl;
2712 v3f player_pos = playersao->getLastGoodPosition();
2714 // Update wielded item
2715 playersao->setWieldIndex(item_i);
2717 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2718 v3s16 p_under = pointed.node_undersurface;
2719 v3s16 p_above = pointed.node_abovesurface;
2721 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2722 ServerActiveObject *pointed_object = NULL;
2723 if(pointed.type == POINTEDTHING_OBJECT)
2725 pointed_object = m_env->getActiveObject(pointed.object_id);
2726 if(pointed_object == NULL)
2728 verbosestream<<"TOSERVER_INTERACT: "
2729 "pointed object is NULL"<<std::endl;
2735 v3f pointed_pos_under = player_pos;
2736 v3f pointed_pos_above = player_pos;
2737 if(pointed.type == POINTEDTHING_NODE)
2739 pointed_pos_under = intToFloat(p_under, BS);
2740 pointed_pos_above = intToFloat(p_above, BS);
2742 else if(pointed.type == POINTEDTHING_OBJECT)
2744 pointed_pos_under = pointed_object->getBasePosition();
2745 pointed_pos_above = pointed_pos_under;
2749 Check that target is reasonably close
2750 (only when digging or placing things)
2752 if(action == 0 || action == 2 || action == 3)
2754 float d = player_pos.getDistanceFrom(pointed_pos_under);
2755 float max_d = BS * 14; // Just some large enough value
2757 actionstream<<"Player "<<player->getName()
2758 <<" tried to access "<<pointed.dump()
2760 <<"d="<<d<<", max_d="<<max_d
2761 <<". ignoring."<<std::endl;
2762 // Re-send block to revert change on client-side
2763 RemoteClient *client = getClient(peer_id);
2764 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2765 client->SetBlockNotSent(blockpos);
2772 Make sure the player is allowed to do it
2774 if(!checkPriv(player->getName(), "interact"))
2776 actionstream<<player->getName()<<" attempted to interact with "
2777 <<pointed.dump()<<" without 'interact' privilege"
2779 // Re-send block to revert change on client-side
2780 RemoteClient *client = getClient(peer_id);
2781 // Digging completed -> under
2783 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2784 client->SetBlockNotSent(blockpos);
2786 // Placement -> above
2788 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2789 client->SetBlockNotSent(blockpos);
2795 If something goes wrong, this player is to blame
2797 RollbackScopeActor rollback_scope(m_rollback,
2798 std::string("player:")+player->getName());
2801 0: start digging or punch object
2805 if(pointed.type == POINTEDTHING_NODE)
2808 NOTE: This can be used in the future to check if
2809 somebody is cheating, by checking the timing.
2811 MapNode n(CONTENT_IGNORE);
2814 n = m_env->getMap().getNode(p_under);
2816 catch(InvalidPositionException &e)
2818 infostream<<"Server: Not punching: Node not found."
2819 <<" Adding block to emerge queue."
2821 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2823 if(n.getContent() != CONTENT_IGNORE)
2824 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2826 playersao->noCheatDigStart(p_under);
2828 else if(pointed.type == POINTEDTHING_OBJECT)
2830 // Skip if object has been removed
2831 if(pointed_object->m_removed)
2834 actionstream<<player->getName()<<" punches object "
2835 <<pointed.object_id<<": "
2836 <<pointed_object->getDescription()<<std::endl;
2838 ItemStack punchitem = playersao->getWieldedItem();
2839 ToolCapabilities toolcap =
2840 punchitem.getToolCapabilities(m_itemdef);
2841 v3f dir = (pointed_object->getBasePosition() -
2842 (player->getPosition() + player->getEyeOffset())
2844 float time_from_last_punch =
2845 playersao->resetTimeFromLastPunch();
2846 pointed_object->punch(dir, &toolcap, playersao,
2847 time_from_last_punch);
2855 else if(action == 1)
2860 2: Digging completed
2862 else if(action == 2)
2864 // Only digging of nodes
2865 if(pointed.type == POINTEDTHING_NODE)
2867 MapNode n(CONTENT_IGNORE);
2870 n = m_env->getMap().getNode(p_under);
2872 catch(InvalidPositionException &e)
2874 infostream<<"Server: Not finishing digging: Node not found."
2875 <<" Adding block to emerge queue."
2877 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2880 /* Cheat prevention */
2881 bool is_valid_dig = true;
2882 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2884 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2885 float nocheat_t = playersao->getNoCheatDigTime();
2886 playersao->noCheatDigEnd();
2887 // If player didn't start digging this, ignore dig
2888 if(nocheat_p != p_under){
2889 infostream<<"Server: NoCheat: "<<player->getName()
2890 <<" started digging "
2891 <<PP(nocheat_p)<<" and completed digging "
2892 <<PP(p_under)<<"; not digging."<<std::endl;
2893 is_valid_dig = false;
2895 // Get player's wielded item
2896 ItemStack playeritem;
2897 InventoryList *mlist = playersao->getInventory()->getList("main");
2899 playeritem = mlist->getItem(playersao->getWieldIndex());
2900 ToolCapabilities playeritem_toolcap =
2901 playeritem.getToolCapabilities(m_itemdef);
2902 // Get diggability and expected digging time
2903 DigParams params = getDigParams(m_nodedef->get(n).groups,
2904 &playeritem_toolcap);
2905 // If can't dig, try hand
2906 if(!params.diggable){
2907 const ItemDefinition &hand = m_itemdef->get("");
2908 const ToolCapabilities *tp = hand.tool_capabilities;
2910 params = getDigParams(m_nodedef->get(n).groups, tp);
2912 // If can't dig, ignore dig
2913 if(!params.diggable){
2914 infostream<<"Server: NoCheat: "<<player->getName()
2915 <<" completed digging "<<PP(p_under)
2916 <<", which is not diggable with tool. not digging."
2918 is_valid_dig = false;
2920 // If time is considerably too short, ignore dig
2921 // Check time only for medium and slow timed digs
2922 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2923 infostream<<"Server: NoCheat: "<<player->getName()
2924 <<" completed digging "
2925 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2926 <<params.time<<"s; not digging."<<std::endl;
2927 is_valid_dig = false;
2931 /* Actually dig node */
2933 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2934 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
2936 // Send unusual result (that is, node not being removed)
2937 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2939 // Re-send block to revert change on client-side
2940 RemoteClient *client = getClient(peer_id);
2941 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2942 client->SetBlockNotSent(blockpos);
2948 3: place block or right-click object
2950 else if(action == 3)
2952 ItemStack item = playersao->getWieldedItem();
2954 // Reset build time counter
2955 if(pointed.type == POINTEDTHING_NODE &&
2956 item.getDefinition(m_itemdef).type == ITEM_NODE)
2957 getClient(peer_id)->m_time_from_building = 0.0;
2959 if(pointed.type == POINTEDTHING_OBJECT)
2961 // Right click object
2963 // Skip if object has been removed
2964 if(pointed_object->m_removed)
2967 actionstream<<player->getName()<<" right-clicks object "
2968 <<pointed.object_id<<": "
2969 <<pointed_object->getDescription()<<std::endl;
2972 pointed_object->rightClick(playersao);
2974 else if(scriptapi_item_on_place(m_lua,
2975 item, playersao, pointed))
2977 // Placement was handled in lua
2979 // Apply returned ItemStack
2980 playersao->setWieldedItem(item);
2983 // If item has node placement prediction, always send the above
2984 // node to make sure the client knows what exactly happened
2985 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2986 RemoteClient *client = getClient(peer_id);
2987 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2988 client->SetBlockNotSent(blockpos);
2995 else if(action == 4)
2997 ItemStack item = playersao->getWieldedItem();
2999 actionstream<<player->getName()<<" uses "<<item.name
3000 <<", pointing at "<<pointed.dump()<<std::endl;
3002 if(scriptapi_item_on_use(m_lua,
3003 item, playersao, pointed))
3005 // Apply returned ItemStack
3006 playersao->setWieldedItem(item);
3013 Catch invalid actions
3017 infostream<<"WARNING: Server: Invalid action "
3018 <<action<<std::endl;
3021 else if(command == TOSERVER_REMOVED_SOUNDS)
3023 std::string datastring((char*)&data[2], datasize-2);
3024 std::istringstream is(datastring, std::ios_base::binary);
3026 int num = readU16(is);
3027 for(int k=0; k<num; k++){
3028 s32 id = readS32(is);
3029 std::map<s32, ServerPlayingSound>::iterator i =
3030 m_playing_sounds.find(id);
3031 if(i == m_playing_sounds.end())
3033 ServerPlayingSound &psound = i->second;
3034 psound.clients.erase(peer_id);
3035 if(psound.clients.size() == 0)
3036 m_playing_sounds.erase(i++);
3039 else if(command == TOSERVER_NODEMETA_FIELDS)
3041 std::string datastring((char*)&data[2], datasize-2);
3042 std::istringstream is(datastring, std::ios_base::binary);
3044 v3s16 p = readV3S16(is);
3045 std::string formname = deSerializeString(is);
3046 int num = readU16(is);
3047 std::map<std::string, std::string> fields;
3048 for(int k=0; k<num; k++){
3049 std::string fieldname = deSerializeString(is);
3050 std::string fieldvalue = deSerializeLongString(is);
3051 fields[fieldname] = fieldvalue;
3054 // If something goes wrong, this player is to blame
3055 RollbackScopeActor rollback_scope(m_rollback,
3056 std::string("player:")+player->getName());
3058 // Check the target node for rollback data; leave others unnoticed
3059 RollbackNode rn_old(&m_env->getMap(), p, this);
3061 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3064 // Report rollback data
3065 RollbackNode rn_new(&m_env->getMap(), p, this);
3066 if(rollback() && rn_new != rn_old){
3067 RollbackAction action;
3068 action.setSetNode(p, rn_old, rn_new);
3069 rollback()->reportAction(action);
3072 else if(command == TOSERVER_INVENTORY_FIELDS)
3074 std::string datastring((char*)&data[2], datasize-2);
3075 std::istringstream is(datastring, std::ios_base::binary);
3077 std::string formname = deSerializeString(is);
3078 int num = readU16(is);
3079 std::map<std::string, std::string> fields;
3080 for(int k=0; k<num; k++){
3081 std::string fieldname = deSerializeString(is);
3082 std::string fieldvalue = deSerializeLongString(is);
3083 fields[fieldname] = fieldvalue;
3086 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3090 infostream<<"Server::ProcessData(): Ignoring "
3091 "unknown command "<<command<<std::endl;
3095 catch(SendFailedException &e)
3097 errorstream<<"Server::ProcessData(): SendFailedException: "
3103 void Server::onMapEditEvent(MapEditEvent *event)
3105 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3106 if(m_ignore_map_edit_events)
3108 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3110 MapEditEvent *e = event->clone();
3111 m_unsent_map_edit_queue.push_back(e);
3114 Inventory* Server::getInventory(const InventoryLocation &loc)
3117 case InventoryLocation::UNDEFINED:
3120 case InventoryLocation::CURRENT_PLAYER:
3123 case InventoryLocation::PLAYER:
3125 Player *player = m_env->getPlayer(loc.name.c_str());
3128 PlayerSAO *playersao = player->getPlayerSAO();
3131 return playersao->getInventory();
3134 case InventoryLocation::NODEMETA:
3136 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3139 return meta->getInventory();
3142 case InventoryLocation::DETACHED:
3144 if(m_detached_inventories.count(loc.name) == 0)
3146 return m_detached_inventories[loc.name];
3154 void Server::setInventoryModified(const InventoryLocation &loc)
3157 case InventoryLocation::UNDEFINED:
3160 case InventoryLocation::PLAYER:
3162 Player *player = m_env->getPlayer(loc.name.c_str());
3165 PlayerSAO *playersao = player->getPlayerSAO();
3168 playersao->m_inventory_not_sent = true;
3169 playersao->m_wielded_item_not_sent = true;
3172 case InventoryLocation::NODEMETA:
3174 v3s16 blockpos = getNodeBlockPos(loc.p);
3176 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3178 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3180 setBlockNotSent(blockpos);
3183 case InventoryLocation::DETACHED:
3185 sendDetachedInventoryToAll(loc.name);
3193 std::list<PlayerInfo> Server::getPlayerInfo()
3195 DSTACK(__FUNCTION_NAME);
3196 JMutexAutoLock envlock(m_env_mutex);
3197 JMutexAutoLock conlock(m_con_mutex);
3199 std::list<PlayerInfo> list;
3201 std::list<Player*> players = m_env->getPlayers();
3203 std::list<Player*>::iterator i;
3204 for(i = players.begin();
3205 i != players.end(); ++i)
3209 Player *player = *i;
3212 // Copy info from connection to info struct
3213 info.id = player->peer_id;
3214 info.address = m_con.GetPeerAddress(player->peer_id);
3215 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3217 catch(con::PeerNotFoundException &e)
3219 // Set dummy peer info
3221 info.address = Address(0,0,0,0,0);
3225 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3226 info.position = player->getPosition();
3228 list.push_back(info);
3235 void Server::peerAdded(con::Peer *peer)
3237 DSTACK(__FUNCTION_NAME);
3238 verbosestream<<"Server::peerAdded(): peer->id="
3239 <<peer->id<<std::endl;
3242 c.type = PEER_ADDED;
3243 c.peer_id = peer->id;
3245 m_peer_change_queue.push_back(c);
3248 void Server::deletingPeer(con::Peer *peer, bool timeout)
3250 DSTACK(__FUNCTION_NAME);
3251 verbosestream<<"Server::deletingPeer(): peer->id="
3252 <<peer->id<<", timeout="<<timeout<<std::endl;
3255 c.type = PEER_REMOVED;
3256 c.peer_id = peer->id;
3257 c.timeout = timeout;
3258 m_peer_change_queue.push_back(c);
3265 void Server::SendMovement(con::Connection &con, u16 peer_id)
3267 DSTACK(__FUNCTION_NAME);
3268 std::ostringstream os(std::ios_base::binary);
3270 writeU16(os, TOCLIENT_MOVEMENT);
3271 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3272 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3273 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3274 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3275 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3276 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3277 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3278 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3279 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3280 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3281 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3282 writeF1000(os, g_settings->getFloat("movement_gravity"));
3285 std::string s = os.str();
3286 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3288 con.Send(peer_id, 0, data, true);
3291 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3293 DSTACK(__FUNCTION_NAME);
3294 std::ostringstream os(std::ios_base::binary);
3296 writeU16(os, TOCLIENT_HP);
3300 std::string s = os.str();
3301 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3303 con.Send(peer_id, 0, data, true);
3306 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3307 const std::wstring &reason)
3309 DSTACK(__FUNCTION_NAME);
3310 std::ostringstream os(std::ios_base::binary);
3312 writeU16(os, TOCLIENT_ACCESS_DENIED);
3313 os<<serializeWideString(reason);
3316 std::string s = os.str();
3317 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3319 con.Send(peer_id, 0, data, true);
3322 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3323 bool set_camera_point_target, v3f camera_point_target)
3325 DSTACK(__FUNCTION_NAME);
3326 std::ostringstream os(std::ios_base::binary);
3328 writeU16(os, TOCLIENT_DEATHSCREEN);
3329 writeU8(os, set_camera_point_target);
3330 writeV3F1000(os, camera_point_target);
3333 std::string s = os.str();
3334 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3336 con.Send(peer_id, 0, data, true);
3339 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3340 IItemDefManager *itemdef, u16 protocol_version)
3342 DSTACK(__FUNCTION_NAME);
3343 std::ostringstream os(std::ios_base::binary);
3347 u32 length of the next item
3348 zlib-compressed serialized ItemDefManager
3350 writeU16(os, TOCLIENT_ITEMDEF);
3351 std::ostringstream tmp_os(std::ios::binary);
3352 itemdef->serialize(tmp_os, protocol_version);
3353 std::ostringstream tmp_os2(std::ios::binary);
3354 compressZlib(tmp_os.str(), tmp_os2);
3355 os<<serializeLongString(tmp_os2.str());
3358 std::string s = os.str();
3359 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3360 <<"): size="<<s.size()<<std::endl;
3361 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3363 con.Send(peer_id, 0, data, true);
3366 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3367 INodeDefManager *nodedef, u16 protocol_version)
3369 DSTACK(__FUNCTION_NAME);
3370 std::ostringstream os(std::ios_base::binary);
3374 u32 length of the next item
3375 zlib-compressed serialized NodeDefManager
3377 writeU16(os, TOCLIENT_NODEDEF);
3378 std::ostringstream tmp_os(std::ios::binary);
3379 nodedef->serialize(tmp_os, protocol_version);
3380 std::ostringstream tmp_os2(std::ios::binary);
3381 compressZlib(tmp_os.str(), tmp_os2);
3382 os<<serializeLongString(tmp_os2.str());
3385 std::string s = os.str();
3386 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3387 <<"): size="<<s.size()<<std::endl;
3388 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3390 con.Send(peer_id, 0, data, true);
3394 Non-static send methods
3397 void Server::SendInventory(u16 peer_id)
3399 DSTACK(__FUNCTION_NAME);
3401 PlayerSAO *playersao = getPlayerSAO(peer_id);
3404 playersao->m_inventory_not_sent = false;
3410 std::ostringstream os;
3411 playersao->getInventory()->serialize(os);
3413 std::string s = os.str();
3415 SharedBuffer<u8> data(s.size()+2);
3416 writeU16(&data[0], TOCLIENT_INVENTORY);
3417 memcpy(&data[2], s.c_str(), s.size());
3420 m_con.Send(peer_id, 0, data, true);
3423 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3425 DSTACK(__FUNCTION_NAME);
3427 std::ostringstream os(std::ios_base::binary);
3431 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3432 os.write((char*)buf, 2);
3435 writeU16(buf, message.size());
3436 os.write((char*)buf, 2);
3439 for(u32 i=0; i<message.size(); i++)
3443 os.write((char*)buf, 2);
3447 std::string s = os.str();
3448 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3450 m_con.Send(peer_id, 0, data, true);
3452 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, const std::string formname)
3454 DSTACK(__FUNCTION_NAME);
3456 std::ostringstream os(std::ios_base::binary);
3460 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3461 os.write((char*)buf, 2);
3462 os<<serializeLongString(formspec);
3463 os<<serializeString(formname);
3466 std::string s = os.str();
3467 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3469 m_con.Send(peer_id, 0, data, true);
3472 // Spawns a particle on peer with peer_id
3473 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture)
3475 DSTACK(__FUNCTION_NAME);
3477 std::ostringstream os(std::ios_base::binary);
3478 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3479 writeV3F1000(os, pos);
3480 writeV3F1000(os, velocity);
3481 writeV3F1000(os, acceleration);
3482 writeF1000(os, expirationtime);
3483 writeF1000(os, size);
3484 writeU8(os, collisiondetection);
3485 os<<serializeLongString(texture);
3488 std::string s = os.str();
3489 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3491 m_con.Send(peer_id, 0, data, true);
3494 // Spawns a particle on all peers
3495 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture)
3497 for(std::map<u16, RemoteClient*>::iterator
3498 i = m_clients.begin();
3499 i != m_clients.end(); i++)
3501 // Get client and check that it is valid
3502 RemoteClient *client = i->second;
3503 assert(client->peer_id == i->first);
3504 if(client->serialization_version == SER_FMT_VER_INVALID)
3507 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3508 expirationtime, size, collisiondetection, texture);
3512 // Adds a ParticleSpawner on peer with peer_id
3513 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3514 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3515 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3517 DSTACK(__FUNCTION_NAME);
3519 std::ostringstream os(std::ios_base::binary);
3520 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3522 writeU16(os, amount);
3523 writeF1000(os, spawntime);
3524 writeV3F1000(os, minpos);
3525 writeV3F1000(os, maxpos);
3526 writeV3F1000(os, minvel);
3527 writeV3F1000(os, maxvel);
3528 writeV3F1000(os, minacc);
3529 writeV3F1000(os, maxacc);
3530 writeF1000(os, minexptime);
3531 writeF1000(os, maxexptime);
3532 writeF1000(os, minsize);
3533 writeF1000(os, maxsize);
3534 writeU8(os, collisiondetection);
3535 os<<serializeLongString(texture);
3539 std::string s = os.str();
3540 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3542 m_con.Send(peer_id, 0, data, true);
3545 // Adds a ParticleSpawner on all peers
3546 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3547 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3548 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3550 for(std::map<u16, RemoteClient*>::iterator
3551 i = m_clients.begin();
3552 i != m_clients.end(); i++)
3554 // Get client and check that it is valid
3555 RemoteClient *client = i->second;
3556 assert(client->peer_id == i->first);
3557 if(client->serialization_version == SER_FMT_VER_INVALID)
3560 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3561 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3562 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3566 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3568 DSTACK(__FUNCTION_NAME);
3570 std::ostringstream os(std::ios_base::binary);
3571 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3576 std::string s = os.str();
3577 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3579 m_con.Send(peer_id, 0, data, true);
3582 void Server::SendDeleteParticleSpawnerAll(u32 id)
3584 for(std::map<u16, RemoteClient*>::iterator
3585 i = m_clients.begin();
3586 i != m_clients.end(); i++)
3588 // Get client and check that it is valid
3589 RemoteClient *client = i->second;
3590 assert(client->peer_id == i->first);
3591 if(client->serialization_version == SER_FMT_VER_INVALID)
3594 SendDeleteParticleSpawner(client->peer_id, id);
3598 void Server::SendHUDAdd(u16 peer_id, const u32 id, HudElement* form)
3600 DSTACK(__FUNCTION_NAME);
3602 std::ostringstream os(std::ios_base::binary);
3606 writeU16(buf, TOCLIENT_HUDADD);
3607 os.write((char*)buf, 2);
3609 writeU8(os, form->type);
3610 writeV2F1000(os, form->pos);
3611 os<<serializeString(form->name);
3612 writeV2F1000(os, form->scale);
3613 os<<serializeString(form->text);
3614 writeU32(os, form->number);
3615 writeU32(os, form->item);
3616 writeU32(os, form->dir);
3619 std::string s = os.str();
3620 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3622 m_con.Send(peer_id, 0, data, true);
3625 void Server::SendHUDRm(u16 peer_id, const u32 id)
3627 DSTACK(__FUNCTION_NAME);
3629 std::ostringstream os(std::ios_base::binary);
3633 writeU16(buf, TOCLIENT_HUDRM);
3634 os.write((char*)buf, 2);
3638 std::string s = os.str();
3639 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3641 m_con.Send(peer_id, 0, data, true);
3644 void Server::SendHUDChange(u16 peer_id, const u32 id, const u8 stat, v2f data)
3646 DSTACK(__FUNCTION_NAME);
3648 std::ostringstream os(std::ios_base::binary);
3652 writeU16(buf, TOCLIENT_HUDCHANGE);
3653 os.write((char*)buf, 2);
3656 writeV2F1000(os, data);
3659 std::string s = os.str();
3660 SharedBuffer<u8> ddata((u8*)s.c_str(), s.size());
3662 m_con.Send(peer_id, 0, ddata, true);
3665 void Server::SendHUDChange(u16 peer_id, const u32 id, const u8 stat, std::string data)
3667 DSTACK(__FUNCTION_NAME);
3669 std::ostringstream os(std::ios_base::binary);
3673 writeU16(buf, TOCLIENT_HUDCHANGE);
3674 os.write((char*)buf, 2);
3677 os<<serializeString(data);
3680 std::string s = os.str();
3681 SharedBuffer<u8> ddata((u8*)s.c_str(), s.size());
3683 m_con.Send(peer_id, 0, ddata, true);
3686 void Server::SendHUDChange(u16 peer_id, const u32 id, const u8 stat, u32 data)
3688 DSTACK(__FUNCTION_NAME);
3690 std::ostringstream os(std::ios_base::binary);
3694 writeU16(buf, TOCLIENT_HUDCHANGE);
3695 os.write((char*)buf, 2);
3701 std::string s = os.str();
3702 SharedBuffer<u8> ddata((u8*)s.c_str(), s.size());
3704 m_con.Send(peer_id, 0, ddata, true);
3707 void Server::BroadcastChatMessage(const std::wstring &message)
3709 for(std::map<u16, RemoteClient*>::iterator
3710 i = m_clients.begin();
3711 i != m_clients.end(); ++i)
3713 // Get client and check that it is valid
3714 RemoteClient *client = i->second;
3715 assert(client->peer_id == i->first);
3716 if(client->serialization_version == SER_FMT_VER_INVALID)
3719 SendChatMessage(client->peer_id, message);
3723 void Server::SendPlayerHP(u16 peer_id)
3725 DSTACK(__FUNCTION_NAME);
3726 PlayerSAO *playersao = getPlayerSAO(peer_id);
3728 playersao->m_hp_not_sent = false;
3729 SendHP(m_con, peer_id, playersao->getHP());
3732 void Server::SendMovePlayer(u16 peer_id)
3734 DSTACK(__FUNCTION_NAME);
3735 Player *player = m_env->getPlayer(peer_id);
3738 std::ostringstream os(std::ios_base::binary);
3739 writeU16(os, TOCLIENT_MOVE_PLAYER);
3740 writeV3F1000(os, player->getPosition());
3741 writeF1000(os, player->getPitch());
3742 writeF1000(os, player->getYaw());
3745 v3f pos = player->getPosition();
3746 f32 pitch = player->getPitch();
3747 f32 yaw = player->getYaw();
3748 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3749 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3756 std::string s = os.str();
3757 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3759 m_con.Send(peer_id, 0, data, true);
3762 void Server::SendPlayerPrivileges(u16 peer_id)
3764 Player *player = m_env->getPlayer(peer_id);
3766 if(player->peer_id == PEER_ID_INEXISTENT)
3769 std::set<std::string> privs;
3770 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3772 std::ostringstream os(std::ios_base::binary);
3773 writeU16(os, TOCLIENT_PRIVILEGES);
3774 writeU16(os, privs.size());
3775 for(std::set<std::string>::const_iterator i = privs.begin();
3776 i != privs.end(); i++){
3777 os<<serializeString(*i);
3781 std::string s = os.str();
3782 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3784 m_con.Send(peer_id, 0, data, true);
3787 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3789 Player *player = m_env->getPlayer(peer_id);
3791 if(player->peer_id == PEER_ID_INEXISTENT)
3794 std::ostringstream os(std::ios_base::binary);
3795 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3796 os<<serializeLongString(player->inventory_formspec);
3799 std::string s = os.str();
3800 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3802 m_con.Send(peer_id, 0, data, true);
3805 s32 Server::playSound(const SimpleSoundSpec &spec,
3806 const ServerSoundParams ¶ms)
3808 // Find out initial position of sound
3809 bool pos_exists = false;
3810 v3f pos = params.getPos(m_env, &pos_exists);
3811 // If position is not found while it should be, cancel sound
3812 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3814 // Filter destination clients
3815 std::set<RemoteClient*> dst_clients;
3816 if(params.to_player != "")
3818 Player *player = m_env->getPlayer(params.to_player.c_str());
3820 infostream<<"Server::playSound: Player \""<<params.to_player
3821 <<"\" not found"<<std::endl;
3824 if(player->peer_id == PEER_ID_INEXISTENT){
3825 infostream<<"Server::playSound: Player \""<<params.to_player
3826 <<"\" not connected"<<std::endl;
3829 RemoteClient *client = getClient(player->peer_id);
3830 dst_clients.insert(client);
3834 for(std::map<u16, RemoteClient*>::iterator
3835 i = m_clients.begin(); i != m_clients.end(); ++i)
3837 RemoteClient *client = i->second;
3838 Player *player = m_env->getPlayer(client->peer_id);
3842 if(player->getPosition().getDistanceFrom(pos) >
3843 params.max_hear_distance)
3846 dst_clients.insert(client);
3849 if(dst_clients.size() == 0)
3852 s32 id = m_next_sound_id++;
3853 // The sound will exist as a reference in m_playing_sounds
3854 m_playing_sounds[id] = ServerPlayingSound();
3855 ServerPlayingSound &psound = m_playing_sounds[id];
3856 psound.params = params;
3857 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3858 i != dst_clients.end(); i++)
3859 psound.clients.insert((*i)->peer_id);
3861 std::ostringstream os(std::ios_base::binary);
3862 writeU16(os, TOCLIENT_PLAY_SOUND);
3864 os<<serializeString(spec.name);
3865 writeF1000(os, spec.gain * params.gain);
3866 writeU8(os, params.type);
3867 writeV3F1000(os, pos);
3868 writeU16(os, params.object);
3869 writeU8(os, params.loop);
3871 std::string s = os.str();
3872 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3874 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3875 i != dst_clients.end(); i++){
3877 m_con.Send((*i)->peer_id, 0, data, true);
3881 void Server::stopSound(s32 handle)
3883 // Get sound reference
3884 std::map<s32, ServerPlayingSound>::iterator i =
3885 m_playing_sounds.find(handle);
3886 if(i == m_playing_sounds.end())
3888 ServerPlayingSound &psound = i->second;
3890 std::ostringstream os(std::ios_base::binary);
3891 writeU16(os, TOCLIENT_STOP_SOUND);
3892 writeS32(os, handle);
3894 std::string s = os.str();
3895 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3897 for(std::set<u16>::iterator i = psound.clients.begin();
3898 i != psound.clients.end(); i++){
3900 m_con.Send(*i, 0, data, true);
3902 // Remove sound reference
3903 m_playing_sounds.erase(i);
3906 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3907 std::list<u16> *far_players, float far_d_nodes)
3909 float maxd = far_d_nodes*BS;
3910 v3f p_f = intToFloat(p, BS);
3914 SharedBuffer<u8> reply(replysize);
3915 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3916 writeS16(&reply[2], p.X);
3917 writeS16(&reply[4], p.Y);
3918 writeS16(&reply[6], p.Z);
3920 for(std::map<u16, RemoteClient*>::iterator
3921 i = m_clients.begin();
3922 i != m_clients.end(); ++i)
3924 // Get client and check that it is valid
3925 RemoteClient *client = i->second;
3926 assert(client->peer_id == i->first);
3927 if(client->serialization_version == SER_FMT_VER_INVALID)
3930 // Don't send if it's the same one
3931 if(client->peer_id == ignore_id)
3937 Player *player = m_env->getPlayer(client->peer_id);
3940 // If player is far away, only set modified blocks not sent
3941 v3f player_pos = player->getPosition();
3942 if(player_pos.getDistanceFrom(p_f) > maxd)
3944 far_players->push_back(client->peer_id);
3951 m_con.Send(client->peer_id, 0, reply, true);
3955 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3956 std::list<u16> *far_players, float far_d_nodes)
3958 float maxd = far_d_nodes*BS;
3959 v3f p_f = intToFloat(p, BS);
3961 for(std::map<u16, RemoteClient*>::iterator
3962 i = m_clients.begin();
3963 i != m_clients.end(); ++i)
3965 // Get client and check that it is valid
3966 RemoteClient *client = i->second;
3967 assert(client->peer_id == i->first);
3968 if(client->serialization_version == SER_FMT_VER_INVALID)
3971 // Don't send if it's the same one
3972 if(client->peer_id == ignore_id)
3978 Player *player = m_env->getPlayer(client->peer_id);
3981 // If player is far away, only set modified blocks not sent
3982 v3f player_pos = player->getPosition();
3983 if(player_pos.getDistanceFrom(p_f) > maxd)
3985 far_players->push_back(client->peer_id);
3992 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3993 SharedBuffer<u8> reply(replysize);
3994 writeU16(&reply[0], TOCLIENT_ADDNODE);
3995 writeS16(&reply[2], p.X);
3996 writeS16(&reply[4], p.Y);
3997 writeS16(&reply[6], p.Z);
3998 n.serialize(&reply[8], client->serialization_version);
4001 m_con.Send(client->peer_id, 0, reply, true);
4005 void Server::setBlockNotSent(v3s16 p)
4007 for(std::map<u16, RemoteClient*>::iterator
4008 i = m_clients.begin();
4009 i != m_clients.end(); ++i)
4011 RemoteClient *client = i->second;
4012 client->SetBlockNotSent(p);
4016 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4018 DSTACK(__FUNCTION_NAME);
4020 v3s16 p = block->getPos();
4024 bool completely_air = true;
4025 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4026 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4027 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4029 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4031 completely_air = false;
4032 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4037 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4039 infostream<<"[completely air] ";
4040 infostream<<std::endl;
4044 Create a packet with the block in the right format
4047 std::ostringstream os(std::ios_base::binary);
4048 block->serialize(os, ver, false);
4049 std::string s = os.str();
4050 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4052 u32 replysize = 8 + blockdata.getSize();
4053 SharedBuffer<u8> reply(replysize);
4054 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4055 writeS16(&reply[2], p.X);
4056 writeS16(&reply[4], p.Y);
4057 writeS16(&reply[6], p.Z);
4058 memcpy(&reply[8], *blockdata, blockdata.getSize());
4060 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4061 <<": \tpacket size: "<<replysize<<std::endl;*/
4066 m_con.Send(peer_id, 1, reply, true);
4069 void Server::SendBlocks(float dtime)
4071 DSTACK(__FUNCTION_NAME);
4073 JMutexAutoLock envlock(m_env_mutex);
4074 JMutexAutoLock conlock(m_con_mutex);
4076 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4078 std::vector<PrioritySortedBlockTransfer> queue;
4080 s32 total_sending = 0;
4083 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4085 for(std::map<u16, RemoteClient*>::iterator
4086 i = m_clients.begin();
4087 i != m_clients.end(); ++i)
4089 RemoteClient *client = i->second;
4090 assert(client->peer_id == i->first);
4092 // If definitions and textures have not been sent, don't
4093 // send MapBlocks either
4094 if(!client->definitions_sent)
4097 total_sending += client->SendingCount();
4099 if(client->serialization_version == SER_FMT_VER_INVALID)
4102 client->GetNextBlocks(this, dtime, queue);
4107 // Lowest priority number comes first.
4108 // Lowest is most important.
4109 std::sort(queue.begin(), queue.end());
4111 for(u32 i=0; i<queue.size(); i++)
4113 //TODO: Calculate limit dynamically
4114 if(total_sending >= g_settings->getS32
4115 ("max_simultaneous_block_sends_server_total"))
4118 PrioritySortedBlockTransfer q = queue[i];
4120 MapBlock *block = NULL;
4123 block = m_env->getMap().getBlockNoCreate(q.pos);
4125 catch(InvalidPositionException &e)
4130 RemoteClient *client = getClient(q.peer_id);
4132 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4134 client->SentBlock(q.pos);
4140 void Server::fillMediaCache()
4142 DSTACK(__FUNCTION_NAME);
4144 infostream<<"Server: Calculating media file checksums"<<std::endl;
4146 // Collect all media file paths
4147 std::list<std::string> paths;
4148 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4149 i != m_mods.end(); i++){
4150 const ModSpec &mod = *i;
4151 paths.push_back(mod.path + DIR_DELIM + "textures");
4152 paths.push_back(mod.path + DIR_DELIM + "sounds");
4153 paths.push_back(mod.path + DIR_DELIM + "media");
4154 paths.push_back(mod.path + DIR_DELIM + "models");
4156 std::string path_all = "textures";
4157 paths.push_back(path_all + DIR_DELIM + "all");
4159 // Collect media file information from paths into cache
4160 for(std::list<std::string>::iterator i = paths.begin();
4161 i != paths.end(); i++)
4163 std::string mediapath = *i;
4164 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4165 for(u32 j=0; j<dirlist.size(); j++){
4166 if(dirlist[j].dir) // Ignode dirs
4168 std::string filename = dirlist[j].name;
4169 // If name contains illegal characters, ignore the file
4170 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4171 infostream<<"Server: ignoring illegal file name: \""
4172 <<filename<<"\""<<std::endl;
4175 // If name is not in a supported format, ignore it
4176 const char *supported_ext[] = {
4177 ".png", ".jpg", ".bmp", ".tga",
4178 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4180 ".x", ".b3d", ".md2", ".obj",
4183 if(removeStringEnd(filename, supported_ext) == ""){
4184 infostream<<"Server: ignoring unsupported file extension: \""
4185 <<filename<<"\""<<std::endl;
4188 // Ok, attempt to load the file and add to cache
4189 std::string filepath = mediapath + DIR_DELIM + filename;
4191 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4192 if(fis.good() == false){
4193 errorstream<<"Server::fillMediaCache(): Could not open \""
4194 <<filename<<"\" for reading"<<std::endl;
4197 std::ostringstream tmp_os(std::ios_base::binary);
4201 fis.read(buf, 1024);
4202 std::streamsize len = fis.gcount();
4203 tmp_os.write(buf, len);
4212 errorstream<<"Server::fillMediaCache(): Failed to read \""
4213 <<filename<<"\""<<std::endl;
4216 if(tmp_os.str().length() == 0){
4217 errorstream<<"Server::fillMediaCache(): Empty file \""
4218 <<filepath<<"\""<<std::endl;
4223 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4225 unsigned char *digest = sha1.getDigest();
4226 std::string sha1_base64 = base64_encode(digest, 20);
4227 std::string sha1_hex = hex_encode((char*)digest, 20);
4231 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4232 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4237 struct SendableMediaAnnouncement
4240 std::string sha1_digest;
4242 SendableMediaAnnouncement(const std::string name_="",
4243 const std::string sha1_digest_=""):
4245 sha1_digest(sha1_digest_)
4249 void Server::sendMediaAnnouncement(u16 peer_id)
4251 DSTACK(__FUNCTION_NAME);
4253 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4256 std::list<SendableMediaAnnouncement> file_announcements;
4258 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4259 i != m_media.end(); i++){
4261 file_announcements.push_back(
4262 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4266 std::ostringstream os(std::ios_base::binary);
4274 u16 length of sha1_digest
4279 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4280 writeU16(os, file_announcements.size());
4282 for(std::list<SendableMediaAnnouncement>::iterator
4283 j = file_announcements.begin();
4284 j != file_announcements.end(); ++j){
4285 os<<serializeString(j->name);
4286 os<<serializeString(j->sha1_digest);
4288 os<<serializeString(g_settings->get("remote_media"));
4291 std::string s = os.str();
4292 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4295 m_con.Send(peer_id, 0, data, true);
4298 struct SendableMedia
4304 SendableMedia(const std::string &name_="", const std::string path_="",
4305 const std::string &data_=""):
4312 void Server::sendRequestedMedia(u16 peer_id,
4313 const std::list<MediaRequest> &tosend)
4315 DSTACK(__FUNCTION_NAME);
4317 verbosestream<<"Server::sendRequestedMedia(): "
4318 <<"Sending files to client"<<std::endl;
4322 // Put 5kB in one bunch (this is not accurate)
4323 u32 bytes_per_bunch = 5000;
4325 std::vector< std::list<SendableMedia> > file_bunches;
4326 file_bunches.push_back(std::list<SendableMedia>());
4328 u32 file_size_bunch_total = 0;
4330 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4331 i != tosend.end(); ++i)
4333 if(m_media.find(i->name) == m_media.end()){
4334 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4335 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4339 //TODO get path + name
4340 std::string tpath = m_media[(*i).name].path;
4343 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4344 if(fis.good() == false){
4345 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4346 <<tpath<<"\" for reading"<<std::endl;
4349 std::ostringstream tmp_os(std::ios_base::binary);
4353 fis.read(buf, 1024);
4354 std::streamsize len = fis.gcount();
4355 tmp_os.write(buf, len);
4356 file_size_bunch_total += len;
4365 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4366 <<(*i).name<<"\""<<std::endl;
4369 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4370 <<tname<<"\""<<std::endl;*/
4372 file_bunches[file_bunches.size()-1].push_back(
4373 SendableMedia((*i).name, tpath, tmp_os.str()));
4375 // Start next bunch if got enough data
4376 if(file_size_bunch_total >= bytes_per_bunch){
4377 file_bunches.push_back(std::list<SendableMedia>());
4378 file_size_bunch_total = 0;
4383 /* Create and send packets */
4385 u32 num_bunches = file_bunches.size();
4386 for(u32 i=0; i<num_bunches; i++)
4388 std::ostringstream os(std::ios_base::binary);
4392 u16 total number of texture bunches
4393 u16 index of this bunch
4394 u32 number of files in this bunch
4403 writeU16(os, TOCLIENT_MEDIA);
4404 writeU16(os, num_bunches);
4406 writeU32(os, file_bunches[i].size());
4408 for(std::list<SendableMedia>::iterator
4409 j = file_bunches[i].begin();
4410 j != file_bunches[i].end(); ++j){
4411 os<<serializeString(j->name);
4412 os<<serializeLongString(j->data);
4416 std::string s = os.str();
4417 verbosestream<<"Server::sendRequestedMedia(): bunch "
4418 <<i<<"/"<<num_bunches
4419 <<" files="<<file_bunches[i].size()
4420 <<" size=" <<s.size()<<std::endl;
4421 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4423 m_con.Send(peer_id, 0, data, true);
4427 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4429 if(m_detached_inventories.count(name) == 0){
4430 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4433 Inventory *inv = m_detached_inventories[name];
4435 std::ostringstream os(std::ios_base::binary);
4436 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4437 os<<serializeString(name);
4441 std::string s = os.str();
4442 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4444 m_con.Send(peer_id, 0, data, true);
4447 void Server::sendDetachedInventoryToAll(const std::string &name)
4449 DSTACK(__FUNCTION_NAME);
4451 for(std::map<u16, RemoteClient*>::iterator
4452 i = m_clients.begin();
4453 i != m_clients.end(); ++i){
4454 RemoteClient *client = i->second;
4455 sendDetachedInventory(name, client->peer_id);
4459 void Server::sendDetachedInventories(u16 peer_id)
4461 DSTACK(__FUNCTION_NAME);
4463 for(std::map<std::string, Inventory*>::iterator
4464 i = m_detached_inventories.begin();
4465 i != m_detached_inventories.end(); i++){
4466 const std::string &name = i->first;
4467 //Inventory *inv = i->second;
4468 sendDetachedInventory(name, peer_id);
4476 void Server::DiePlayer(u16 peer_id)
4478 DSTACK(__FUNCTION_NAME);
4480 PlayerSAO *playersao = getPlayerSAO(peer_id);
4483 infostream<<"Server::DiePlayer(): Player "
4484 <<playersao->getPlayer()->getName()
4485 <<" dies"<<std::endl;
4487 playersao->setHP(0);
4489 // Trigger scripted stuff
4490 scriptapi_on_dieplayer(m_lua, playersao);
4492 SendPlayerHP(peer_id);
4493 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4496 void Server::RespawnPlayer(u16 peer_id)
4498 DSTACK(__FUNCTION_NAME);
4500 PlayerSAO *playersao = getPlayerSAO(peer_id);
4503 infostream<<"Server::RespawnPlayer(): Player "
4504 <<playersao->getPlayer()->getName()
4505 <<" respawns"<<std::endl;
4507 playersao->setHP(PLAYER_MAX_HP);
4509 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4511 v3f pos = findSpawnPos(m_env->getServerMap());
4512 playersao->setPos(pos);
4516 void Server::UpdateCrafting(u16 peer_id)
4518 DSTACK(__FUNCTION_NAME);
4520 Player* player = m_env->getPlayer(peer_id);
4523 // Get a preview for crafting
4525 getCraftingResult(&player->inventory, preview, false, this);
4527 // Put the new preview in
4528 InventoryList *plist = player->inventory.getList("craftpreview");
4530 assert(plist->getSize() >= 1);
4531 plist->changeItem(0, preview);
4534 RemoteClient* Server::getClient(u16 peer_id)
4536 DSTACK(__FUNCTION_NAME);
4537 //JMutexAutoLock lock(m_con_mutex);
4538 std::map<u16, RemoteClient*>::iterator n;
4539 n = m_clients.find(peer_id);
4540 // A client should exist for all peers
4541 assert(n != m_clients.end());
4545 std::wstring Server::getStatusString()
4547 std::wostringstream os(std::ios_base::binary);
4550 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4552 os<<L", uptime="<<m_uptime.get();
4553 // Information about clients
4554 std::map<u16, RemoteClient*>::iterator i;
4557 for(i = m_clients.begin(), first = true;
4558 i != m_clients.end(); ++i)
4560 // Get client and check that it is valid
4561 RemoteClient *client = i->second;
4562 assert(client->peer_id == i->first);
4563 if(client->serialization_version == SER_FMT_VER_INVALID)
4566 Player *player = m_env->getPlayer(client->peer_id);
4567 // Get name of player
4568 std::wstring name = L"unknown";
4570 name = narrow_to_wide(player->getName());
4571 // Add name to information string
4579 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4580 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4581 if(g_settings->get("motd") != "")
4582 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4586 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4588 std::set<std::string> privs;
4589 scriptapi_get_auth(m_lua, name, NULL, &privs);
4593 bool Server::checkPriv(const std::string &name, const std::string &priv)
4595 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4596 return (privs.count(priv) != 0);
4599 void Server::reportPrivsModified(const std::string &name)
4602 for(std::map<u16, RemoteClient*>::iterator
4603 i = m_clients.begin();
4604 i != m_clients.end(); ++i){
4605 RemoteClient *client = i->second;
4606 Player *player = m_env->getPlayer(client->peer_id);
4607 reportPrivsModified(player->getName());
4610 Player *player = m_env->getPlayer(name.c_str());
4613 SendPlayerPrivileges(player->peer_id);
4614 PlayerSAO *sao = player->getPlayerSAO();
4617 sao->updatePrivileges(
4618 getPlayerEffectivePrivs(name),
4623 void Server::reportInventoryFormspecModified(const std::string &name)
4625 Player *player = m_env->getPlayer(name.c_str());
4628 SendPlayerInventoryFormspec(player->peer_id);
4631 // Saves g_settings to configpath given at initialization
4632 void Server::saveConfig()
4634 if(m_path_config != "")
4635 g_settings->updateConfigFile(m_path_config.c_str());
4638 void Server::notifyPlayer(const char *name, const std::wstring msg)
4640 Player *player = m_env->getPlayer(name);
4643 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4646 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4648 Player *player = m_env->getPlayer(playername);
4652 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4656 SendShowFormspecMessage(player->peer_id, formspec, formname);
4660 bool Server::hudadd(const char *playername, const u32 &id, HudElement* form)
4662 Player *player = m_env->getPlayer(playername);
4666 infostream<<"hudadd: couldn't find player:"<<playername<<std::endl;
4670 SendHUDAdd(player->peer_id, id, form);
4674 bool Server::hudrm(const char *playername, const u32 &id)
4676 Player *player = m_env->getPlayer(playername);
4680 infostream<<"hudrm: couldn't find player:"<<playername<<std::endl;
4684 SendHUDRm(player->peer_id, id);
4688 bool Server::hudchange(const char *playername, const u32 &id, const u8 &stat, v2f data)
4690 Player *player = m_env->getPlayer(playername);
4694 infostream<<"hudchange: couldn't find player:"<<playername<<std::endl;
4698 SendHUDChange(player->peer_id, id, stat, data);
4702 bool Server::hudchange(const char *playername, const u32 &id, const u8 &stat, std::string data)
4704 Player *player = m_env->getPlayer(playername);
4708 infostream<<"hudchange: couldn't find player:"<<playername<<std::endl;
4712 SendHUDChange(player->peer_id, id, stat, data);
4716 bool Server::hudchange(const char *playername, const u32 &id, const u8 &stat, u32 data)
4718 Player *player = m_env->getPlayer(playername);
4722 infostream<<"hudchange: couldn't find player:"<<playername<<std::endl;
4726 SendHUDChange(player->peer_id, id, stat, data);
4730 void Server::notifyPlayers(const std::wstring msg)
4732 BroadcastChatMessage(msg);
4735 void Server::spawnParticle(const char *playername, v3f pos,
4736 v3f velocity, v3f acceleration,
4737 float expirationtime, float size, bool
4738 collisiondetection, std::string texture)
4740 Player *player = m_env->getPlayer(playername);
4743 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4744 expirationtime, size, collisiondetection, texture);
4747 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4748 float expirationtime, float size,
4749 bool collisiondetection, std::string texture)
4751 SendSpawnParticleAll(pos, velocity, acceleration,
4752 expirationtime, size, collisiondetection, texture);
4755 u32 Server::addParticleSpawner(const char *playername,
4756 u16 amount, float spawntime,
4757 v3f minpos, v3f maxpos,
4758 v3f minvel, v3f maxvel,
4759 v3f minacc, v3f maxacc,
4760 float minexptime, float maxexptime,
4761 float minsize, float maxsize,
4762 bool collisiondetection, std::string texture)
4764 Player *player = m_env->getPlayer(playername);
4769 for(;;) // look for unused particlespawner id
4772 if (std::find(m_particlespawner_ids.begin(),
4773 m_particlespawner_ids.end(), id)
4774 == m_particlespawner_ids.end())
4776 m_particlespawner_ids.push_back(id);
4781 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4782 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4783 minexptime, maxexptime, minsize, maxsize,
4784 collisiondetection, texture, id);
4789 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4790 v3f minpos, v3f maxpos,
4791 v3f minvel, v3f maxvel,
4792 v3f minacc, v3f maxacc,
4793 float minexptime, float maxexptime,
4794 float minsize, float maxsize,
4795 bool collisiondetection, std::string texture)
4798 for(;;) // look for unused particlespawner id
4801 if (std::find(m_particlespawner_ids.begin(),
4802 m_particlespawner_ids.end(), id)
4803 == m_particlespawner_ids.end())
4805 m_particlespawner_ids.push_back(id);
4810 SendAddParticleSpawnerAll(amount, spawntime,
4811 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4812 minexptime, maxexptime, minsize, maxsize,
4813 collisiondetection, texture, id);
4818 void Server::deleteParticleSpawner(const char *playername, u32 id)
4820 Player *player = m_env->getPlayer(playername);
4824 m_particlespawner_ids.erase(
4825 std::remove(m_particlespawner_ids.begin(),
4826 m_particlespawner_ids.end(), id),
4827 m_particlespawner_ids.end());
4828 SendDeleteParticleSpawner(player->peer_id, id);
4831 void Server::deleteParticleSpawnerAll(u32 id)
4833 m_particlespawner_ids.erase(
4834 std::remove(m_particlespawner_ids.begin(),
4835 m_particlespawner_ids.end(), id),
4836 m_particlespawner_ids.end());
4837 SendDeleteParticleSpawnerAll(id);
4840 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4842 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4845 Inventory* Server::createDetachedInventory(const std::string &name)
4847 if(m_detached_inventories.count(name) > 0){
4848 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4849 delete m_detached_inventories[name];
4851 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4853 Inventory *inv = new Inventory(m_itemdef);
4855 m_detached_inventories[name] = inv;
4856 sendDetachedInventoryToAll(name);
4863 BoolScopeSet(bool *dst, bool val):
4866 m_orig_state = *m_dst;
4871 *m_dst = m_orig_state;
4878 // actions: time-reversed list
4879 // Return value: success/failure
4880 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4881 std::list<std::string> *log)
4883 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4884 ServerMap *map = (ServerMap*)(&m_env->getMap());
4885 // Disable rollback report sink while reverting
4886 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4888 // Fail if no actions to handle
4889 if(actions.empty()){
4890 log->push_back("Nothing to do.");
4897 for(std::list<RollbackAction>::const_iterator
4898 i = actions.begin();
4899 i != actions.end(); i++)
4901 const RollbackAction &action = *i;
4903 bool success = action.applyRevert(map, this, this);
4906 std::ostringstream os;
4907 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4908 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4910 log->push_back(os.str());
4912 std::ostringstream os;
4913 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4914 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4916 log->push_back(os.str());
4920 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4921 <<" failed"<<std::endl;
4923 // Call it done if less than half failed
4924 return num_failed <= num_tried/2;
4927 // IGameDef interface
4929 IItemDefManager* Server::getItemDefManager()
4933 INodeDefManager* Server::getNodeDefManager()
4937 ICraftDefManager* Server::getCraftDefManager()
4941 ITextureSource* Server::getTextureSource()
4945 IShaderSource* Server::getShaderSource()
4949 u16 Server::allocateUnknownNodeId(const std::string &name)
4951 return m_nodedef->allocateDummy(name);
4953 ISoundManager* Server::getSoundManager()
4955 return &dummySoundManager;
4957 MtEventManager* Server::getEventManager()
4961 IRollbackReportSink* Server::getRollbackReportSink()
4963 if(!m_enable_rollback_recording)
4965 if(!m_rollback_sink_enabled)
4970 IWritableItemDefManager* Server::getWritableItemDefManager()
4974 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4978 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4983 const ModSpec* Server::getModSpec(const std::string &modname)
4985 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4986 i != m_mods.end(); i++){
4987 const ModSpec &mod = *i;
4988 if(mod.name == modname)
4993 void Server::getModNames(std::list<std::string> &modlist)
4995 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4997 modlist.push_back(i->name);
5000 std::string Server::getBuiltinLuaPath()
5002 return porting::path_share + DIR_DELIM + "builtin";
5005 v3f findSpawnPos(ServerMap &map)
5007 //return v3f(50,50,50)*BS;
5012 nodepos = v2s16(0,0);
5017 s16 water_level = map.m_mgparams->water_level;
5019 // Try to find a good place a few times
5020 for(s32 i=0; i<1000; i++)
5023 // We're going to try to throw the player to this position
5024 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
5025 -range + (myrand()%(range*2)));
5026 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
5027 // Get ground height at point (fallbacks to heightmap function)
5028 s16 groundheight = map.findGroundLevel(nodepos2d);
5029 // Don't go underwater
5030 if(groundheight <= water_level)
5032 //infostream<<"-> Underwater"<<std::endl;
5035 // Don't go to high places
5036 if(groundheight > water_level + 6)
5038 //infostream<<"-> Underwater"<<std::endl;
5042 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
5043 bool is_good = false;
5045 for(s32 i=0; i<10; i++){
5046 v3s16 blockpos = getNodeBlockPos(nodepos);
5047 map.emergeBlock(blockpos, true);
5048 MapNode n = map.getNodeNoEx(nodepos);
5049 if(n.getContent() == CONTENT_AIR){
5060 // Found a good place
5061 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5067 return intToFloat(nodepos, BS);
5070 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5072 RemotePlayer *player = NULL;
5073 bool newplayer = false;
5076 Try to get an existing player
5078 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5080 // If player is already connected, cancel
5081 if(player != NULL && player->peer_id != 0)
5083 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5088 If player with the wanted peer_id already exists, cancel.
5090 if(m_env->getPlayer(peer_id) != NULL)
5092 infostream<<"emergePlayer(): Player with wrong name but same"
5093 " peer_id already exists"<<std::endl;
5098 Create a new player if it doesn't exist yet
5103 player = new RemotePlayer(this);
5104 player->updateName(name);
5106 /* Set player position */
5107 infostream<<"Server: Finding spawn place for player \""
5108 <<name<<"\""<<std::endl;
5109 v3f pos = findSpawnPos(m_env->getServerMap());
5110 player->setPosition(pos);
5112 /* Add player to environment */
5113 m_env->addPlayer(player);
5117 Create a new player active object
5119 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5120 getPlayerEffectivePrivs(player->getName()),
5123 /* Add object to environment */
5124 m_env->addActiveObject(playersao);
5128 scriptapi_on_newplayer(m_lua, playersao);
5130 scriptapi_on_joinplayer(m_lua, playersao);
5135 void Server::handlePeerChange(PeerChange &c)
5137 JMutexAutoLock envlock(m_env_mutex);
5138 JMutexAutoLock conlock(m_con_mutex);
5140 if(c.type == PEER_ADDED)
5147 std::map<u16, RemoteClient*>::iterator n;
5148 n = m_clients.find(c.peer_id);
5149 // The client shouldn't already exist
5150 assert(n == m_clients.end());
5153 RemoteClient *client = new RemoteClient();
5154 client->peer_id = c.peer_id;
5155 m_clients[client->peer_id] = client;
5158 else if(c.type == PEER_REMOVED)
5165 std::map<u16, RemoteClient*>::iterator n;
5166 n = m_clients.find(c.peer_id);
5167 // The client should exist
5168 assert(n != m_clients.end());
5171 Mark objects to be not known by the client
5173 RemoteClient *client = n->second;
5175 for(std::set<u16>::iterator
5176 i = client->m_known_objects.begin();
5177 i != client->m_known_objects.end(); ++i)
5181 ServerActiveObject* obj = m_env->getActiveObject(id);
5183 if(obj && obj->m_known_by_count > 0)
5184 obj->m_known_by_count--;
5188 Clear references to playing sounds
5190 for(std::map<s32, ServerPlayingSound>::iterator
5191 i = m_playing_sounds.begin();
5192 i != m_playing_sounds.end();)
5194 ServerPlayingSound &psound = i->second;
5195 psound.clients.erase(c.peer_id);
5196 if(psound.clients.size() == 0)
5197 m_playing_sounds.erase(i++);
5202 Player *player = m_env->getPlayer(c.peer_id);
5204 // Collect information about leaving in chat
5205 std::wstring message;
5209 std::wstring name = narrow_to_wide(player->getName());
5212 message += L" left the game.";
5214 message += L" (timed out)";
5218 /* Run scripts and remove from environment */
5222 PlayerSAO *playersao = player->getPlayerSAO();
5225 scriptapi_on_leaveplayer(m_lua, playersao);
5227 playersao->disconnected();
5237 std::ostringstream os(std::ios_base::binary);
5238 for(std::map<u16, RemoteClient*>::iterator
5239 i = m_clients.begin();
5240 i != m_clients.end(); ++i)
5242 RemoteClient *client = i->second;
5243 assert(client->peer_id == i->first);
5244 if(client->serialization_version == SER_FMT_VER_INVALID)
5247 Player *player = m_env->getPlayer(client->peer_id);
5250 // Get name of player
5251 os<<player->getName()<<" ";
5254 actionstream<<player->getName()<<" "
5255 <<(c.timeout?"times out.":"leaves game.")
5256 <<" List of players: "
5257 <<os.str()<<std::endl;
5262 delete m_clients[c.peer_id];
5263 m_clients.erase(c.peer_id);
5265 // Send player info to all remaining clients
5266 //SendPlayerInfos();
5268 // Send leave chat message to all remaining clients
5269 if(message.length() != 0)
5270 BroadcastChatMessage(message);
5279 void Server::handlePeerChanges()
5281 while(m_peer_change_queue.size() > 0)
5283 PeerChange c = m_peer_change_queue.pop_front();
5285 verbosestream<<"Server: Handling peer change: "
5286 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5289 handlePeerChange(c);
5293 void dedicated_server_loop(Server &server, bool &kill)
5295 DSTACK(__FUNCTION_NAME);
5297 verbosestream<<"dedicated_server_loop()"<<std::endl;
5299 IntervalLimiter m_profiler_interval;
5303 float steplen = g_settings->getFloat("dedicated_server_step");
5304 // This is kind of a hack but can be done like this
5305 // because server.step() is very light
5307 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5308 sleep_ms((int)(steplen*1000.0));
5310 server.step(steplen);
5312 if(server.getShutdownRequested() || kill)
5314 infostream<<"Dedicated server quitting"<<std::endl;
5316 if(g_settings->getBool("server_announce") == true)
5317 ServerList::sendAnnounce("delete");
5325 float profiler_print_interval =
5326 g_settings->getFloat("profiler_print_interval");
5327 if(profiler_print_interval != 0)
5329 if(m_profiler_interval.step(steplen, profiler_print_interval))
5331 infostream<<"Profiler:"<<std::endl;
5332 g_profiler->print(infostream);
5333 g_profiler->clear();