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"
37 #include "script/cpp_api/scriptapi.h"
44 #include "content_mapnode.h"
45 #include "content_nodemeta.h"
46 #include "content_abm.h"
47 #include "content_sao.h"
52 #include "sound.h" // dummySoundManager
53 #include "event_manager.h"
55 #include "serverlist.h"
56 #include "util/string.h"
57 #include "util/pointedthing.h"
58 #include "util/mathconstants.h"
60 #include "util/serialize.h"
61 #include "defaultsettings.h"
63 void * ServerThread::Thread()
67 log_register_thread("ServerThread");
69 DSTACK(__FUNCTION_NAME);
71 BEGIN_DEBUG_EXCEPTION_HANDLER
76 //TimeTaker timer("AsyncRunStep() + Receive()");
79 //TimeTaker timer("AsyncRunStep()");
80 m_server->AsyncRunStep();
83 //infostream<<"Running m_server->Receive()"<<std::endl;
86 catch(con::NoIncomingDataException &e)
89 catch(con::PeerNotFoundException &e)
91 infostream<<"Server: PeerNotFoundException"<<std::endl;
93 catch(con::ConnectionBindFailed &e)
95 m_server->setAsyncFatalError(e.what());
99 m_server->setAsyncFatalError(e.what());
103 END_DEBUG_EXCEPTION_HANDLER(errorstream)
108 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
110 if(pos_exists) *pos_exists = false;
115 if(pos_exists) *pos_exists = true;
120 ServerActiveObject *sao = env->getActiveObject(object);
123 if(pos_exists) *pos_exists = true;
124 return sao->getBasePosition(); }
129 void RemoteClient::GetNextBlocks(Server *server, float dtime,
130 std::vector<PrioritySortedBlockTransfer> &dest)
132 DSTACK(__FUNCTION_NAME);
135 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
138 m_nothing_to_send_pause_timer -= dtime;
139 m_nearest_unsent_reset_timer += dtime;
141 if(m_nothing_to_send_pause_timer >= 0)
144 Player *player = server->m_env->getPlayer(peer_id);
145 // This can happen sometimes; clients and players are not in perfect sync.
149 // Won't send anything if already sending
150 if(m_blocks_sending.size() >= g_settings->getU16
151 ("max_simultaneous_block_sends_per_client"))
153 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
157 //TimeTaker timer("RemoteClient::GetNextBlocks");
159 v3f playerpos = player->getPosition();
160 v3f playerspeed = player->getSpeed();
161 v3f playerspeeddir(0,0,0);
162 if(playerspeed.getLength() > 1.0*BS)
163 playerspeeddir = playerspeed / playerspeed.getLength();
164 // Predict to next block
165 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
167 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
169 v3s16 center = getNodeBlockPos(center_nodepos);
171 // Camera position and direction
172 v3f camera_pos = player->getEyePosition();
173 v3f camera_dir = v3f(0,0,1);
174 camera_dir.rotateYZBy(player->getPitch());
175 camera_dir.rotateXZBy(player->getYaw());
177 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
178 <<camera_dir.Z<<")"<<std::endl;*/
181 Get the starting value of the block finder radius.
184 if(m_last_center != center)
186 m_nearest_unsent_d = 0;
187 m_last_center = center;
190 /*infostream<<"m_nearest_unsent_reset_timer="
191 <<m_nearest_unsent_reset_timer<<std::endl;*/
193 // Reset periodically to workaround for some bugs or stuff
194 if(m_nearest_unsent_reset_timer > 20.0)
196 m_nearest_unsent_reset_timer = 0;
197 m_nearest_unsent_d = 0;
198 //infostream<<"Resetting m_nearest_unsent_d for "
199 // <<server->getPlayerName(peer_id)<<std::endl;
202 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
203 s16 d_start = m_nearest_unsent_d;
205 //infostream<<"d_start="<<d_start<<std::endl;
207 u16 max_simul_sends_setting = g_settings->getU16
208 ("max_simultaneous_block_sends_per_client");
209 u16 max_simul_sends_usually = max_simul_sends_setting;
212 Check the time from last addNode/removeNode.
214 Decrease send rate if player is building stuff.
216 m_time_from_building += dtime;
217 if(m_time_from_building < g_settings->getFloat(
218 "full_block_send_enable_min_time_from_building"))
220 max_simul_sends_usually
221 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
225 Number of blocks sending + number of blocks selected for sending
227 u32 num_blocks_selected = m_blocks_sending.size();
230 next time d will be continued from the d from which the nearest
231 unsent block was found this time.
233 This is because not necessarily any of the blocks found this
234 time are actually sent.
236 s32 new_nearest_unsent_d = -1;
238 s16 d_max = g_settings->getS16("max_block_send_distance");
239 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
241 // Don't loop very much at a time
242 s16 max_d_increment_at_time = 2;
243 if(d_max > d_start + max_d_increment_at_time)
244 d_max = d_start + max_d_increment_at_time;
245 /*if(d_max_gen > d_start+2)
246 d_max_gen = d_start+2;*/
248 //infostream<<"Starting from "<<d_start<<std::endl;
250 s32 nearest_emerged_d = -1;
251 s32 nearest_emergefull_d = -1;
252 s32 nearest_sent_d = -1;
253 bool queue_is_full = false;
256 for(d = d_start; d <= d_max; d++)
258 /*errorstream<<"checking d="<<d<<" for "
259 <<server->getPlayerName(peer_id)<<std::endl;*/
260 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
263 If m_nearest_unsent_d was changed by the EmergeThread
264 (it can change it to 0 through SetBlockNotSent),
266 Else update m_nearest_unsent_d
268 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
270 d = m_nearest_unsent_d;
271 last_nearest_unsent_d = m_nearest_unsent_d;
275 Get the border/face dot coordinates of a "d-radiused"
278 std::list<v3s16> list;
279 getFacePositions(list, d);
281 std::list<v3s16>::iterator li;
282 for(li=list.begin(); li!=list.end(); ++li)
284 v3s16 p = *li + center;
288 - Don't allow too many simultaneous transfers
289 - EXCEPT when the blocks are very close
291 Also, don't send blocks that are already flying.
294 // Start with the usual maximum
295 u16 max_simul_dynamic = max_simul_sends_usually;
297 // If block is very close, allow full maximum
298 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
299 max_simul_dynamic = max_simul_sends_setting;
301 // Don't select too many blocks for sending
302 if(num_blocks_selected >= max_simul_dynamic)
304 queue_is_full = true;
305 goto queue_full_break;
308 // Don't send blocks that are currently being transferred
309 if(m_blocks_sending.find(p) != m_blocks_sending.end())
315 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
316 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
317 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
318 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
319 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
320 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
323 // If this is true, inexistent block will be made from scratch
324 bool generate = d <= d_max_gen;
327 /*// Limit the generating area vertically to 2/3
328 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
331 // Limit the send area vertically to 1/2
332 if(abs(p.Y - center.Y) > d_max / 2)
338 If block is far away, don't generate it unless it is
344 // Block center y in nodes
345 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
346 // Don't generate if it's very high or very low
347 if(y < -64 || y > 64)
351 v2s16 p2d_nodes_center(
355 // Get ground height in nodes
356 s16 gh = server->m_env->getServerMap().findGroundLevel(
359 // If differs a lot, don't generate
360 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
362 // Actually, don't even send it
368 //infostream<<"d="<<d<<std::endl;
371 Don't generate or send if not in sight
372 FIXME This only works if the client uses a small enough
373 FOV setting. The default of 72 degrees is fine.
376 float camera_fov = (72.0*M_PI/180) * 4./3.;
377 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
383 Don't send already sent blocks
386 if(m_blocks_sent.find(p) != m_blocks_sent.end())
393 Check if map has this block
395 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
397 bool surely_not_found_on_disk = false;
398 bool block_is_invalid = false;
401 // Reset usage timer, this block will be of use in the future.
402 block->resetUsageTimer();
404 // Block is dummy if data doesn't exist.
405 // It means it has been not found from disk and not generated
408 surely_not_found_on_disk = true;
411 // Block is valid if lighting is up-to-date and data exists
412 if(block->isValid() == false)
414 block_is_invalid = true;
417 /*if(block->isFullyGenerated() == false)
419 block_is_invalid = true;
424 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
425 v2s16 chunkpos = map->sector_to_chunk(p2d);
426 if(map->chunkNonVolatile(chunkpos) == false)
427 block_is_invalid = true;
429 if(block->isGenerated() == false)
430 block_is_invalid = true;
433 If block is not close, don't send it unless it is near
436 Block is near ground level if night-time mesh
437 differs from day-time mesh.
441 if(block->getDayNightDiff() == false)
448 If block has been marked to not exist on disk (dummy)
449 and generating new ones is not wanted, skip block.
451 if(generate == false && surely_not_found_on_disk == true)
458 Add inexistent block to emerge queue.
460 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
462 /* //TODO: Get value from somewhere
463 // Allow only one block in emerge queue
464 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
465 // Allow two blocks in queue per client
466 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
468 // Make it more responsive when needing to generate stuff
469 if(surely_not_found_on_disk)
471 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
473 //infostream<<"Adding block to emerge queue"<<std::endl;
475 // Add it to the emerge queue and trigger the thread
478 if(generate == false)
479 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
481 server->m_emerge_queue.addBlock(peer_id, p, flags);
482 server->m_emergethread.trigger();
484 if(nearest_emerged_d == -1)
485 nearest_emerged_d = d;
487 if(nearest_emergefull_d == -1)
488 nearest_emergefull_d = d;
489 goto queue_full_break;
493 if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) {
494 if (nearest_emerged_d == -1)
495 nearest_emerged_d = d;
497 if (nearest_emergefull_d == -1)
498 nearest_emergefull_d = d;
499 goto queue_full_break;
506 if(nearest_sent_d == -1)
510 Add block to send queue
513 /*errorstream<<"sending from d="<<d<<" to "
514 <<server->getPlayerName(peer_id)<<std::endl;*/
516 PrioritySortedBlockTransfer q((float)d, p, peer_id);
520 num_blocks_selected += 1;
525 //infostream<<"Stopped at "<<d<<std::endl;
527 // If nothing was found for sending and nothing was queued for
528 // emerging, continue next time browsing from here
529 if(nearest_emerged_d != -1){
530 new_nearest_unsent_d = nearest_emerged_d;
531 } else if(nearest_emergefull_d != -1){
532 new_nearest_unsent_d = nearest_emergefull_d;
534 if(d > g_settings->getS16("max_block_send_distance")){
535 new_nearest_unsent_d = 0;
536 m_nothing_to_send_pause_timer = 2.0;
537 /*infostream<<"GetNextBlocks(): d wrapped around for "
538 <<server->getPlayerName(peer_id)
539 <<"; setting to 0 and pausing"<<std::endl;*/
541 if(nearest_sent_d != -1)
542 new_nearest_unsent_d = nearest_sent_d;
544 new_nearest_unsent_d = d;
548 if(new_nearest_unsent_d != -1)
549 m_nearest_unsent_d = new_nearest_unsent_d;
551 /*timer_result = timer.stop(true);
552 if(timer_result != 0)
553 infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
556 void RemoteClient::GotBlock(v3s16 p)
558 if(m_blocks_sending.find(p) != m_blocks_sending.end())
559 m_blocks_sending.erase(p);
562 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
563 " m_blocks_sending"<<std::endl;*/
564 m_excess_gotblocks++;
566 m_blocks_sent.insert(p);
569 void RemoteClient::SentBlock(v3s16 p)
571 if(m_blocks_sending.find(p) == m_blocks_sending.end())
572 m_blocks_sending[p] = 0.0;
574 infostream<<"RemoteClient::SentBlock(): Sent block"
575 " already in m_blocks_sending"<<std::endl;
578 void RemoteClient::SetBlockNotSent(v3s16 p)
580 m_nearest_unsent_d = 0;
582 if(m_blocks_sending.find(p) != m_blocks_sending.end())
583 m_blocks_sending.erase(p);
584 if(m_blocks_sent.find(p) != m_blocks_sent.end())
585 m_blocks_sent.erase(p);
588 void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
590 m_nearest_unsent_d = 0;
592 for(std::map<v3s16, MapBlock*>::iterator
594 i != blocks.end(); ++i)
598 if(m_blocks_sending.find(p) != m_blocks_sending.end())
599 m_blocks_sending.erase(p);
600 if(m_blocks_sent.find(p) != m_blocks_sent.end())
601 m_blocks_sent.erase(p);
609 PlayerInfo::PlayerInfo()
615 void PlayerInfo::PrintLine(std::ostream *s)
618 (*s)<<"\""<<name<<"\" ("
619 <<(position.X/10)<<","<<(position.Y/10)
620 <<","<<(position.Z/10)<<") ";
622 (*s)<<" avg_rtt="<<avg_rtt;
631 const std::string &path_world,
632 const std::string &path_config,
633 const SubgameSpec &gamespec,
634 bool simple_singleplayer_mode
636 m_path_world(path_world),
637 m_path_config(path_config),
638 m_gamespec(gamespec),
639 m_simple_singleplayer_mode(simple_singleplayer_mode),
640 m_async_fatal_error(""),
642 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT,
643 g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), 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::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
711 // complain about mods with unsatisfied dependencies
712 if(!modconf.isConsistent())
714 for(std::vector<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> load_mod_names;
731 for(std::vector<std::string>::iterator it = names.begin();
732 it != names.end(); ++it)
734 std::string name = *it;
735 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
736 load_mod_names.insert(name.substr(9));
738 // complain about mods declared to be loaded, but not found
739 for(std::vector<ModSpec>::iterator it = m_mods.begin();
740 it != m_mods.end(); ++it)
741 load_mod_names.erase((*it).name);
742 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
743 it != unsatisfied_mods.end(); ++it)
744 load_mod_names.erase((*it).name);
745 if(!load_mod_names.empty())
747 errorstream << "The following mods could not be found:";
748 for(std::set<std::string>::iterator it = load_mod_names.begin();
749 it != load_mod_names.end(); ++it)
750 errorstream << " \"" << (*it) << "\"";
751 errorstream << std::endl;
754 // Path to builtin.lua
755 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
758 JMutexAutoLock envlock(m_env_mutex);
759 JMutexAutoLock conlock(m_con_mutex);
761 // Initialize scripting
763 infostream<<"Server: Initializing Lua"<<std::endl;
765 m_script = new ScriptApi(this);
768 // Load and run builtin.lua
769 infostream<<"Server: Loading builtin.lua [\""
770 <<builtinpath<<"\"]"<<std::endl;
771 bool success = m_script->loadMod(builtinpath, "__builtin");
773 errorstream<<"Server: Failed to load and run "
774 <<builtinpath<<std::endl;
775 throw ModError("Failed to load and run "+builtinpath);
778 infostream<<"Server: Loading mods: ";
779 for(std::vector<ModSpec>::iterator i = m_mods.begin();
780 i != m_mods.end(); i++){
781 const ModSpec &mod = *i;
782 infostream<<mod.name<<" ";
784 infostream<<std::endl;
785 // Load and run "mod" scripts
786 for(std::vector<ModSpec>::iterator i = m_mods.begin();
787 i != m_mods.end(); i++){
788 const ModSpec &mod = *i;
789 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
790 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
791 <<scriptpath<<"\"]"<<std::endl;
792 bool success = m_script->loadMod(scriptpath, mod.name);
794 errorstream<<"Server: Failed to load and run "
795 <<scriptpath<<std::endl;
796 throw ModError("Failed to load and run "+scriptpath);
800 // Read Textures and calculate sha1 sums
803 // Apply item aliases in the node definition manager
804 m_nodedef->updateAliases(m_itemdef);
806 // Initialize Environment
807 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
808 m_env = new ServerEnvironment(servermap, m_script, this, this);
810 m_emerge->initMapgens(servermap->getMapgenParams());
812 // Give environment reference to scripting api
813 m_script->initializeEnvironment(m_env);
815 // Register us to receive map edit events
816 servermap->addEventReceiver(this);
818 // If file exists, load environment metadata
819 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
821 infostream<<"Server: Loading environment metadata"<<std::endl;
822 m_env->loadMeta(m_path_world);
826 infostream<<"Server: Loading players"<<std::endl;
827 m_env->deSerializePlayers(m_path_world);
830 Add some test ActiveBlockModifiers to environment
832 add_legacy_abms(m_env, m_nodedef);
834 m_liquid_transform_every = g_settings->getFloat("liquid_update");
839 infostream<<"Server destructing"<<std::endl;
842 Send shutdown message
845 JMutexAutoLock conlock(m_con_mutex);
847 std::wstring line = L"*** Server shutting down";
850 Send the message to clients
852 for(std::map<u16, RemoteClient*>::iterator
853 i = m_clients.begin();
854 i != m_clients.end(); ++i)
856 // Get client and check that it is valid
857 RemoteClient *client = i->second;
858 assert(client->peer_id == i->first);
859 if(client->serialization_version == SER_FMT_VER_INVALID)
863 SendChatMessage(client->peer_id, line);
865 catch(con::PeerNotFoundException &e)
871 JMutexAutoLock envlock(m_env_mutex);
872 JMutexAutoLock conlock(m_con_mutex);
875 Execute script shutdown hooks
877 m_script->on_shutdown();
881 JMutexAutoLock envlock(m_env_mutex);
886 infostream<<"Server: Saving players"<<std::endl;
887 m_env->serializePlayers(m_path_world);
890 Save environment metadata
892 infostream<<"Server: Saving environment metadata"<<std::endl;
893 m_env->saveMeta(m_path_world);
901 //shutdown all emerge threads first!
908 JMutexAutoLock clientslock(m_con_mutex);
910 for(std::map<u16, RemoteClient*>::iterator
911 i = m_clients.begin();
912 i != m_clients.end(); ++i)
920 // Delete things in the reverse order of creation
928 // Deinitialize scripting
929 infostream<<"Server: Deinitializing scripting"<<std::endl;
932 // Delete detached inventories
934 for(std::map<std::string, Inventory*>::iterator
935 i = m_detached_inventories.begin();
936 i != m_detached_inventories.end(); i++){
942 void Server::start(unsigned short port)
944 DSTACK(__FUNCTION_NAME);
945 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
947 // Stop thread if already running
950 // Initialize connection
951 m_con.SetTimeoutMs(30);
955 m_thread.setRun(true);
958 // ASCII art for the win!
960 <<" .__ __ __ "<<std::endl
961 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
962 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
963 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
964 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
965 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
966 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
967 actionstream<<"Server for gameid=\""<<m_gamespec.id
968 <<"\" listening on port "<<port<<"."<<std::endl;
973 DSTACK(__FUNCTION_NAME);
975 infostream<<"Server: Stopping and waiting threads"<<std::endl;
977 // Stop threads (set run=false first so both start stopping)
978 m_thread.setRun(false);
979 //m_emergethread.setRun(false);
981 //m_emergethread.stop();
983 infostream<<"Server: Threads stopped"<<std::endl;
986 void Server::step(float dtime)
988 DSTACK(__FUNCTION_NAME);
993 JMutexAutoLock lock(m_step_dtime_mutex);
994 m_step_dtime += dtime;
996 // Throw if fatal error occurred in thread
997 std::string async_err = m_async_fatal_error.get();
999 throw ServerError(async_err);
1003 void Server::AsyncRunStep()
1005 DSTACK(__FUNCTION_NAME);
1007 g_profiler->add("Server::AsyncRunStep (num)", 1);
1011 JMutexAutoLock lock1(m_step_dtime_mutex);
1012 dtime = m_step_dtime;
1016 // Send blocks to clients
1023 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1025 //infostream<<"Server steps "<<dtime<<std::endl;
1026 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1029 JMutexAutoLock lock1(m_step_dtime_mutex);
1030 m_step_dtime -= dtime;
1037 m_uptime.set(m_uptime.get() + dtime);
1041 // Process connection's timeouts
1042 JMutexAutoLock lock2(m_con_mutex);
1043 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1044 m_con.RunTimeouts(dtime);
1048 // This has to be called so that the client list gets synced
1049 // with the peer list of the connection
1050 handlePeerChanges();
1054 Update time of day and overall game time
1057 JMutexAutoLock envlock(m_env_mutex);
1059 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1062 Send to clients at constant intervals
1065 m_time_of_day_send_timer -= dtime;
1066 if(m_time_of_day_send_timer < 0.0)
1068 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1070 //JMutexAutoLock envlock(m_env_mutex);
1071 JMutexAutoLock conlock(m_con_mutex);
1073 for(std::map<u16, RemoteClient*>::iterator
1074 i = m_clients.begin();
1075 i != m_clients.end(); ++i)
1077 RemoteClient *client = i->second;
1078 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1079 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1081 m_con.Send(client->peer_id, 0, data, true);
1087 JMutexAutoLock lock(m_env_mutex);
1089 ScopeProfiler sp(g_profiler, "SEnv step");
1090 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1094 const float map_timer_and_unload_dtime = 2.92;
1095 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1097 JMutexAutoLock lock(m_env_mutex);
1098 // Run Map's timers and unload unused data
1099 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1100 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1101 g_settings->getFloat("server_unload_unused_data_timeout"));
1112 JMutexAutoLock lock(m_env_mutex);
1113 JMutexAutoLock lock2(m_con_mutex);
1115 ScopeProfiler sp(g_profiler, "Server: handle players");
1117 for(std::map<u16, RemoteClient*>::iterator
1118 i = m_clients.begin();
1119 i != m_clients.end(); ++i)
1121 RemoteClient *client = i->second;
1122 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1123 if(playersao == NULL)
1127 Handle player HPs (die if hp=0)
1129 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
1131 if(playersao->getHP() == 0)
1132 DiePlayer(client->peer_id);
1134 SendPlayerHP(client->peer_id);
1138 Send player inventories if necessary
1140 if(playersao->m_moved){
1141 SendMovePlayer(client->peer_id);
1142 playersao->m_moved = false;
1144 if(playersao->m_inventory_not_sent){
1145 UpdateCrafting(client->peer_id);
1146 SendInventory(client->peer_id);
1151 /* Transform liquids */
1152 m_liquid_transform_timer += dtime;
1153 if(m_liquid_transform_timer >= m_liquid_transform_every)
1155 m_liquid_transform_timer -= m_liquid_transform_every;
1157 JMutexAutoLock lock(m_env_mutex);
1159 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1161 std::map<v3s16, MapBlock*> modified_blocks;
1162 m_env->getMap().transformLiquids(modified_blocks);
1167 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1168 ServerMap &map = ((ServerMap&)m_env->getMap());
1169 map.updateLighting(modified_blocks, lighting_modified_blocks);
1171 // Add blocks modified by lighting to modified_blocks
1172 for(core::map<v3s16, MapBlock*>::Iterator
1173 i = lighting_modified_blocks.getIterator();
1174 i.atEnd() == false; i++)
1176 MapBlock *block = i.getNode()->getValue();
1177 modified_blocks.insert(block->getPos(), block);
1181 Set the modified blocks unsent for all the clients
1184 JMutexAutoLock lock2(m_con_mutex);
1186 for(std::map<u16, RemoteClient*>::iterator
1187 i = m_clients.begin();
1188 i != m_clients.end(); ++i)
1190 RemoteClient *client = i->second;
1192 if(modified_blocks.size() > 0)
1194 // Remove block from sent history
1195 client->SetBlocksNotSent(modified_blocks);
1200 // Periodically print some info
1202 float &counter = m_print_info_timer;
1208 JMutexAutoLock lock2(m_con_mutex);
1209 m_clients_number = 0;
1210 if(m_clients.size() != 0)
1211 infostream<<"Players:"<<std::endl;
1212 for(std::map<u16, RemoteClient*>::iterator
1213 i = m_clients.begin();
1214 i != m_clients.end(); ++i)
1216 //u16 peer_id = i.getNode()->getKey();
1217 RemoteClient *client = i->second;
1218 Player *player = m_env->getPlayer(client->peer_id);
1221 infostream<<"* "<<player->getName()<<"\t";
1222 client->PrintInfo(infostream);
1230 // send masterserver announce
1232 float &counter = m_masterserver_timer;
1233 if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
1235 ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id);
1242 //if(g_settings->getBool("enable_experimental"))
1246 Check added and deleted active objects
1249 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1250 JMutexAutoLock envlock(m_env_mutex);
1251 JMutexAutoLock conlock(m_con_mutex);
1253 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1255 // Radius inside which objects are active
1256 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1257 radius *= MAP_BLOCKSIZE;
1259 for(std::map<u16, RemoteClient*>::iterator
1260 i = m_clients.begin();
1261 i != m_clients.end(); ++i)
1263 RemoteClient *client = i->second;
1265 // If definitions and textures have not been sent, don't
1266 // send objects either
1267 if(!client->definitions_sent)
1270 Player *player = m_env->getPlayer(client->peer_id);
1273 // This can happen if the client timeouts somehow
1274 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1276 <<" has no associated player"<<std::endl;*/
1279 v3s16 pos = floatToInt(player->getPosition(), BS);
1281 std::set<u16> removed_objects;
1282 std::set<u16> added_objects;
1283 m_env->getRemovedActiveObjects(pos, radius,
1284 client->m_known_objects, removed_objects);
1285 m_env->getAddedActiveObjects(pos, radius,
1286 client->m_known_objects, added_objects);
1288 // Ignore if nothing happened
1289 if(removed_objects.size() == 0 && added_objects.size() == 0)
1291 //infostream<<"active objects: none changed"<<std::endl;
1295 std::string data_buffer;
1299 // Handle removed objects
1300 writeU16((u8*)buf, removed_objects.size());
1301 data_buffer.append(buf, 2);
1302 for(std::set<u16>::iterator
1303 i = removed_objects.begin();
1304 i != removed_objects.end(); ++i)
1308 ServerActiveObject* obj = m_env->getActiveObject(id);
1310 // Add to data buffer for sending
1311 writeU16((u8*)buf, id);
1312 data_buffer.append(buf, 2);
1314 // Remove from known objects
1315 client->m_known_objects.erase(id);
1317 if(obj && obj->m_known_by_count > 0)
1318 obj->m_known_by_count--;
1321 // Handle added objects
1322 writeU16((u8*)buf, added_objects.size());
1323 data_buffer.append(buf, 2);
1324 for(std::set<u16>::iterator
1325 i = added_objects.begin();
1326 i != added_objects.end(); ++i)
1330 ServerActiveObject* obj = m_env->getActiveObject(id);
1333 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1335 infostream<<"WARNING: "<<__FUNCTION_NAME
1336 <<": NULL object"<<std::endl;
1338 type = obj->getSendType();
1340 // Add to data buffer for sending
1341 writeU16((u8*)buf, id);
1342 data_buffer.append(buf, 2);
1343 writeU8((u8*)buf, type);
1344 data_buffer.append(buf, 1);
1347 data_buffer.append(serializeLongString(
1348 obj->getClientInitializationData(client->net_proto_version)));
1350 data_buffer.append(serializeLongString(""));
1352 // Add to known objects
1353 client->m_known_objects.insert(id);
1356 obj->m_known_by_count++;
1360 SharedBuffer<u8> reply(2 + data_buffer.size());
1361 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1362 memcpy((char*)&reply[2], data_buffer.c_str(),
1363 data_buffer.size());
1365 m_con.Send(client->peer_id, 0, reply, true);
1367 verbosestream<<"Server: Sent object remove/add: "
1368 <<removed_objects.size()<<" removed, "
1369 <<added_objects.size()<<" added, "
1370 <<"packet size is "<<reply.getSize()<<std::endl;
1375 Collect a list of all the objects known by the clients
1376 and report it back to the environment.
1379 core::map<u16, bool> all_known_objects;
1381 for(core::map<u16, RemoteClient*>::Iterator
1382 i = m_clients.getIterator();
1383 i.atEnd() == false; i++)
1385 RemoteClient *client = i.getNode()->getValue();
1386 // Go through all known objects of client
1387 for(core::map<u16, bool>::Iterator
1388 i = client->m_known_objects.getIterator();
1389 i.atEnd()==false; i++)
1391 u16 id = i.getNode()->getKey();
1392 all_known_objects[id] = true;
1396 m_env->setKnownActiveObjects(whatever);
1402 Send object messages
1405 JMutexAutoLock envlock(m_env_mutex);
1406 JMutexAutoLock conlock(m_con_mutex);
1408 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1411 // Value = data sent by object
1412 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
1414 // Get active object messages from environment
1417 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1421 std::list<ActiveObjectMessage>* message_list = NULL;
1422 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
1423 n = buffered_messages.find(aom.id);
1424 if(n == buffered_messages.end())
1426 message_list = new std::list<ActiveObjectMessage>;
1427 buffered_messages[aom.id] = message_list;
1431 message_list = n->second;
1433 message_list->push_back(aom);
1436 // Route data to every client
1437 for(std::map<u16, RemoteClient*>::iterator
1438 i = m_clients.begin();
1439 i != m_clients.end(); ++i)
1441 RemoteClient *client = i->second;
1442 std::string reliable_data;
1443 std::string unreliable_data;
1444 // Go through all objects in message buffer
1445 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1446 j = buffered_messages.begin();
1447 j != buffered_messages.end(); ++j)
1449 // If object is not known by client, skip it
1451 if(client->m_known_objects.find(id) == client->m_known_objects.end())
1453 // Get message list of object
1454 std::list<ActiveObjectMessage>* list = j->second;
1455 // Go through every message
1456 for(std::list<ActiveObjectMessage>::iterator
1457 k = list->begin(); k != list->end(); ++k)
1459 // Compose the full new data with header
1460 ActiveObjectMessage aom = *k;
1461 std::string new_data;
1464 writeU16((u8*)&buf[0], aom.id);
1465 new_data.append(buf, 2);
1467 new_data += serializeString(aom.datastring);
1468 // Add data to buffer
1470 reliable_data += new_data;
1472 unreliable_data += new_data;
1476 reliable_data and unreliable_data are now ready.
1479 if(reliable_data.size() > 0)
1481 SharedBuffer<u8> reply(2 + reliable_data.size());
1482 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1483 memcpy((char*)&reply[2], reliable_data.c_str(),
1484 reliable_data.size());
1486 m_con.Send(client->peer_id, 0, reply, true);
1488 if(unreliable_data.size() > 0)
1490 SharedBuffer<u8> reply(2 + unreliable_data.size());
1491 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1492 memcpy((char*)&reply[2], unreliable_data.c_str(),
1493 unreliable_data.size());
1494 // Send as unreliable
1495 m_con.Send(client->peer_id, 0, reply, false);
1498 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1500 infostream<<"Server: Size of object message data: "
1501 <<"reliable: "<<reliable_data.size()
1502 <<", unreliable: "<<unreliable_data.size()
1507 // Clear buffered_messages
1508 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1509 i = buffered_messages.begin();
1510 i != buffered_messages.end(); ++i)
1516 } // enable_experimental
1519 Send queued-for-sending map edit events.
1522 // We will be accessing the environment and the connection
1523 JMutexAutoLock lock(m_env_mutex);
1524 JMutexAutoLock conlock(m_con_mutex);
1526 // Don't send too many at a time
1529 // Single change sending is disabled if queue size is not small
1530 bool disable_single_change_sending = false;
1531 if(m_unsent_map_edit_queue.size() >= 4)
1532 disable_single_change_sending = true;
1534 int event_count = m_unsent_map_edit_queue.size();
1536 // We'll log the amount of each
1539 while(m_unsent_map_edit_queue.size() != 0)
1541 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1543 // Players far away from the change are stored here.
1544 // Instead of sending the changes, MapBlocks are set not sent
1546 std::list<u16> far_players;
1548 if(event->type == MEET_ADDNODE)
1550 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1551 prof.add("MEET_ADDNODE", 1);
1552 if(disable_single_change_sending)
1553 sendAddNode(event->p, event->n, event->already_known_by_peer,
1556 sendAddNode(event->p, event->n, event->already_known_by_peer,
1559 else if(event->type == MEET_REMOVENODE)
1561 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1562 prof.add("MEET_REMOVENODE", 1);
1563 if(disable_single_change_sending)
1564 sendRemoveNode(event->p, event->already_known_by_peer,
1567 sendRemoveNode(event->p, event->already_known_by_peer,
1570 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1572 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1573 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1574 setBlockNotSent(event->p);
1576 else if(event->type == MEET_OTHER)
1578 infostream<<"Server: MEET_OTHER"<<std::endl;
1579 prof.add("MEET_OTHER", 1);
1580 for(std::set<v3s16>::iterator
1581 i = event->modified_blocks.begin();
1582 i != event->modified_blocks.end(); ++i)
1584 setBlockNotSent(*i);
1589 prof.add("unknown", 1);
1590 infostream<<"WARNING: Server: Unknown MapEditEvent "
1591 <<((u32)event->type)<<std::endl;
1595 Set blocks not sent to far players
1597 if(far_players.size() > 0)
1599 // Convert list format to that wanted by SetBlocksNotSent
1600 std::map<v3s16, MapBlock*> modified_blocks2;
1601 for(std::set<v3s16>::iterator
1602 i = event->modified_blocks.begin();
1603 i != event->modified_blocks.end(); ++i)
1605 modified_blocks2[*i] =
1606 m_env->getMap().getBlockNoCreateNoEx(*i);
1608 // Set blocks not sent
1609 for(std::list<u16>::iterator
1610 i = far_players.begin();
1611 i != far_players.end(); ++i)
1614 RemoteClient *client = getClient(peer_id);
1617 client->SetBlocksNotSent(modified_blocks2);
1623 /*// Don't send too many at a time
1625 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1629 if(event_count >= 5){
1630 infostream<<"Server: MapEditEvents:"<<std::endl;
1631 prof.print(infostream);
1632 } else if(event_count != 0){
1633 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1634 prof.print(verbosestream);
1640 Trigger emergethread (it somehow gets to a non-triggered but
1641 bysy state sometimes)
1644 float &counter = m_emergethread_trigger_timer;
1650 for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
1651 m_emerge->emergethread[i]->trigger();
1653 // Update m_enable_rollback_recording here too
1654 m_enable_rollback_recording =
1655 g_settings->getBool("enable_rollback_recording");
1659 // Save map, players and auth stuff
1661 float &counter = m_savemap_timer;
1663 if(counter >= g_settings->getFloat("server_map_save_interval"))
1666 JMutexAutoLock lock(m_env_mutex);
1668 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1671 if(m_banmanager.isModified())
1672 m_banmanager.save();
1674 // Save changed parts of map
1675 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1678 m_env->serializePlayers(m_path_world);
1680 // Save environment metadata
1681 m_env->saveMeta(m_path_world);
1686 void Server::Receive()
1688 DSTACK(__FUNCTION_NAME);
1689 SharedBuffer<u8> data;
1694 JMutexAutoLock conlock(m_con_mutex);
1695 datasize = m_con.Receive(peer_id, data);
1698 // This has to be called so that the client list gets synced
1699 // with the peer list of the connection
1700 handlePeerChanges();
1702 ProcessData(*data, datasize, peer_id);
1704 catch(con::InvalidIncomingDataException &e)
1706 infostream<<"Server::Receive(): "
1707 "InvalidIncomingDataException: what()="
1708 <<e.what()<<std::endl;
1710 catch(con::PeerNotFoundException &e)
1712 //NOTE: This is not needed anymore
1714 // The peer has been disconnected.
1715 // Find the associated player and remove it.
1717 /*JMutexAutoLock envlock(m_env_mutex);
1719 infostream<<"ServerThread: peer_id="<<peer_id
1720 <<" has apparently closed connection. "
1721 <<"Removing player."<<std::endl;
1723 m_env->removePlayer(peer_id);*/
1727 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1729 DSTACK(__FUNCTION_NAME);
1730 // Environment is locked first.
1731 JMutexAutoLock envlock(m_env_mutex);
1732 JMutexAutoLock conlock(m_con_mutex);
1734 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1737 Address address = m_con.GetPeerAddress(peer_id);
1738 std::string addr_s = address.serializeString();
1740 // drop player if is ip is banned
1741 if(m_banmanager.isIpBanned(addr_s)){
1742 infostream<<"Server: A banned client tried to connect from "
1743 <<addr_s<<"; banned name was "
1744 <<m_banmanager.getBanName(addr_s)<<std::endl;
1745 // This actually doesn't seem to transfer to the client
1746 SendAccessDenied(m_con, peer_id,
1747 L"Your ip is banned. Banned name was "
1748 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1749 m_con.DeletePeer(peer_id);
1753 catch(con::PeerNotFoundException &e)
1755 infostream<<"Server::ProcessData(): Cancelling: peer "
1756 <<peer_id<<" not found"<<std::endl;
1760 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1762 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1770 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1772 if(command == TOSERVER_INIT)
1774 // [0] u16 TOSERVER_INIT
1775 // [2] u8 SER_FMT_VER_HIGHEST
1776 // [3] u8[20] player_name
1777 // [23] u8[28] password <--- can be sent without this, from old versions
1779 if(datasize < 2+1+PLAYERNAME_SIZE)
1782 verbosestream<<"Server: Got TOSERVER_INIT from "
1783 <<peer_id<<std::endl;
1785 // First byte after command is maximum supported
1786 // serialization version
1787 u8 client_max = data[2];
1788 u8 our_max = SER_FMT_VER_HIGHEST;
1789 // Use the highest version supported by both
1790 u8 deployed = std::min(client_max, our_max);
1791 // If it's lower than the lowest supported, give up.
1792 if(deployed < SER_FMT_VER_LOWEST)
1793 deployed = SER_FMT_VER_INVALID;
1795 //peer->serialization_version = deployed;
1796 getClient(peer_id)->pending_serialization_version = deployed;
1798 if(deployed == SER_FMT_VER_INVALID)
1800 actionstream<<"Server: A mismatched client tried to connect from "
1801 <<addr_s<<std::endl;
1802 infostream<<"Server: Cannot negotiate "
1803 "serialization version with peer "
1804 <<peer_id<<std::endl;
1805 SendAccessDenied(m_con, peer_id, std::wstring(
1806 L"Your client's version is not supported.\n"
1807 L"Server version is ")
1808 + narrow_to_wide(VERSION_STRING) + L"."
1814 Read and check network protocol version
1817 u16 min_net_proto_version = 0;
1818 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1819 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1821 // Use same version as minimum and maximum if maximum version field
1822 // doesn't exist (backwards compatibility)
1823 u16 max_net_proto_version = min_net_proto_version;
1824 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1825 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1827 // Start with client's maximum version
1828 u16 net_proto_version = max_net_proto_version;
1830 // Figure out a working version if it is possible at all
1831 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1832 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1834 // If maximum is larger than our maximum, go with our maximum
1835 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1836 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1837 // Else go with client's maximum
1839 net_proto_version = max_net_proto_version;
1842 verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
1843 <<min_net_proto_version<<", max: "<<max_net_proto_version
1844 <<", chosen: "<<net_proto_version<<std::endl;
1846 getClient(peer_id)->net_proto_version = net_proto_version;
1848 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1849 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1851 actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
1853 SendAccessDenied(m_con, peer_id, std::wstring(
1854 L"Your client's version is not supported.\n"
1855 L"Server version is ")
1856 + narrow_to_wide(VERSION_STRING) + L",\n"
1857 + L"server's PROTOCOL_VERSION is "
1858 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1860 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1861 + L", client's PROTOCOL_VERSION is "
1862 + narrow_to_wide(itos(min_net_proto_version))
1864 + narrow_to_wide(itos(max_net_proto_version))
1869 if(g_settings->getBool("strict_protocol_version_checking"))
1871 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1873 actionstream<<"Server: A mismatched (strict) client tried to "
1874 <<"connect from "<<addr_s<<std::endl;
1875 SendAccessDenied(m_con, peer_id, std::wstring(
1876 L"Your client's version is not supported.\n"
1877 L"Server version is ")
1878 + narrow_to_wide(VERSION_STRING) + L",\n"
1879 + L"server's PROTOCOL_VERSION (strict) is "
1880 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1881 + L", client's PROTOCOL_VERSION is "
1882 + narrow_to_wide(itos(min_net_proto_version))
1884 + narrow_to_wide(itos(max_net_proto_version))
1895 char playername[PLAYERNAME_SIZE];
1896 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1898 playername[i] = data[3+i];
1900 playername[PLAYERNAME_SIZE-1] = 0;
1902 if(playername[0]=='\0')
1904 actionstream<<"Server: Player with an empty name "
1905 <<"tried to connect from "<<addr_s<<std::endl;
1906 SendAccessDenied(m_con, peer_id,
1911 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1913 actionstream<<"Server: Player with an invalid name "
1914 <<"tried to connect from "<<addr_s<<std::endl;
1915 SendAccessDenied(m_con, peer_id,
1916 L"Name contains unallowed characters");
1920 infostream<<"Server: New connection: \""<<playername<<"\" from "
1921 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1924 char given_password[PASSWORD_SIZE];
1925 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1927 // old version - assume blank password
1928 given_password[0] = 0;
1932 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1934 given_password[i] = data[23+i];
1936 given_password[PASSWORD_SIZE-1] = 0;
1939 if(!base64_is_valid(given_password)){
1940 infostream<<"Server: "<<playername
1941 <<" supplied invalid password hash"<<std::endl;
1942 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1946 std::string checkpwd; // Password hash to check against
1947 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1949 // If no authentication info exists for user, create it
1951 if(!isSingleplayer() &&
1952 g_settings->getBool("disallow_empty_password") &&
1953 std::string(given_password) == ""){
1954 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1955 L"disallowed. Set a password and try again.");
1958 std::wstring raw_default_password =
1959 narrow_to_wide(g_settings->get("default_password"));
1960 std::string initial_password =
1961 translatePassword(playername, raw_default_password);
1963 // If default_password is empty, allow any initial password
1964 if (raw_default_password.length() == 0)
1965 initial_password = given_password;
1967 m_script->createAuth(playername, initial_password);
1970 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1973 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1977 if(given_password != checkpwd){
1978 infostream<<"Server: peer_id="<<peer_id
1979 <<": supplied invalid password for "
1980 <<playername<<std::endl;
1981 SendAccessDenied(m_con, peer_id, L"Invalid password");
1985 // Do not allow multiple players in simple singleplayer mode.
1986 // This isn't a perfect way to do it, but will suffice for now.
1987 if(m_simple_singleplayer_mode && m_clients.size() > 1){
1988 infostream<<"Server: Not allowing another client to connect in"
1989 <<" simple singleplayer mode"<<std::endl;
1990 SendAccessDenied(m_con, peer_id,
1991 L"Running in simple singleplayer mode.");
1995 // Enforce user limit.
1996 // Don't enforce for users that have some admin right
1997 if(m_clients.size() >= g_settings->getU16("max_users") &&
1998 !checkPriv(playername, "server") &&
1999 !checkPriv(playername, "ban") &&
2000 !checkPriv(playername, "privs") &&
2001 !checkPriv(playername, "password") &&
2002 playername != g_settings->get("name"))
2004 actionstream<<"Server: "<<playername<<" tried to join, but there"
2005 <<" are already max_users="
2006 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2007 SendAccessDenied(m_con, peer_id, L"Too many users.");
2012 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2014 // If failed, cancel
2015 if(playersao == NULL)
2017 errorstream<<"Server: peer_id="<<peer_id
2018 <<": failed to emerge player"<<std::endl;
2023 Answer with a TOCLIENT_INIT
2026 SharedBuffer<u8> reply(2+1+6+8+4);
2027 writeU16(&reply[0], TOCLIENT_INIT);
2028 writeU8(&reply[2], deployed);
2029 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2030 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2031 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2034 m_con.Send(peer_id, 0, reply, true);
2038 Send complete position information
2040 SendMovePlayer(peer_id);
2045 if(command == TOSERVER_INIT2)
2047 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2048 <<peer_id<<std::endl;
2050 Player *player = m_env->getPlayer(peer_id);
2052 verbosestream<<"Server: TOSERVER_INIT2: "
2053 <<"Player not found; ignoring."<<std::endl;
2057 RemoteClient *client = getClient(peer_id);
2058 client->serialization_version =
2059 getClient(peer_id)->pending_serialization_version;
2062 Send some initialization data
2065 infostream<<"Server: Sending content to "
2066 <<getPlayerName(peer_id)<<std::endl;
2068 // Send player movement settings
2069 SendMovement(m_con, peer_id);
2071 // Send item definitions
2072 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2074 // Send node definitions
2075 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2077 // Send media announcement
2078 sendMediaAnnouncement(peer_id);
2081 SendPlayerPrivileges(peer_id);
2083 // Send inventory formspec
2084 SendPlayerInventoryFormspec(peer_id);
2087 UpdateCrafting(peer_id);
2088 SendInventory(peer_id);
2091 if(g_settings->getBool("enable_damage"))
2092 SendPlayerHP(peer_id);
2094 // Send detached inventories
2095 sendDetachedInventories(peer_id);
2097 // Show death screen if necessary
2099 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2103 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2104 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2105 m_con.Send(peer_id, 0, data, true);
2108 // Note things in chat if not in simple singleplayer mode
2109 if(!m_simple_singleplayer_mode)
2111 // Send information about server to player in chat
2112 SendChatMessage(peer_id, getStatusString());
2114 // Send information about joining in chat
2116 std::wstring name = L"unknown";
2117 Player *player = m_env->getPlayer(peer_id);
2119 name = narrow_to_wide(player->getName());
2121 std::wstring message;
2124 message += L" joined the game.";
2125 BroadcastChatMessage(message);
2129 // Warnings about protocol version can be issued here
2130 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2132 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2133 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2140 std::ostringstream os(std::ios_base::binary);
2141 for(std::map<u16, RemoteClient*>::iterator
2142 i = m_clients.begin();
2143 i != m_clients.end(); ++i)
2145 RemoteClient *client = i->second;
2146 assert(client->peer_id == i->first);
2147 if(client->serialization_version == SER_FMT_VER_INVALID)
2150 Player *player = m_env->getPlayer(client->peer_id);
2153 // Get name of player
2154 os<<player->getName()<<" ";
2157 actionstream<<player->getName()<<" ["<<addr_s<<"] "<<" joins game. List of players: "
2158 <<os.str()<<std::endl;
2164 if(peer_ser_ver == SER_FMT_VER_INVALID)
2166 infostream<<"Server::ProcessData(): Cancelling: Peer"
2167 " serialization format invalid or not initialized."
2168 " Skipping incoming command="<<command<<std::endl;
2172 Player *player = m_env->getPlayer(peer_id);
2174 infostream<<"Server::ProcessData(): Cancelling: "
2175 "No player for peer_id="<<peer_id
2180 PlayerSAO *playersao = player->getPlayerSAO();
2181 if(playersao == NULL){
2182 infostream<<"Server::ProcessData(): Cancelling: "
2183 "No player object for peer_id="<<peer_id
2188 if(command == TOSERVER_PLAYERPOS)
2190 if(datasize < 2+12+12+4+4)
2194 v3s32 ps = readV3S32(&data[start+2]);
2195 v3s32 ss = readV3S32(&data[start+2+12]);
2196 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2197 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2199 if(datasize >= 2+12+12+4+4+4)
2200 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2201 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2202 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2203 pitch = wrapDegrees(pitch);
2204 yaw = wrapDegrees(yaw);
2206 player->setPosition(position);
2207 player->setSpeed(speed);
2208 player->setPitch(pitch);
2209 player->setYaw(yaw);
2210 player->keyPressed=keyPressed;
2211 player->control.up = (bool)(keyPressed&1);
2212 player->control.down = (bool)(keyPressed&2);
2213 player->control.left = (bool)(keyPressed&4);
2214 player->control.right = (bool)(keyPressed&8);
2215 player->control.jump = (bool)(keyPressed&16);
2216 player->control.aux1 = (bool)(keyPressed&32);
2217 player->control.sneak = (bool)(keyPressed&64);
2218 player->control.LMB = (bool)(keyPressed&128);
2219 player->control.RMB = (bool)(keyPressed&256);
2221 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2222 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2223 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2225 else if(command == TOSERVER_GOTBLOCKS)
2238 u16 count = data[2];
2239 for(u16 i=0; i<count; i++)
2241 if((s16)datasize < 2+1+(i+1)*6)
2242 throw con::InvalidIncomingDataException
2243 ("GOTBLOCKS length is too short");
2244 v3s16 p = readV3S16(&data[2+1+i*6]);
2245 /*infostream<<"Server: GOTBLOCKS ("
2246 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2247 RemoteClient *client = getClient(peer_id);
2248 client->GotBlock(p);
2251 else if(command == TOSERVER_DELETEDBLOCKS)
2264 u16 count = data[2];
2265 for(u16 i=0; i<count; i++)
2267 if((s16)datasize < 2+1+(i+1)*6)
2268 throw con::InvalidIncomingDataException
2269 ("DELETEDBLOCKS length is too short");
2270 v3s16 p = readV3S16(&data[2+1+i*6]);
2271 /*infostream<<"Server: DELETEDBLOCKS ("
2272 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2273 RemoteClient *client = getClient(peer_id);
2274 client->SetBlockNotSent(p);
2277 else if(command == TOSERVER_CLICK_OBJECT)
2279 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2282 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2284 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2287 else if(command == TOSERVER_GROUND_ACTION)
2289 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2293 else if(command == TOSERVER_RELEASE)
2295 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2298 else if(command == TOSERVER_SIGNTEXT)
2300 infostream<<"Server: SIGNTEXT not supported anymore"
2304 else if(command == TOSERVER_SIGNNODETEXT)
2306 infostream<<"Server: SIGNNODETEXT not supported anymore"
2310 else if(command == TOSERVER_INVENTORY_ACTION)
2312 // Strip command and create a stream
2313 std::string datastring((char*)&data[2], datasize-2);
2314 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2315 std::istringstream is(datastring, std::ios_base::binary);
2317 InventoryAction *a = InventoryAction::deSerialize(is);
2320 infostream<<"TOSERVER_INVENTORY_ACTION: "
2321 <<"InventoryAction::deSerialize() returned NULL"
2326 // If something goes wrong, this player is to blame
2327 RollbackScopeActor rollback_scope(m_rollback,
2328 std::string("player:")+player->getName());
2331 Note: Always set inventory not sent, to repair cases
2332 where the client made a bad prediction.
2336 Handle restrictions and special cases of the move action
2338 if(a->getType() == IACTION_MOVE)
2340 IMoveAction *ma = (IMoveAction*)a;
2342 ma->from_inv.applyCurrentPlayer(player->getName());
2343 ma->to_inv.applyCurrentPlayer(player->getName());
2345 setInventoryModified(ma->from_inv);
2346 setInventoryModified(ma->to_inv);
2348 bool from_inv_is_current_player =
2349 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2350 (ma->from_inv.name == player->getName());
2352 bool to_inv_is_current_player =
2353 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2354 (ma->to_inv.name == player->getName());
2357 Disable moving items out of craftpreview
2359 if(ma->from_list == "craftpreview")
2361 infostream<<"Ignoring IMoveAction from "
2362 <<(ma->from_inv.dump())<<":"<<ma->from_list
2363 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2364 <<" because src is "<<ma->from_list<<std::endl;
2370 Disable moving items into craftresult and craftpreview
2372 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2374 infostream<<"Ignoring IMoveAction from "
2375 <<(ma->from_inv.dump())<<":"<<ma->from_list
2376 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2377 <<" because dst is "<<ma->to_list<<std::endl;
2382 // Disallow moving items in elsewhere than player's inventory
2383 // if not allowed to interact
2384 if(!checkPriv(player->getName(), "interact") &&
2385 (!from_inv_is_current_player ||
2386 !to_inv_is_current_player))
2388 infostream<<"Cannot move outside of player's inventory: "
2389 <<"No interact privilege"<<std::endl;
2395 Handle restrictions and special cases of the drop action
2397 else if(a->getType() == IACTION_DROP)
2399 IDropAction *da = (IDropAction*)a;
2401 da->from_inv.applyCurrentPlayer(player->getName());
2403 setInventoryModified(da->from_inv);
2406 Disable dropping items out of craftpreview
2408 if(da->from_list == "craftpreview")
2410 infostream<<"Ignoring IDropAction from "
2411 <<(da->from_inv.dump())<<":"<<da->from_list
2412 <<" because src is "<<da->from_list<<std::endl;
2417 // Disallow dropping items if not allowed to interact
2418 if(!checkPriv(player->getName(), "interact"))
2425 Handle restrictions and special cases of the craft action
2427 else if(a->getType() == IACTION_CRAFT)
2429 ICraftAction *ca = (ICraftAction*)a;
2431 ca->craft_inv.applyCurrentPlayer(player->getName());
2433 setInventoryModified(ca->craft_inv);
2435 //bool craft_inv_is_current_player =
2436 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2437 // (ca->craft_inv.name == player->getName());
2439 // Disallow crafting if not allowed to interact
2440 if(!checkPriv(player->getName(), "interact"))
2442 infostream<<"Cannot craft: "
2443 <<"No interact privilege"<<std::endl;
2450 a->apply(this, playersao, this);
2454 else if(command == TOSERVER_CHAT_MESSAGE)
2462 std::string datastring((char*)&data[2], datasize-2);
2463 std::istringstream is(datastring, std::ios_base::binary);
2466 is.read((char*)buf, 2);
2467 u16 len = readU16(buf);
2469 std::wstring message;
2470 for(u16 i=0; i<len; i++)
2472 is.read((char*)buf, 2);
2473 message += (wchar_t)readU16(buf);
2476 // If something goes wrong, this player is to blame
2477 RollbackScopeActor rollback_scope(m_rollback,
2478 std::string("player:")+player->getName());
2480 // Get player name of this client
2481 std::wstring name = narrow_to_wide(player->getName());
2484 bool ate = m_script->on_chat_message(player->getName(),
2485 wide_to_narrow(message));
2486 // If script ate the message, don't proceed
2490 // Line to send to players
2492 // Whether to send to the player that sent the line
2493 bool send_to_sender = false;
2494 // Whether to send to other players
2495 bool send_to_others = false;
2497 // Commands are implemented in Lua, so only catch invalid
2498 // commands that were not "eaten" and send an error back
2499 if(message[0] == L'/')
2501 message = message.substr(1);
2502 send_to_sender = true;
2503 if(message.length() == 0)
2504 line += L"-!- Empty command";
2506 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2510 if(checkPriv(player->getName(), "shout")){
2515 send_to_others = true;
2517 line += L"-!- You don't have permission to shout.";
2518 send_to_sender = true;
2525 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2528 Send the message to clients
2530 for(std::map<u16, RemoteClient*>::iterator
2531 i = m_clients.begin();
2532 i != m_clients.end(); ++i)
2534 // Get client and check that it is valid
2535 RemoteClient *client = i->second;
2536 assert(client->peer_id == i->first);
2537 if(client->serialization_version == SER_FMT_VER_INVALID)
2541 bool sender_selected = (peer_id == client->peer_id);
2542 if(sender_selected == true && send_to_sender == false)
2544 if(sender_selected == false && send_to_others == false)
2547 SendChatMessage(client->peer_id, line);
2551 else if(command == TOSERVER_DAMAGE)
2553 std::string datastring((char*)&data[2], datasize-2);
2554 std::istringstream is(datastring, std::ios_base::binary);
2555 u8 damage = readU8(is);
2557 if(g_settings->getBool("enable_damage"))
2559 actionstream<<player->getName()<<" damaged by "
2560 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2563 playersao->setHP(playersao->getHP() - damage);
2565 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2568 if(playersao->m_hp_not_sent)
2569 SendPlayerHP(peer_id);
2572 else if(command == TOSERVER_PASSWORD)
2575 [0] u16 TOSERVER_PASSWORD
2576 [2] u8[28] old password
2577 [30] u8[28] new password
2580 if(datasize != 2+PASSWORD_SIZE*2)
2582 /*char password[PASSWORD_SIZE];
2583 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2584 password[i] = data[2+i];
2585 password[PASSWORD_SIZE-1] = 0;*/
2587 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2595 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2597 char c = data[2+PASSWORD_SIZE+i];
2603 if(!base64_is_valid(newpwd)){
2604 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2605 // Wrong old password supplied!!
2606 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2610 infostream<<"Server: Client requests a password change from "
2611 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2613 std::string playername = player->getName();
2615 std::string checkpwd;
2616 m_script->getAuth(playername, &checkpwd, NULL);
2618 if(oldpwd != checkpwd)
2620 infostream<<"Server: invalid old password"<<std::endl;
2621 // Wrong old password supplied!!
2622 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2626 bool success = m_script->setPassword(playername, newpwd);
2628 actionstream<<player->getName()<<" changes password"<<std::endl;
2629 SendChatMessage(peer_id, L"Password change successful.");
2631 actionstream<<player->getName()<<" tries to change password but "
2632 <<"it fails"<<std::endl;
2633 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2636 else if(command == TOSERVER_PLAYERITEM)
2641 u16 item = readU16(&data[2]);
2642 playersao->setWieldIndex(item);
2644 else if(command == TOSERVER_RESPAWN)
2646 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2649 RespawnPlayer(peer_id);
2651 actionstream<<player->getName()<<" respawns at "
2652 <<PP(player->getPosition()/BS)<<std::endl;
2654 // ActiveObject is added to environment in AsyncRunStep after
2655 // the previous addition has been succesfully removed
2657 else if(command == TOSERVER_REQUEST_MEDIA) {
2658 std::string datastring((char*)&data[2], datasize-2);
2659 std::istringstream is(datastring, std::ios_base::binary);
2661 std::list<MediaRequest> tosend;
2662 u16 numfiles = readU16(is);
2664 infostream<<"Sending "<<numfiles<<" files to "
2665 <<getPlayerName(peer_id)<<std::endl;
2666 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2668 for(int i = 0; i < numfiles; i++) {
2669 std::string name = deSerializeString(is);
2670 tosend.push_back(MediaRequest(name));
2671 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2675 sendRequestedMedia(peer_id, tosend);
2677 // Now the client should know about everything
2678 // (definitions and files)
2679 getClient(peer_id)->definitions_sent = true;
2681 else if(command == TOSERVER_RECEIVED_MEDIA) {
2682 getClient(peer_id)->definitions_sent = true;
2684 else if(command == TOSERVER_INTERACT)
2686 std::string datastring((char*)&data[2], datasize-2);
2687 std::istringstream is(datastring, std::ios_base::binary);
2693 [5] u32 length of the next item
2694 [9] serialized PointedThing
2696 0: start digging (from undersurface) or use
2697 1: stop digging (all parameters ignored)
2698 2: digging completed
2699 3: place block or item (to abovesurface)
2702 u8 action = readU8(is);
2703 u16 item_i = readU16(is);
2704 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2705 PointedThing pointed;
2706 pointed.deSerialize(tmp_is);
2708 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2709 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2713 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2714 <<" tried to interact, but is dead!"<<std::endl;
2718 v3f player_pos = playersao->getLastGoodPosition();
2720 // Update wielded item
2721 playersao->setWieldIndex(item_i);
2723 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2724 v3s16 p_under = pointed.node_undersurface;
2725 v3s16 p_above = pointed.node_abovesurface;
2727 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2728 ServerActiveObject *pointed_object = NULL;
2729 if(pointed.type == POINTEDTHING_OBJECT)
2731 pointed_object = m_env->getActiveObject(pointed.object_id);
2732 if(pointed_object == NULL)
2734 verbosestream<<"TOSERVER_INTERACT: "
2735 "pointed object is NULL"<<std::endl;
2741 v3f pointed_pos_under = player_pos;
2742 v3f pointed_pos_above = player_pos;
2743 if(pointed.type == POINTEDTHING_NODE)
2745 pointed_pos_under = intToFloat(p_under, BS);
2746 pointed_pos_above = intToFloat(p_above, BS);
2748 else if(pointed.type == POINTEDTHING_OBJECT)
2750 pointed_pos_under = pointed_object->getBasePosition();
2751 pointed_pos_above = pointed_pos_under;
2755 Check that target is reasonably close
2756 (only when digging or placing things)
2758 if(action == 0 || action == 2 || action == 3)
2760 float d = player_pos.getDistanceFrom(pointed_pos_under);
2761 float max_d = BS * 14; // Just some large enough value
2763 actionstream<<"Player "<<player->getName()
2764 <<" tried to access "<<pointed.dump()
2766 <<"d="<<d<<", max_d="<<max_d
2767 <<". ignoring."<<std::endl;
2768 // Re-send block to revert change on client-side
2769 RemoteClient *client = getClient(peer_id);
2770 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2771 client->SetBlockNotSent(blockpos);
2778 Make sure the player is allowed to do it
2780 if(!checkPriv(player->getName(), "interact"))
2782 actionstream<<player->getName()<<" attempted to interact with "
2783 <<pointed.dump()<<" without 'interact' privilege"
2785 // Re-send block to revert change on client-side
2786 RemoteClient *client = getClient(peer_id);
2787 // Digging completed -> under
2789 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2790 client->SetBlockNotSent(blockpos);
2792 // Placement -> above
2794 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2795 client->SetBlockNotSent(blockpos);
2801 If something goes wrong, this player is to blame
2803 RollbackScopeActor rollback_scope(m_rollback,
2804 std::string("player:")+player->getName());
2807 0: start digging or punch object
2811 if(pointed.type == POINTEDTHING_NODE)
2814 NOTE: This can be used in the future to check if
2815 somebody is cheating, by checking the timing.
2817 MapNode n(CONTENT_IGNORE);
2820 n = m_env->getMap().getNode(p_under);
2822 catch(InvalidPositionException &e)
2824 infostream<<"Server: Not punching: Node not found."
2825 <<" Adding block to emerge queue."
2827 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2829 if(n.getContent() != CONTENT_IGNORE)
2830 m_script->node_on_punch(p_under, n, playersao);
2832 playersao->noCheatDigStart(p_under);
2834 else if(pointed.type == POINTEDTHING_OBJECT)
2836 // Skip if object has been removed
2837 if(pointed_object->m_removed)
2840 actionstream<<player->getName()<<" punches object "
2841 <<pointed.object_id<<": "
2842 <<pointed_object->getDescription()<<std::endl;
2844 ItemStack punchitem = playersao->getWieldedItem();
2845 ToolCapabilities toolcap =
2846 punchitem.getToolCapabilities(m_itemdef);
2847 v3f dir = (pointed_object->getBasePosition() -
2848 (player->getPosition() + player->getEyeOffset())
2850 float time_from_last_punch =
2851 playersao->resetTimeFromLastPunch();
2852 pointed_object->punch(dir, &toolcap, playersao,
2853 time_from_last_punch);
2861 else if(action == 1)
2866 2: Digging completed
2868 else if(action == 2)
2870 // Only digging of nodes
2871 if(pointed.type == POINTEDTHING_NODE)
2873 MapNode n(CONTENT_IGNORE);
2876 n = m_env->getMap().getNode(p_under);
2878 catch(InvalidPositionException &e)
2880 infostream<<"Server: Not finishing digging: Node not found."
2881 <<" Adding block to emerge queue."
2883 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2886 /* Cheat prevention */
2887 bool is_valid_dig = true;
2888 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2890 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2891 float nocheat_t = playersao->getNoCheatDigTime();
2892 playersao->noCheatDigEnd();
2893 // If player didn't start digging this, ignore dig
2894 if(nocheat_p != p_under){
2895 infostream<<"Server: NoCheat: "<<player->getName()
2896 <<" started digging "
2897 <<PP(nocheat_p)<<" and completed digging "
2898 <<PP(p_under)<<"; not digging."<<std::endl;
2899 is_valid_dig = false;
2901 // Get player's wielded item
2902 ItemStack playeritem;
2903 InventoryList *mlist = playersao->getInventory()->getList("main");
2905 playeritem = mlist->getItem(playersao->getWieldIndex());
2906 ToolCapabilities playeritem_toolcap =
2907 playeritem.getToolCapabilities(m_itemdef);
2908 // Get diggability and expected digging time
2909 DigParams params = getDigParams(m_nodedef->get(n).groups,
2910 &playeritem_toolcap);
2911 // If can't dig, try hand
2912 if(!params.diggable){
2913 const ItemDefinition &hand = m_itemdef->get("");
2914 const ToolCapabilities *tp = hand.tool_capabilities;
2916 params = getDigParams(m_nodedef->get(n).groups, tp);
2918 // If can't dig, ignore dig
2919 if(!params.diggable){
2920 infostream<<"Server: NoCheat: "<<player->getName()
2921 <<" completed digging "<<PP(p_under)
2922 <<", which is not diggable with tool. not digging."
2924 is_valid_dig = false;
2926 // If time is considerably too short, ignore dig
2927 // Check time only for medium and slow timed digs
2928 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2929 infostream<<"Server: NoCheat: "<<player->getName()
2930 <<" completed digging "
2931 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2932 <<params.time<<"s; not digging."<<std::endl;
2933 is_valid_dig = false;
2937 /* Actually dig node */
2939 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2940 m_script->node_on_dig(p_under, n, playersao);
2942 // Send unusual result (that is, node not being removed)
2943 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2945 // Re-send block to revert change on client-side
2946 RemoteClient *client = getClient(peer_id);
2947 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2948 client->SetBlockNotSent(blockpos);
2954 3: place block or right-click object
2956 else if(action == 3)
2958 ItemStack item = playersao->getWieldedItem();
2960 // Reset build time counter
2961 if(pointed.type == POINTEDTHING_NODE &&
2962 item.getDefinition(m_itemdef).type == ITEM_NODE)
2963 getClient(peer_id)->m_time_from_building = 0.0;
2965 if(pointed.type == POINTEDTHING_OBJECT)
2967 // Right click object
2969 // Skip if object has been removed
2970 if(pointed_object->m_removed)
2973 actionstream<<player->getName()<<" right-clicks object "
2974 <<pointed.object_id<<": "
2975 <<pointed_object->getDescription()<<std::endl;
2978 pointed_object->rightClick(playersao);
2980 else if(m_script->item_OnPlace(
2981 item, playersao, pointed))
2983 // Placement was handled in lua
2985 // Apply returned ItemStack
2986 playersao->setWieldedItem(item);
2989 // If item has node placement prediction, always send the
2990 // blocks to make sure the client knows what exactly happened
2991 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2992 RemoteClient *client = getClient(peer_id);
2993 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2994 client->SetBlockNotSent(blockpos);
2995 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2996 if(blockpos2 != blockpos){
2997 client->SetBlockNotSent(blockpos2);
3005 else if(action == 4)
3007 ItemStack item = playersao->getWieldedItem();
3009 actionstream<<player->getName()<<" uses "<<item.name
3010 <<", pointing at "<<pointed.dump()<<std::endl;
3012 if(m_script->item_OnUse(
3013 item, playersao, pointed))
3015 // Apply returned ItemStack
3016 playersao->setWieldedItem(item);
3023 Catch invalid actions
3027 infostream<<"WARNING: Server: Invalid action "
3028 <<action<<std::endl;
3031 else if(command == TOSERVER_REMOVED_SOUNDS)
3033 std::string datastring((char*)&data[2], datasize-2);
3034 std::istringstream is(datastring, std::ios_base::binary);
3036 int num = readU16(is);
3037 for(int k=0; k<num; k++){
3038 s32 id = readS32(is);
3039 std::map<s32, ServerPlayingSound>::iterator i =
3040 m_playing_sounds.find(id);
3041 if(i == m_playing_sounds.end())
3043 ServerPlayingSound &psound = i->second;
3044 psound.clients.erase(peer_id);
3045 if(psound.clients.size() == 0)
3046 m_playing_sounds.erase(i++);
3049 else if(command == TOSERVER_NODEMETA_FIELDS)
3051 std::string datastring((char*)&data[2], datasize-2);
3052 std::istringstream is(datastring, std::ios_base::binary);
3054 v3s16 p = readV3S16(is);
3055 std::string formname = deSerializeString(is);
3056 int num = readU16(is);
3057 std::map<std::string, std::string> fields;
3058 for(int k=0; k<num; k++){
3059 std::string fieldname = deSerializeString(is);
3060 std::string fieldvalue = deSerializeLongString(is);
3061 fields[fieldname] = fieldvalue;
3064 // If something goes wrong, this player is to blame
3065 RollbackScopeActor rollback_scope(m_rollback,
3066 std::string("player:")+player->getName());
3068 // Check the target node for rollback data; leave others unnoticed
3069 RollbackNode rn_old(&m_env->getMap(), p, this);
3071 m_script->node_on_receive_fields(p, formname, fields,playersao);
3073 // Report rollback data
3074 RollbackNode rn_new(&m_env->getMap(), p, this);
3075 if(rollback() && rn_new != rn_old){
3076 RollbackAction action;
3077 action.setSetNode(p, rn_old, rn_new);
3078 rollback()->reportAction(action);
3081 else if(command == TOSERVER_INVENTORY_FIELDS)
3083 std::string datastring((char*)&data[2], datasize-2);
3084 std::istringstream is(datastring, std::ios_base::binary);
3086 std::string formname = deSerializeString(is);
3087 int num = readU16(is);
3088 std::map<std::string, std::string> fields;
3089 for(int k=0; k<num; k++){
3090 std::string fieldname = deSerializeString(is);
3091 std::string fieldvalue = deSerializeLongString(is);
3092 fields[fieldname] = fieldvalue;
3095 m_script->on_playerReceiveFields(playersao, formname, fields);
3099 infostream<<"Server::ProcessData(): Ignoring "
3100 "unknown command "<<command<<std::endl;
3104 catch(SendFailedException &e)
3106 errorstream<<"Server::ProcessData(): SendFailedException: "
3112 void Server::onMapEditEvent(MapEditEvent *event)
3114 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3115 if(m_ignore_map_edit_events)
3117 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3119 MapEditEvent *e = event->clone();
3120 m_unsent_map_edit_queue.push_back(e);
3123 Inventory* Server::getInventory(const InventoryLocation &loc)
3126 case InventoryLocation::UNDEFINED:
3129 case InventoryLocation::CURRENT_PLAYER:
3132 case InventoryLocation::PLAYER:
3134 Player *player = m_env->getPlayer(loc.name.c_str());
3137 PlayerSAO *playersao = player->getPlayerSAO();
3140 return playersao->getInventory();
3143 case InventoryLocation::NODEMETA:
3145 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3148 return meta->getInventory();
3151 case InventoryLocation::DETACHED:
3153 if(m_detached_inventories.count(loc.name) == 0)
3155 return m_detached_inventories[loc.name];
3163 void Server::setInventoryModified(const InventoryLocation &loc)
3166 case InventoryLocation::UNDEFINED:
3169 case InventoryLocation::PLAYER:
3171 Player *player = m_env->getPlayer(loc.name.c_str());
3174 PlayerSAO *playersao = player->getPlayerSAO();
3177 playersao->m_inventory_not_sent = true;
3178 playersao->m_wielded_item_not_sent = true;
3181 case InventoryLocation::NODEMETA:
3183 v3s16 blockpos = getNodeBlockPos(loc.p);
3185 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3187 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3189 setBlockNotSent(blockpos);
3192 case InventoryLocation::DETACHED:
3194 sendDetachedInventoryToAll(loc.name);
3202 //std::list<PlayerInfo> Server::getPlayerInfo()
3204 // DSTACK(__FUNCTION_NAME);
3205 // JMutexAutoLock envlock(m_env_mutex);
3206 // JMutexAutoLock conlock(m_con_mutex);
3208 // std::list<PlayerInfo> list;
3210 // std::list<Player*> players = m_env->getPlayers();
3212 // std::list<Player*>::iterator i;
3213 // for(i = players.begin();
3214 // i != players.end(); ++i)
3218 // Player *player = *i;
3221 // // Copy info from connection to info struct
3222 // info.id = player->peer_id;
3223 // info.address = m_con.GetPeerAddress(player->peer_id);
3224 // info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3226 // catch(con::PeerNotFoundException &e)
3228 // // Set dummy peer info
3230 // info.address = Address(0,0,0,0,0);
3231 // info.avg_rtt = 0.0;
3234 // snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3235 // info.position = player->getPosition();
3237 // list.push_back(info);
3244 void Server::peerAdded(con::Peer *peer)
3246 DSTACK(__FUNCTION_NAME);
3247 verbosestream<<"Server::peerAdded(): peer->id="
3248 <<peer->id<<std::endl;
3251 c.type = PEER_ADDED;
3252 c.peer_id = peer->id;
3254 m_peer_change_queue.push_back(c);
3257 void Server::deletingPeer(con::Peer *peer, bool timeout)
3259 DSTACK(__FUNCTION_NAME);
3260 verbosestream<<"Server::deletingPeer(): peer->id="
3261 <<peer->id<<", timeout="<<timeout<<std::endl;
3264 c.type = PEER_REMOVED;
3265 c.peer_id = peer->id;
3266 c.timeout = timeout;
3267 m_peer_change_queue.push_back(c);
3274 void Server::SendMovement(con::Connection &con, u16 peer_id)
3276 DSTACK(__FUNCTION_NAME);
3277 std::ostringstream os(std::ios_base::binary);
3279 writeU16(os, TOCLIENT_MOVEMENT);
3280 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3281 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3282 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3283 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3284 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3285 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3286 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3287 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3288 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3289 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3290 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3291 writeF1000(os, g_settings->getFloat("movement_gravity"));
3294 std::string s = os.str();
3295 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3297 con.Send(peer_id, 0, data, true);
3300 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3302 DSTACK(__FUNCTION_NAME);
3303 std::ostringstream os(std::ios_base::binary);
3305 writeU16(os, TOCLIENT_HP);
3309 std::string s = os.str();
3310 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3312 con.Send(peer_id, 0, data, true);
3315 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3316 const std::wstring &reason)
3318 DSTACK(__FUNCTION_NAME);
3319 std::ostringstream os(std::ios_base::binary);
3321 writeU16(os, TOCLIENT_ACCESS_DENIED);
3322 os<<serializeWideString(reason);
3325 std::string s = os.str();
3326 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3328 con.Send(peer_id, 0, data, true);
3331 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3332 bool set_camera_point_target, v3f camera_point_target)
3334 DSTACK(__FUNCTION_NAME);
3335 std::ostringstream os(std::ios_base::binary);
3337 writeU16(os, TOCLIENT_DEATHSCREEN);
3338 writeU8(os, set_camera_point_target);
3339 writeV3F1000(os, camera_point_target);
3342 std::string s = os.str();
3343 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3345 con.Send(peer_id, 0, data, true);
3348 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3349 IItemDefManager *itemdef, u16 protocol_version)
3351 DSTACK(__FUNCTION_NAME);
3352 std::ostringstream os(std::ios_base::binary);
3356 u32 length of the next item
3357 zlib-compressed serialized ItemDefManager
3359 writeU16(os, TOCLIENT_ITEMDEF);
3360 std::ostringstream tmp_os(std::ios::binary);
3361 itemdef->serialize(tmp_os, protocol_version);
3362 std::ostringstream tmp_os2(std::ios::binary);
3363 compressZlib(tmp_os.str(), tmp_os2);
3364 os<<serializeLongString(tmp_os2.str());
3367 std::string s = os.str();
3368 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3369 <<"): size="<<s.size()<<std::endl;
3370 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3372 con.Send(peer_id, 0, data, true);
3375 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3376 INodeDefManager *nodedef, u16 protocol_version)
3378 DSTACK(__FUNCTION_NAME);
3379 std::ostringstream os(std::ios_base::binary);
3383 u32 length of the next item
3384 zlib-compressed serialized NodeDefManager
3386 writeU16(os, TOCLIENT_NODEDEF);
3387 std::ostringstream tmp_os(std::ios::binary);
3388 nodedef->serialize(tmp_os, protocol_version);
3389 std::ostringstream tmp_os2(std::ios::binary);
3390 compressZlib(tmp_os.str(), tmp_os2);
3391 os<<serializeLongString(tmp_os2.str());
3394 std::string s = os.str();
3395 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3396 <<"): size="<<s.size()<<std::endl;
3397 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3399 con.Send(peer_id, 0, data, true);
3403 Non-static send methods
3406 void Server::SendInventory(u16 peer_id)
3408 DSTACK(__FUNCTION_NAME);
3410 PlayerSAO *playersao = getPlayerSAO(peer_id);
3413 playersao->m_inventory_not_sent = false;
3419 std::ostringstream os;
3420 playersao->getInventory()->serialize(os);
3422 std::string s = os.str();
3424 SharedBuffer<u8> data(s.size()+2);
3425 writeU16(&data[0], TOCLIENT_INVENTORY);
3426 memcpy(&data[2], s.c_str(), s.size());
3429 m_con.Send(peer_id, 0, data, true);
3432 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3434 DSTACK(__FUNCTION_NAME);
3436 std::ostringstream os(std::ios_base::binary);
3440 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3441 os.write((char*)buf, 2);
3444 writeU16(buf, message.size());
3445 os.write((char*)buf, 2);
3448 for(u32 i=0; i<message.size(); i++)
3452 os.write((char*)buf, 2);
3456 std::string s = os.str();
3457 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3459 m_con.Send(peer_id, 0, data, true);
3462 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3463 const std::string formname)
3465 DSTACK(__FUNCTION_NAME);
3467 std::ostringstream os(std::ios_base::binary);
3471 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3472 os.write((char*)buf, 2);
3473 os<<serializeLongString(formspec);
3474 os<<serializeString(formname);
3477 std::string s = os.str();
3478 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3480 m_con.Send(peer_id, 0, data, true);
3483 // Spawns a particle on peer with peer_id
3484 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3485 float expirationtime, float size, bool collisiondetection,
3486 std::string texture)
3488 DSTACK(__FUNCTION_NAME);
3490 std::ostringstream os(std::ios_base::binary);
3491 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3492 writeV3F1000(os, pos);
3493 writeV3F1000(os, velocity);
3494 writeV3F1000(os, acceleration);
3495 writeF1000(os, expirationtime);
3496 writeF1000(os, size);
3497 writeU8(os, collisiondetection);
3498 os<<serializeLongString(texture);
3501 std::string s = os.str();
3502 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3504 m_con.Send(peer_id, 0, data, true);
3507 // Spawns a particle on all peers
3508 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
3509 float expirationtime, float size, bool collisiondetection,
3510 std::string texture)
3512 for(std::map<u16, RemoteClient*>::iterator
3513 i = m_clients.begin();
3514 i != m_clients.end(); i++)
3516 // Get client and check that it is valid
3517 RemoteClient *client = i->second;
3518 assert(client->peer_id == i->first);
3519 if(client->serialization_version == SER_FMT_VER_INVALID)
3522 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3523 expirationtime, size, collisiondetection, texture);
3527 // Adds a ParticleSpawner on peer with peer_id
3528 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3529 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3530 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3532 DSTACK(__FUNCTION_NAME);
3534 std::ostringstream os(std::ios_base::binary);
3535 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3537 writeU16(os, amount);
3538 writeF1000(os, spawntime);
3539 writeV3F1000(os, minpos);
3540 writeV3F1000(os, maxpos);
3541 writeV3F1000(os, minvel);
3542 writeV3F1000(os, maxvel);
3543 writeV3F1000(os, minacc);
3544 writeV3F1000(os, maxacc);
3545 writeF1000(os, minexptime);
3546 writeF1000(os, maxexptime);
3547 writeF1000(os, minsize);
3548 writeF1000(os, maxsize);
3549 writeU8(os, collisiondetection);
3550 os<<serializeLongString(texture);
3554 std::string s = os.str();
3555 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3557 m_con.Send(peer_id, 0, data, true);
3560 // Adds a ParticleSpawner on all peers
3561 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3562 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3563 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3565 for(std::map<u16, RemoteClient*>::iterator
3566 i = m_clients.begin();
3567 i != m_clients.end(); i++)
3569 // Get client and check that it is valid
3570 RemoteClient *client = i->second;
3571 assert(client->peer_id == i->first);
3572 if(client->serialization_version == SER_FMT_VER_INVALID)
3575 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3576 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3577 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3581 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3583 DSTACK(__FUNCTION_NAME);
3585 std::ostringstream os(std::ios_base::binary);
3586 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3591 std::string s = os.str();
3592 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3594 m_con.Send(peer_id, 0, data, true);
3597 void Server::SendDeleteParticleSpawnerAll(u32 id)
3599 for(std::map<u16, RemoteClient*>::iterator
3600 i = m_clients.begin();
3601 i != m_clients.end(); i++)
3603 // Get client and check that it is valid
3604 RemoteClient *client = i->second;
3605 assert(client->peer_id == i->first);
3606 if(client->serialization_version == SER_FMT_VER_INVALID)
3609 SendDeleteParticleSpawner(client->peer_id, id);
3613 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3615 std::ostringstream os(std::ios_base::binary);
3618 writeU16(os, TOCLIENT_HUDADD);
3620 writeU8(os, (u8)form->type);
3621 writeV2F1000(os, form->pos);
3622 os << serializeString(form->name);
3623 writeV2F1000(os, form->scale);
3624 os << serializeString(form->text);
3625 writeU32(os, form->number);
3626 writeU32(os, form->item);
3627 writeU32(os, form->dir);
3628 writeV2F1000(os, form->align);
3629 writeV2F1000(os, form->offset);
3632 std::string s = os.str();
3633 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3635 m_con.Send(peer_id, 0, data, true);
3638 void Server::SendHUDRemove(u16 peer_id, u32 id)
3640 std::ostringstream os(std::ios_base::binary);
3643 writeU16(os, TOCLIENT_HUDRM);
3647 std::string s = os.str();
3648 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3650 m_con.Send(peer_id, 0, data, true);
3653 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3655 std::ostringstream os(std::ios_base::binary);
3658 writeU16(os, TOCLIENT_HUDCHANGE);
3660 writeU8(os, (u8)stat);
3663 case HUD_STAT_SCALE:
3664 case HUD_STAT_ALIGN:
3665 case HUD_STAT_OFFSET:
3666 writeV2F1000(os, *(v2f *)value);
3670 os << serializeString(*(std::string *)value);
3672 case HUD_STAT_NUMBER:
3676 writeU32(os, *(u32 *)value);
3681 std::string s = os.str();
3682 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3684 m_con.Send(peer_id, 0, data, true);
3687 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3689 std::ostringstream os(std::ios_base::binary);
3692 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3693 writeU32(os, flags);
3697 std::string s = os.str();
3698 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3700 m_con.Send(peer_id, 0, data, true);
3703 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3705 std::ostringstream os(std::ios_base::binary);
3708 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3709 writeU16(os, param);
3710 os<<serializeString(value);
3713 std::string s = os.str();
3714 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3716 m_con.Send(peer_id, 0, data, true);
3719 void Server::BroadcastChatMessage(const std::wstring &message)
3721 for(std::map<u16, RemoteClient*>::iterator
3722 i = m_clients.begin();
3723 i != m_clients.end(); ++i)
3725 // Get client and check that it is valid
3726 RemoteClient *client = i->second;
3727 assert(client->peer_id == i->first);
3728 if(client->serialization_version == SER_FMT_VER_INVALID)
3731 SendChatMessage(client->peer_id, message);
3735 void Server::SendPlayerHP(u16 peer_id)
3737 DSTACK(__FUNCTION_NAME);
3738 PlayerSAO *playersao = getPlayerSAO(peer_id);
3740 playersao->m_hp_not_sent = false;
3741 SendHP(m_con, peer_id, playersao->getHP());
3744 void Server::SendMovePlayer(u16 peer_id)
3746 DSTACK(__FUNCTION_NAME);
3747 Player *player = m_env->getPlayer(peer_id);
3750 std::ostringstream os(std::ios_base::binary);
3751 writeU16(os, TOCLIENT_MOVE_PLAYER);
3752 writeV3F1000(os, player->getPosition());
3753 writeF1000(os, player->getPitch());
3754 writeF1000(os, player->getYaw());
3757 v3f pos = player->getPosition();
3758 f32 pitch = player->getPitch();
3759 f32 yaw = player->getYaw();
3760 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3761 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3768 std::string s = os.str();
3769 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3771 m_con.Send(peer_id, 0, data, true);
3774 void Server::SendPlayerPrivileges(u16 peer_id)
3776 Player *player = m_env->getPlayer(peer_id);
3778 if(player->peer_id == PEER_ID_INEXISTENT)
3781 std::set<std::string> privs;
3782 m_script->getAuth(player->getName(), NULL, &privs);
3784 std::ostringstream os(std::ios_base::binary);
3785 writeU16(os, TOCLIENT_PRIVILEGES);
3786 writeU16(os, privs.size());
3787 for(std::set<std::string>::const_iterator i = privs.begin();
3788 i != privs.end(); i++){
3789 os<<serializeString(*i);
3793 std::string s = os.str();
3794 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3796 m_con.Send(peer_id, 0, data, true);
3799 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3801 Player *player = m_env->getPlayer(peer_id);
3803 if(player->peer_id == PEER_ID_INEXISTENT)
3806 std::ostringstream os(std::ios_base::binary);
3807 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3808 os<<serializeLongString(player->inventory_formspec);
3811 std::string s = os.str();
3812 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3814 m_con.Send(peer_id, 0, data, true);
3817 s32 Server::playSound(const SimpleSoundSpec &spec,
3818 const ServerSoundParams ¶ms)
3820 // Find out initial position of sound
3821 bool pos_exists = false;
3822 v3f pos = params.getPos(m_env, &pos_exists);
3823 // If position is not found while it should be, cancel sound
3824 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3826 // Filter destination clients
3827 std::set<RemoteClient*> dst_clients;
3828 if(params.to_player != "")
3830 Player *player = m_env->getPlayer(params.to_player.c_str());
3832 infostream<<"Server::playSound: Player \""<<params.to_player
3833 <<"\" not found"<<std::endl;
3836 if(player->peer_id == PEER_ID_INEXISTENT){
3837 infostream<<"Server::playSound: Player \""<<params.to_player
3838 <<"\" not connected"<<std::endl;
3841 RemoteClient *client = getClient(player->peer_id);
3842 dst_clients.insert(client);
3846 for(std::map<u16, RemoteClient*>::iterator
3847 i = m_clients.begin(); i != m_clients.end(); ++i)
3849 RemoteClient *client = i->second;
3850 Player *player = m_env->getPlayer(client->peer_id);
3854 if(player->getPosition().getDistanceFrom(pos) >
3855 params.max_hear_distance)
3858 dst_clients.insert(client);
3861 if(dst_clients.size() == 0)
3864 s32 id = m_next_sound_id++;
3865 // The sound will exist as a reference in m_playing_sounds
3866 m_playing_sounds[id] = ServerPlayingSound();
3867 ServerPlayingSound &psound = m_playing_sounds[id];
3868 psound.params = params;
3869 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3870 i != dst_clients.end(); i++)
3871 psound.clients.insert((*i)->peer_id);
3873 std::ostringstream os(std::ios_base::binary);
3874 writeU16(os, TOCLIENT_PLAY_SOUND);
3876 os<<serializeString(spec.name);
3877 writeF1000(os, spec.gain * params.gain);
3878 writeU8(os, params.type);
3879 writeV3F1000(os, pos);
3880 writeU16(os, params.object);
3881 writeU8(os, params.loop);
3883 std::string s = os.str();
3884 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3886 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3887 i != dst_clients.end(); i++){
3889 m_con.Send((*i)->peer_id, 0, data, true);
3893 void Server::stopSound(s32 handle)
3895 // Get sound reference
3896 std::map<s32, ServerPlayingSound>::iterator i =
3897 m_playing_sounds.find(handle);
3898 if(i == m_playing_sounds.end())
3900 ServerPlayingSound &psound = i->second;
3902 std::ostringstream os(std::ios_base::binary);
3903 writeU16(os, TOCLIENT_STOP_SOUND);
3904 writeS32(os, handle);
3906 std::string s = os.str();
3907 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3909 for(std::set<u16>::iterator i = psound.clients.begin();
3910 i != psound.clients.end(); i++){
3912 m_con.Send(*i, 0, data, true);
3914 // Remove sound reference
3915 m_playing_sounds.erase(i);
3918 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3919 std::list<u16> *far_players, float far_d_nodes)
3921 float maxd = far_d_nodes*BS;
3922 v3f p_f = intToFloat(p, BS);
3926 SharedBuffer<u8> reply(replysize);
3927 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3928 writeS16(&reply[2], p.X);
3929 writeS16(&reply[4], p.Y);
3930 writeS16(&reply[6], p.Z);
3932 for(std::map<u16, RemoteClient*>::iterator
3933 i = m_clients.begin();
3934 i != m_clients.end(); ++i)
3936 // Get client and check that it is valid
3937 RemoteClient *client = i->second;
3938 assert(client->peer_id == i->first);
3939 if(client->serialization_version == SER_FMT_VER_INVALID)
3942 // Don't send if it's the same one
3943 if(client->peer_id == ignore_id)
3949 Player *player = m_env->getPlayer(client->peer_id);
3952 // If player is far away, only set modified blocks not sent
3953 v3f player_pos = player->getPosition();
3954 if(player_pos.getDistanceFrom(p_f) > maxd)
3956 far_players->push_back(client->peer_id);
3963 m_con.Send(client->peer_id, 0, reply, true);
3967 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3968 std::list<u16> *far_players, float far_d_nodes)
3970 float maxd = far_d_nodes*BS;
3971 v3f p_f = intToFloat(p, BS);
3973 for(std::map<u16, RemoteClient*>::iterator
3974 i = m_clients.begin();
3975 i != m_clients.end(); ++i)
3977 // Get client and check that it is valid
3978 RemoteClient *client = i->second;
3979 assert(client->peer_id == i->first);
3980 if(client->serialization_version == SER_FMT_VER_INVALID)
3983 // Don't send if it's the same one
3984 if(client->peer_id == ignore_id)
3990 Player *player = m_env->getPlayer(client->peer_id);
3993 // If player is far away, only set modified blocks not sent
3994 v3f player_pos = player->getPosition();
3995 if(player_pos.getDistanceFrom(p_f) > maxd)
3997 far_players->push_back(client->peer_id);
4004 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4005 SharedBuffer<u8> reply(replysize);
4006 writeU16(&reply[0], TOCLIENT_ADDNODE);
4007 writeS16(&reply[2], p.X);
4008 writeS16(&reply[4], p.Y);
4009 writeS16(&reply[6], p.Z);
4010 n.serialize(&reply[8], client->serialization_version);
4013 m_con.Send(client->peer_id, 0, reply, true);
4017 void Server::setBlockNotSent(v3s16 p)
4019 for(std::map<u16, RemoteClient*>::iterator
4020 i = m_clients.begin();
4021 i != m_clients.end(); ++i)
4023 RemoteClient *client = i->second;
4024 client->SetBlockNotSent(p);
4028 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4030 DSTACK(__FUNCTION_NAME);
4032 v3s16 p = block->getPos();
4036 bool completely_air = true;
4037 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4038 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4039 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4041 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4043 completely_air = false;
4044 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4049 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4051 infostream<<"[completely air] ";
4052 infostream<<std::endl;
4056 Create a packet with the block in the right format
4059 std::ostringstream os(std::ios_base::binary);
4060 block->serialize(os, ver, false);
4061 std::string s = os.str();
4062 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4064 u32 replysize = 8 + blockdata.getSize();
4065 SharedBuffer<u8> reply(replysize);
4066 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4067 writeS16(&reply[2], p.X);
4068 writeS16(&reply[4], p.Y);
4069 writeS16(&reply[6], p.Z);
4070 memcpy(&reply[8], *blockdata, blockdata.getSize());
4072 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4073 <<": \tpacket size: "<<replysize<<std::endl;*/
4078 m_con.Send(peer_id, 1, reply, true);
4081 void Server::SendBlocks(float dtime)
4083 DSTACK(__FUNCTION_NAME);
4085 JMutexAutoLock envlock(m_env_mutex);
4086 JMutexAutoLock conlock(m_con_mutex);
4088 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4090 std::vector<PrioritySortedBlockTransfer> queue;
4092 s32 total_sending = 0;
4095 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4097 for(std::map<u16, RemoteClient*>::iterator
4098 i = m_clients.begin();
4099 i != m_clients.end(); ++i)
4101 RemoteClient *client = i->second;
4102 assert(client->peer_id == i->first);
4104 // If definitions and textures have not been sent, don't
4105 // send MapBlocks either
4106 if(!client->definitions_sent)
4109 total_sending += client->SendingCount();
4111 if(client->serialization_version == SER_FMT_VER_INVALID)
4114 client->GetNextBlocks(this, dtime, queue);
4119 // Lowest priority number comes first.
4120 // Lowest is most important.
4121 std::sort(queue.begin(), queue.end());
4123 for(u32 i=0; i<queue.size(); i++)
4125 //TODO: Calculate limit dynamically
4126 if(total_sending >= g_settings->getS32
4127 ("max_simultaneous_block_sends_server_total"))
4130 PrioritySortedBlockTransfer q = queue[i];
4132 MapBlock *block = NULL;
4135 block = m_env->getMap().getBlockNoCreate(q.pos);
4137 catch(InvalidPositionException &e)
4142 RemoteClient *client = getClient(q.peer_id);
4144 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4146 client->SentBlock(q.pos);
4152 void Server::fillMediaCache()
4154 DSTACK(__FUNCTION_NAME);
4156 infostream<<"Server: Calculating media file checksums"<<std::endl;
4158 // Collect all media file paths
4159 std::list<std::string> paths;
4160 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4161 i != m_mods.end(); i++){
4162 const ModSpec &mod = *i;
4163 paths.push_back(mod.path + DIR_DELIM + "textures");
4164 paths.push_back(mod.path + DIR_DELIM + "sounds");
4165 paths.push_back(mod.path + DIR_DELIM + "media");
4166 paths.push_back(mod.path + DIR_DELIM + "models");
4168 std::string path_all = "textures";
4169 paths.push_back(path_all + DIR_DELIM + "all");
4171 // Collect media file information from paths into cache
4172 for(std::list<std::string>::iterator i = paths.begin();
4173 i != paths.end(); i++)
4175 std::string mediapath = *i;
4176 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4177 for(u32 j=0; j<dirlist.size(); j++){
4178 if(dirlist[j].dir) // Ignode dirs
4180 std::string filename = dirlist[j].name;
4181 // If name contains illegal characters, ignore the file
4182 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4183 infostream<<"Server: ignoring illegal file name: \""
4184 <<filename<<"\""<<std::endl;
4187 // If name is not in a supported format, ignore it
4188 const char *supported_ext[] = {
4189 ".png", ".jpg", ".bmp", ".tga",
4190 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4192 ".x", ".b3d", ".md2", ".obj",
4195 if(removeStringEnd(filename, supported_ext) == ""){
4196 infostream<<"Server: ignoring unsupported file extension: \""
4197 <<filename<<"\""<<std::endl;
4200 // Ok, attempt to load the file and add to cache
4201 std::string filepath = mediapath + DIR_DELIM + filename;
4203 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4204 if(fis.good() == false){
4205 errorstream<<"Server::fillMediaCache(): Could not open \""
4206 <<filename<<"\" for reading"<<std::endl;
4209 std::ostringstream tmp_os(std::ios_base::binary);
4213 fis.read(buf, 1024);
4214 std::streamsize len = fis.gcount();
4215 tmp_os.write(buf, len);
4224 errorstream<<"Server::fillMediaCache(): Failed to read \""
4225 <<filename<<"\""<<std::endl;
4228 if(tmp_os.str().length() == 0){
4229 errorstream<<"Server::fillMediaCache(): Empty file \""
4230 <<filepath<<"\""<<std::endl;
4235 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4237 unsigned char *digest = sha1.getDigest();
4238 std::string sha1_base64 = base64_encode(digest, 20);
4239 std::string sha1_hex = hex_encode((char*)digest, 20);
4243 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4244 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4249 struct SendableMediaAnnouncement
4252 std::string sha1_digest;
4254 SendableMediaAnnouncement(const std::string name_="",
4255 const std::string sha1_digest_=""):
4257 sha1_digest(sha1_digest_)
4261 void Server::sendMediaAnnouncement(u16 peer_id)
4263 DSTACK(__FUNCTION_NAME);
4265 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4268 std::list<SendableMediaAnnouncement> file_announcements;
4270 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4271 i != m_media.end(); i++){
4273 file_announcements.push_back(
4274 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4278 std::ostringstream os(std::ios_base::binary);
4286 u16 length of sha1_digest
4291 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4292 writeU16(os, file_announcements.size());
4294 for(std::list<SendableMediaAnnouncement>::iterator
4295 j = file_announcements.begin();
4296 j != file_announcements.end(); ++j){
4297 os<<serializeString(j->name);
4298 os<<serializeString(j->sha1_digest);
4300 os<<serializeString(g_settings->get("remote_media"));
4303 std::string s = os.str();
4304 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4307 m_con.Send(peer_id, 0, data, true);
4310 struct SendableMedia
4316 SendableMedia(const std::string &name_="", const std::string path_="",
4317 const std::string &data_=""):
4324 void Server::sendRequestedMedia(u16 peer_id,
4325 const std::list<MediaRequest> &tosend)
4327 DSTACK(__FUNCTION_NAME);
4329 verbosestream<<"Server::sendRequestedMedia(): "
4330 <<"Sending files to client"<<std::endl;
4334 // Put 5kB in one bunch (this is not accurate)
4335 u32 bytes_per_bunch = 5000;
4337 std::vector< std::list<SendableMedia> > file_bunches;
4338 file_bunches.push_back(std::list<SendableMedia>());
4340 u32 file_size_bunch_total = 0;
4342 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4343 i != tosend.end(); ++i)
4345 if(m_media.find(i->name) == m_media.end()){
4346 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4347 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4351 //TODO get path + name
4352 std::string tpath = m_media[(*i).name].path;
4355 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4356 if(fis.good() == false){
4357 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4358 <<tpath<<"\" for reading"<<std::endl;
4361 std::ostringstream tmp_os(std::ios_base::binary);
4365 fis.read(buf, 1024);
4366 std::streamsize len = fis.gcount();
4367 tmp_os.write(buf, len);
4368 file_size_bunch_total += len;
4377 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4378 <<(*i).name<<"\""<<std::endl;
4381 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4382 <<tname<<"\""<<std::endl;*/
4384 file_bunches[file_bunches.size()-1].push_back(
4385 SendableMedia((*i).name, tpath, tmp_os.str()));
4387 // Start next bunch if got enough data
4388 if(file_size_bunch_total >= bytes_per_bunch){
4389 file_bunches.push_back(std::list<SendableMedia>());
4390 file_size_bunch_total = 0;
4395 /* Create and send packets */
4397 u32 num_bunches = file_bunches.size();
4398 for(u32 i=0; i<num_bunches; i++)
4400 std::ostringstream os(std::ios_base::binary);
4404 u16 total number of texture bunches
4405 u16 index of this bunch
4406 u32 number of files in this bunch
4415 writeU16(os, TOCLIENT_MEDIA);
4416 writeU16(os, num_bunches);
4418 writeU32(os, file_bunches[i].size());
4420 for(std::list<SendableMedia>::iterator
4421 j = file_bunches[i].begin();
4422 j != file_bunches[i].end(); ++j){
4423 os<<serializeString(j->name);
4424 os<<serializeLongString(j->data);
4428 std::string s = os.str();
4429 verbosestream<<"Server::sendRequestedMedia(): bunch "
4430 <<i<<"/"<<num_bunches
4431 <<" files="<<file_bunches[i].size()
4432 <<" size=" <<s.size()<<std::endl;
4433 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4435 m_con.Send(peer_id, 0, data, true);
4439 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4441 if(m_detached_inventories.count(name) == 0){
4442 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4445 Inventory *inv = m_detached_inventories[name];
4447 std::ostringstream os(std::ios_base::binary);
4448 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4449 os<<serializeString(name);
4453 std::string s = os.str();
4454 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4456 m_con.Send(peer_id, 0, data, true);
4459 void Server::sendDetachedInventoryToAll(const std::string &name)
4461 DSTACK(__FUNCTION_NAME);
4463 for(std::map<u16, RemoteClient*>::iterator
4464 i = m_clients.begin();
4465 i != m_clients.end(); ++i){
4466 RemoteClient *client = i->second;
4467 sendDetachedInventory(name, client->peer_id);
4471 void Server::sendDetachedInventories(u16 peer_id)
4473 DSTACK(__FUNCTION_NAME);
4475 for(std::map<std::string, Inventory*>::iterator
4476 i = m_detached_inventories.begin();
4477 i != m_detached_inventories.end(); i++){
4478 const std::string &name = i->first;
4479 //Inventory *inv = i->second;
4480 sendDetachedInventory(name, peer_id);
4488 void Server::DiePlayer(u16 peer_id)
4490 DSTACK(__FUNCTION_NAME);
4492 PlayerSAO *playersao = getPlayerSAO(peer_id);
4495 infostream<<"Server::DiePlayer(): Player "
4496 <<playersao->getPlayer()->getName()
4497 <<" dies"<<std::endl;
4499 playersao->setHP(0);
4501 // Trigger scripted stuff
4502 m_script->on_dieplayer(playersao);
4504 SendPlayerHP(peer_id);
4505 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4508 void Server::RespawnPlayer(u16 peer_id)
4510 DSTACK(__FUNCTION_NAME);
4512 PlayerSAO *playersao = getPlayerSAO(peer_id);
4515 infostream<<"Server::RespawnPlayer(): Player "
4516 <<playersao->getPlayer()->getName()
4517 <<" respawns"<<std::endl;
4519 playersao->setHP(PLAYER_MAX_HP);
4521 bool repositioned = m_script->on_respawnplayer(playersao);
4523 v3f pos = findSpawnPos(m_env->getServerMap());
4524 playersao->setPos(pos);
4528 void Server::UpdateCrafting(u16 peer_id)
4530 DSTACK(__FUNCTION_NAME);
4532 Player* player = m_env->getPlayer(peer_id);
4535 // Get a preview for crafting
4537 getCraftingResult(&player->inventory, preview, false, this);
4539 // Put the new preview in
4540 InventoryList *plist = player->inventory.getList("craftpreview");
4542 assert(plist->getSize() >= 1);
4543 plist->changeItem(0, preview);
4546 RemoteClient* Server::getClient(u16 peer_id)
4548 DSTACK(__FUNCTION_NAME);
4549 //JMutexAutoLock lock(m_con_mutex);
4550 std::map<u16, RemoteClient*>::iterator n;
4551 n = m_clients.find(peer_id);
4552 // A client should exist for all peers
4553 assert(n != m_clients.end());
4557 std::wstring Server::getStatusString()
4559 std::wostringstream os(std::ios_base::binary);
4562 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4564 os<<L", uptime="<<m_uptime.get();
4565 // Information about clients
4566 std::map<u16, RemoteClient*>::iterator i;
4569 for(i = m_clients.begin(), first = true;
4570 i != m_clients.end(); ++i)
4572 // Get client and check that it is valid
4573 RemoteClient *client = i->second;
4574 assert(client->peer_id == i->first);
4575 if(client->serialization_version == SER_FMT_VER_INVALID)
4578 Player *player = m_env->getPlayer(client->peer_id);
4579 // Get name of player
4580 std::wstring name = L"unknown";
4582 name = narrow_to_wide(player->getName());
4583 // Add name to information string
4591 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4592 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4593 if(g_settings->get("motd") != "")
4594 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4598 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4600 std::set<std::string> privs;
4601 m_script->getAuth(name, NULL, &privs);
4605 bool Server::checkPriv(const std::string &name, const std::string &priv)
4607 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4608 return (privs.count(priv) != 0);
4611 void Server::reportPrivsModified(const std::string &name)
4614 for(std::map<u16, RemoteClient*>::iterator
4615 i = m_clients.begin();
4616 i != m_clients.end(); ++i){
4617 RemoteClient *client = i->second;
4618 Player *player = m_env->getPlayer(client->peer_id);
4619 reportPrivsModified(player->getName());
4622 Player *player = m_env->getPlayer(name.c_str());
4625 SendPlayerPrivileges(player->peer_id);
4626 PlayerSAO *sao = player->getPlayerSAO();
4629 sao->updatePrivileges(
4630 getPlayerEffectivePrivs(name),
4635 void Server::reportInventoryFormspecModified(const std::string &name)
4637 Player *player = m_env->getPlayer(name.c_str());
4640 SendPlayerInventoryFormspec(player->peer_id);
4643 // Saves g_settings to configpath given at initialization
4644 void Server::saveConfig()
4646 if(m_path_config != "")
4647 g_settings->updateConfigFile(m_path_config.c_str());
4650 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4652 Player *player = m_env->getPlayer(name);
4656 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4658 SendChatMessage(player->peer_id, msg);
4661 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4663 Player *player = m_env->getPlayer(playername);
4667 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4671 SendShowFormspecMessage(player->peer_id, formspec, formname);
4675 u32 Server::hudAdd(Player *player, HudElement *form) {
4679 u32 id = hud_get_free_id(player);
4680 if (id < player->hud.size())
4681 player->hud[id] = form;
4683 player->hud.push_back(form);
4685 SendHUDAdd(player->peer_id, id, form);
4689 bool Server::hudRemove(Player *player, u32 id) {
4690 if (!player || id >= player->hud.size() || !player->hud[id])
4693 delete player->hud[id];
4694 player->hud[id] = NULL;
4696 SendHUDRemove(player->peer_id, id);
4700 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4704 SendHUDChange(player->peer_id, id, stat, data);
4708 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4712 SendHUDSetFlags(player->peer_id, flags, mask);
4716 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4719 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4722 std::ostringstream os(std::ios::binary);
4723 writeS32(os, hotbar_itemcount);
4724 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4728 void Server::notifyPlayers(const std::wstring msg)
4730 BroadcastChatMessage(msg);
4733 void Server::spawnParticle(const char *playername, v3f pos,
4734 v3f velocity, v3f acceleration,
4735 float expirationtime, float size, bool
4736 collisiondetection, std::string texture)
4738 Player *player = m_env->getPlayer(playername);
4741 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4742 expirationtime, size, collisiondetection, texture);
4745 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4746 float expirationtime, float size,
4747 bool collisiondetection, std::string texture)
4749 SendSpawnParticleAll(pos, velocity, acceleration,
4750 expirationtime, size, collisiondetection, texture);
4753 u32 Server::addParticleSpawner(const char *playername,
4754 u16 amount, float spawntime,
4755 v3f minpos, v3f maxpos,
4756 v3f minvel, v3f maxvel,
4757 v3f minacc, v3f maxacc,
4758 float minexptime, float maxexptime,
4759 float minsize, float maxsize,
4760 bool collisiondetection, std::string texture)
4762 Player *player = m_env->getPlayer(playername);
4767 for(;;) // look for unused particlespawner id
4770 if (std::find(m_particlespawner_ids.begin(),
4771 m_particlespawner_ids.end(), id)
4772 == m_particlespawner_ids.end())
4774 m_particlespawner_ids.push_back(id);
4779 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4780 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4781 minexptime, maxexptime, minsize, maxsize,
4782 collisiondetection, texture, id);
4787 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4788 v3f minpos, v3f maxpos,
4789 v3f minvel, v3f maxvel,
4790 v3f minacc, v3f maxacc,
4791 float minexptime, float maxexptime,
4792 float minsize, float maxsize,
4793 bool collisiondetection, std::string texture)
4796 for(;;) // look for unused particlespawner id
4799 if (std::find(m_particlespawner_ids.begin(),
4800 m_particlespawner_ids.end(), id)
4801 == m_particlespawner_ids.end())
4803 m_particlespawner_ids.push_back(id);
4808 SendAddParticleSpawnerAll(amount, spawntime,
4809 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4810 minexptime, maxexptime, minsize, maxsize,
4811 collisiondetection, texture, id);
4816 void Server::deleteParticleSpawner(const char *playername, u32 id)
4818 Player *player = m_env->getPlayer(playername);
4822 m_particlespawner_ids.erase(
4823 std::remove(m_particlespawner_ids.begin(),
4824 m_particlespawner_ids.end(), id),
4825 m_particlespawner_ids.end());
4826 SendDeleteParticleSpawner(player->peer_id, id);
4829 void Server::deleteParticleSpawnerAll(u32 id)
4831 m_particlespawner_ids.erase(
4832 std::remove(m_particlespawner_ids.begin(),
4833 m_particlespawner_ids.end(), id),
4834 m_particlespawner_ids.end());
4835 SendDeleteParticleSpawnerAll(id);
4838 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4840 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4843 Inventory* Server::createDetachedInventory(const std::string &name)
4845 if(m_detached_inventories.count(name) > 0){
4846 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4847 delete m_detached_inventories[name];
4849 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4851 Inventory *inv = new Inventory(m_itemdef);
4853 m_detached_inventories[name] = inv;
4854 sendDetachedInventoryToAll(name);
4861 BoolScopeSet(bool *dst, bool val):
4864 m_orig_state = *m_dst;
4869 *m_dst = m_orig_state;
4876 // actions: time-reversed list
4877 // Return value: success/failure
4878 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4879 std::list<std::string> *log)
4881 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4882 ServerMap *map = (ServerMap*)(&m_env->getMap());
4883 // Disable rollback report sink while reverting
4884 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4886 // Fail if no actions to handle
4887 if(actions.empty()){
4888 log->push_back("Nothing to do.");
4895 for(std::list<RollbackAction>::const_iterator
4896 i = actions.begin();
4897 i != actions.end(); i++)
4899 const RollbackAction &action = *i;
4901 bool success = action.applyRevert(map, this, this);
4904 std::ostringstream os;
4905 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4906 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4908 log->push_back(os.str());
4910 std::ostringstream os;
4911 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4912 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4914 log->push_back(os.str());
4918 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4919 <<" failed"<<std::endl;
4921 // Call it done if less than half failed
4922 return num_failed <= num_tried/2;
4925 // IGameDef interface
4927 IItemDefManager* Server::getItemDefManager()
4931 INodeDefManager* Server::getNodeDefManager()
4935 ICraftDefManager* Server::getCraftDefManager()
4939 ITextureSource* Server::getTextureSource()
4943 IShaderSource* Server::getShaderSource()
4947 u16 Server::allocateUnknownNodeId(const std::string &name)
4949 return m_nodedef->allocateDummy(name);
4951 ISoundManager* Server::getSoundManager()
4953 return &dummySoundManager;
4955 MtEventManager* Server::getEventManager()
4959 IRollbackReportSink* Server::getRollbackReportSink()
4961 if(!m_enable_rollback_recording)
4963 if(!m_rollback_sink_enabled)
4968 IWritableItemDefManager* Server::getWritableItemDefManager()
4972 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4976 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4981 const ModSpec* Server::getModSpec(const std::string &modname)
4983 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4984 i != m_mods.end(); i++){
4985 const ModSpec &mod = *i;
4986 if(mod.name == modname)
4991 void Server::getModNames(std::list<std::string> &modlist)
4993 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4995 modlist.push_back(i->name);
4998 std::string Server::getBuiltinLuaPath()
5000 return porting::path_share + DIR_DELIM + "builtin";
5003 v3f findSpawnPos(ServerMap &map)
5005 //return v3f(50,50,50)*BS;
5010 nodepos = v2s16(0,0);
5015 s16 water_level = map.m_mgparams->water_level;
5017 // Try to find a good place a few times
5018 for(s32 i=0; i<1000; i++)
5021 // We're going to try to throw the player to this position
5022 v2s16 nodepos2d = v2s16(
5023 -range + (myrand() % (range * 2)),
5024 -range + (myrand() % (range * 2)));
5026 // Get ground height at point
5027 s16 groundheight = map.findGroundLevel(nodepos2d);
5028 if (groundheight <= water_level) // Don't go underwater
5030 if (groundheight > water_level + 6) // Don't go to high places
5033 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
5034 bool is_good = false;
5036 for (s32 i = 0; i < 10; i++) {
5037 v3s16 blockpos = getNodeBlockPos(nodepos);
5038 map.emergeBlock(blockpos, true);
5039 content_t c = map.getNodeNoEx(nodepos).getContent();
5040 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
5042 if (air_count >= 2){
5050 // Found a good place
5051 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5057 return intToFloat(nodepos, BS);
5060 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5062 RemotePlayer *player = NULL;
5063 bool newplayer = false;
5066 Try to get an existing player
5068 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5070 // If player is already connected, cancel
5071 if(player != NULL && player->peer_id != 0)
5073 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5078 If player with the wanted peer_id already exists, cancel.
5080 if(m_env->getPlayer(peer_id) != NULL)
5082 infostream<<"emergePlayer(): Player with wrong name but same"
5083 " peer_id already exists"<<std::endl;
5088 Create a new player if it doesn't exist yet
5093 player = new RemotePlayer(this);
5094 player->updateName(name);
5096 /* Set player position */
5097 infostream<<"Server: Finding spawn place for player \""
5098 <<name<<"\""<<std::endl;
5099 v3f pos = findSpawnPos(m_env->getServerMap());
5100 player->setPosition(pos);
5102 /* Add player to environment */
5103 m_env->addPlayer(player);
5107 Create a new player active object
5109 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5110 getPlayerEffectivePrivs(player->getName()),
5113 /* Clean up old HUD elements from previous sessions */
5114 player->hud.clear();
5116 /* Add object to environment */
5117 m_env->addActiveObject(playersao);
5121 m_script->on_newplayer(playersao);
5123 m_script->on_joinplayer(playersao);
5128 void Server::handlePeerChange(PeerChange &c)
5130 JMutexAutoLock envlock(m_env_mutex);
5131 JMutexAutoLock conlock(m_con_mutex);
5133 if(c.type == PEER_ADDED)
5140 std::map<u16, RemoteClient*>::iterator n;
5141 n = m_clients.find(c.peer_id);
5142 // The client shouldn't already exist
5143 assert(n == m_clients.end());
5146 RemoteClient *client = new RemoteClient();
5147 client->peer_id = c.peer_id;
5148 m_clients[client->peer_id] = client;
5151 else if(c.type == PEER_REMOVED)
5158 std::map<u16, RemoteClient*>::iterator n;
5159 n = m_clients.find(c.peer_id);
5160 // The client should exist
5161 assert(n != m_clients.end());
5164 Mark objects to be not known by the client
5166 RemoteClient *client = n->second;
5168 for(std::set<u16>::iterator
5169 i = client->m_known_objects.begin();
5170 i != client->m_known_objects.end(); ++i)
5174 ServerActiveObject* obj = m_env->getActiveObject(id);
5176 if(obj && obj->m_known_by_count > 0)
5177 obj->m_known_by_count--;
5181 Clear references to playing sounds
5183 for(std::map<s32, ServerPlayingSound>::iterator
5184 i = m_playing_sounds.begin();
5185 i != m_playing_sounds.end();)
5187 ServerPlayingSound &psound = i->second;
5188 psound.clients.erase(c.peer_id);
5189 if(psound.clients.size() == 0)
5190 m_playing_sounds.erase(i++);
5195 Player *player = m_env->getPlayer(c.peer_id);
5197 // Collect information about leaving in chat
5198 std::wstring message;
5202 std::wstring name = narrow_to_wide(player->getName());
5205 message += L" left the game.";
5207 message += L" (timed out)";
5211 /* Run scripts and remove from environment */
5215 PlayerSAO *playersao = player->getPlayerSAO();
5218 m_script->on_leaveplayer(playersao);
5220 playersao->disconnected();
5230 std::ostringstream os(std::ios_base::binary);
5231 for(std::map<u16, RemoteClient*>::iterator
5232 i = m_clients.begin();
5233 i != m_clients.end(); ++i)
5235 RemoteClient *client = i->second;
5236 assert(client->peer_id == i->first);
5237 if(client->serialization_version == SER_FMT_VER_INVALID)
5240 Player *player = m_env->getPlayer(client->peer_id);
5243 // Get name of player
5244 os<<player->getName()<<" ";
5247 actionstream<<player->getName()<<" "
5248 <<(c.timeout?"times out.":"leaves game.")
5249 <<" List of players: "
5250 <<os.str()<<std::endl;
5255 delete m_clients[c.peer_id];
5256 m_clients.erase(c.peer_id);
5258 // Send player info to all remaining clients
5259 //SendPlayerInfos();
5261 // Send leave chat message to all remaining clients
5262 if(message.length() != 0)
5263 BroadcastChatMessage(message);
5272 void Server::handlePeerChanges()
5274 while(m_peer_change_queue.size() > 0)
5276 PeerChange c = m_peer_change_queue.pop_front();
5278 verbosestream<<"Server: Handling peer change: "
5279 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5282 handlePeerChange(c);
5286 void dedicated_server_loop(Server &server, bool &kill)
5288 DSTACK(__FUNCTION_NAME);
5290 verbosestream<<"dedicated_server_loop()"<<std::endl;
5292 IntervalLimiter m_profiler_interval;
5296 float steplen = g_settings->getFloat("dedicated_server_step");
5297 // This is kind of a hack but can be done like this
5298 // because server.step() is very light
5300 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5301 sleep_ms((int)(steplen*1000.0));
5303 server.step(steplen);
5305 if(server.getShutdownRequested() || kill)
5307 infostream<<"Dedicated server quitting"<<std::endl;
5309 if(g_settings->getBool("server_announce") == true)
5310 ServerList::sendAnnounce("delete");
5318 float profiler_print_interval =
5319 g_settings->getFloat("profiler_print_interval");
5320 if(profiler_print_interval != 0)
5322 if(m_profiler_interval.step(steplen, profiler_print_interval))
5324 infostream<<"Profiler:"<<std::endl;
5325 g_profiler->print(infostream);
5326 g_profiler->clear();