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, this),
643 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
645 m_rollback_sink_enabled(true),
646 m_enable_rollback_recording(false),
649 m_itemdef(createItemDefManager()),
650 m_nodedef(createNodeDefManager()),
651 m_craftdef(createCraftDefManager()),
652 m_event(new EventManager()),
654 m_time_of_day_send_timer(0),
656 m_shutdown_requested(false),
657 m_ignore_map_edit_events(false),
658 m_ignore_map_edit_events_peer_id(0)
660 m_liquid_transform_timer = 0.0;
661 m_liquid_transform_every = 1.0;
662 m_print_info_timer = 0.0;
663 m_masterserver_timer = 0.0;
664 m_objectdata_timer = 0.0;
665 m_emergethread_trigger_timer = 0.0;
666 m_savemap_timer = 0.0;
667 m_clients_number = 0;
671 m_step_dtime_mutex.Init();
675 throw ServerError("Supplied empty world path");
677 if(!gamespec.isValid())
678 throw ServerError("Supplied invalid gamespec");
680 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
681 if(m_simple_singleplayer_mode)
682 infostream<<" in simple singleplayer mode"<<std::endl;
684 infostream<<std::endl;
685 infostream<<"- world: "<<m_path_world<<std::endl;
686 infostream<<"- config: "<<m_path_config<<std::endl;
687 infostream<<"- game: "<<m_gamespec.path<<std::endl;
689 // Initialize default settings and override defaults with those provided
691 set_default_settings(g_settings);
692 Settings gamedefaults;
693 getGameMinetestConfig(gamespec.path, gamedefaults);
694 override_default_settings(g_settings, &gamedefaults);
696 // Create emerge manager
697 m_emerge = new EmergeManager(this);
699 // Create rollback manager
700 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
701 m_rollback = createRollbackManager(rollback_path, this);
703 // Create world if it doesn't exist
704 if(!initializeWorld(m_path_world, m_gamespec.id))
705 throw ServerError("Failed to initialize world");
707 ModConfiguration modconf(m_path_world);
708 m_mods = modconf.getMods();
709 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
710 // complain about mods with unsatisfied dependencies
711 if(!modconf.isConsistent())
713 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
714 it != unsatisfied_mods.end(); ++it)
717 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
718 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
719 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
720 errorstream << " \"" << *dep_it << "\"";
721 errorstream << std::endl;
725 Settings worldmt_settings;
726 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
727 worldmt_settings.readConfigFile(worldmt.c_str());
728 std::vector<std::string> names = worldmt_settings.getNames();
729 std::set<std::string> load_mod_names;
730 for(std::vector<std::string>::iterator it = names.begin();
731 it != names.end(); ++it)
733 std::string name = *it;
734 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
735 load_mod_names.insert(name.substr(9));
737 // complain about mods declared to be loaded, but not found
738 for(std::vector<ModSpec>::iterator it = m_mods.begin();
739 it != m_mods.end(); ++it)
740 load_mod_names.erase((*it).name);
741 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
742 it != unsatisfied_mods.end(); ++it)
743 load_mod_names.erase((*it).name);
744 if(!load_mod_names.empty())
746 errorstream << "The following mods could not be found:";
747 for(std::set<std::string>::iterator it = load_mod_names.begin();
748 it != load_mod_names.end(); ++it)
749 errorstream << " \"" << (*it) << "\"";
750 errorstream << std::endl;
753 // Path to builtin.lua
754 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
757 JMutexAutoLock envlock(m_env_mutex);
758 JMutexAutoLock conlock(m_con_mutex);
760 // Initialize scripting
762 infostream<<"Server: Initializing Lua"<<std::endl;
764 m_script = new ScriptApi(this);
767 // Load and run builtin.lua
768 infostream<<"Server: Loading builtin.lua [\""
769 <<builtinpath<<"\"]"<<std::endl;
770 bool success = m_script->loadMod(builtinpath, "__builtin");
772 errorstream<<"Server: Failed to load and run "
773 <<builtinpath<<std::endl;
774 throw ModError("Failed to load and run "+builtinpath);
777 infostream<<"Server: Loading mods: ";
778 for(std::vector<ModSpec>::iterator i = m_mods.begin();
779 i != m_mods.end(); i++){
780 const ModSpec &mod = *i;
781 infostream<<mod.name<<" ";
783 infostream<<std::endl;
784 // Load and run "mod" scripts
785 for(std::vector<ModSpec>::iterator i = m_mods.begin();
786 i != m_mods.end(); i++){
787 const ModSpec &mod = *i;
788 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
789 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
790 <<scriptpath<<"\"]"<<std::endl;
791 bool success = m_script->loadMod(scriptpath, mod.name);
793 errorstream<<"Server: Failed to load and run "
794 <<scriptpath<<std::endl;
795 throw ModError("Failed to load and run "+scriptpath);
799 // Read Textures and calculate sha1 sums
802 // Apply item aliases in the node definition manager
803 m_nodedef->updateAliases(m_itemdef);
805 // Initialize Environment
806 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
807 m_env = new ServerEnvironment(servermap, m_script, this, this);
809 m_emerge->initMapgens(servermap->getMapgenParams());
811 // Give environment reference to scripting api
812 m_script->initializeEnvironment(m_env);
814 // Register us to receive map edit events
815 servermap->addEventReceiver(this);
817 // If file exists, load environment metadata
818 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
820 infostream<<"Server: Loading environment metadata"<<std::endl;
821 m_env->loadMeta(m_path_world);
825 infostream<<"Server: Loading players"<<std::endl;
826 m_env->deSerializePlayers(m_path_world);
829 Add some test ActiveBlockModifiers to environment
831 add_legacy_abms(m_env, m_nodedef);
833 m_liquid_transform_every = g_settings->getFloat("liquid_update");
838 infostream<<"Server destructing"<<std::endl;
841 Send shutdown message
844 JMutexAutoLock conlock(m_con_mutex);
846 std::wstring line = L"*** Server shutting down";
849 Send the message to clients
851 for(std::map<u16, RemoteClient*>::iterator
852 i = m_clients.begin();
853 i != m_clients.end(); ++i)
855 // Get client and check that it is valid
856 RemoteClient *client = i->second;
857 assert(client->peer_id == i->first);
858 if(client->serialization_version == SER_FMT_VER_INVALID)
862 SendChatMessage(client->peer_id, line);
864 catch(con::PeerNotFoundException &e)
870 JMutexAutoLock envlock(m_env_mutex);
871 JMutexAutoLock conlock(m_con_mutex);
874 Execute script shutdown hooks
876 m_script->on_shutdown();
880 JMutexAutoLock envlock(m_env_mutex);
885 infostream<<"Server: Saving players"<<std::endl;
886 m_env->serializePlayers(m_path_world);
889 Save environment metadata
891 infostream<<"Server: Saving environment metadata"<<std::endl;
892 m_env->saveMeta(m_path_world);
900 //shutdown all emerge threads first!
907 JMutexAutoLock clientslock(m_con_mutex);
909 for(std::map<u16, RemoteClient*>::iterator
910 i = m_clients.begin();
911 i != m_clients.end(); ++i)
919 // Delete things in the reverse order of creation
927 // Deinitialize scripting
928 infostream<<"Server: Deinitializing scripting"<<std::endl;
931 // Delete detached inventories
933 for(std::map<std::string, Inventory*>::iterator
934 i = m_detached_inventories.begin();
935 i != m_detached_inventories.end(); i++){
941 void Server::start(unsigned short port)
943 DSTACK(__FUNCTION_NAME);
944 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
946 // Stop thread if already running
949 // Initialize connection
950 m_con.SetTimeoutMs(30);
954 m_thread.setRun(true);
957 // ASCII art for the win!
959 <<" .__ __ __ "<<std::endl
960 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
961 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
962 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
963 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
964 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
965 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
966 actionstream<<"Server for gameid=\""<<m_gamespec.id
967 <<"\" listening on port "<<port<<"."<<std::endl;
972 DSTACK(__FUNCTION_NAME);
974 infostream<<"Server: Stopping and waiting threads"<<std::endl;
976 // Stop threads (set run=false first so both start stopping)
977 m_thread.setRun(false);
978 //m_emergethread.setRun(false);
980 //m_emergethread.stop();
982 infostream<<"Server: Threads stopped"<<std::endl;
985 void Server::step(float dtime)
987 DSTACK(__FUNCTION_NAME);
992 JMutexAutoLock lock(m_step_dtime_mutex);
993 m_step_dtime += dtime;
995 // Throw if fatal error occurred in thread
996 std::string async_err = m_async_fatal_error.get();
998 throw ServerError(async_err);
1002 void Server::AsyncRunStep()
1004 DSTACK(__FUNCTION_NAME);
1006 g_profiler->add("Server::AsyncRunStep (num)", 1);
1010 JMutexAutoLock lock1(m_step_dtime_mutex);
1011 dtime = m_step_dtime;
1015 // Send blocks to clients
1022 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1024 //infostream<<"Server steps "<<dtime<<std::endl;
1025 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1028 JMutexAutoLock lock1(m_step_dtime_mutex);
1029 m_step_dtime -= dtime;
1036 m_uptime.set(m_uptime.get() + dtime);
1040 // Process connection's timeouts
1041 JMutexAutoLock lock2(m_con_mutex);
1042 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1043 m_con.RunTimeouts(dtime);
1047 // This has to be called so that the client list gets synced
1048 // with the peer list of the connection
1049 handlePeerChanges();
1053 Update time of day and overall game time
1056 JMutexAutoLock envlock(m_env_mutex);
1058 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1061 Send to clients at constant intervals
1064 m_time_of_day_send_timer -= dtime;
1065 if(m_time_of_day_send_timer < 0.0)
1067 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1069 //JMutexAutoLock envlock(m_env_mutex);
1070 JMutexAutoLock conlock(m_con_mutex);
1072 for(std::map<u16, RemoteClient*>::iterator
1073 i = m_clients.begin();
1074 i != m_clients.end(); ++i)
1076 RemoteClient *client = i->second;
1077 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1078 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1080 m_con.Send(client->peer_id, 0, data, true);
1086 JMutexAutoLock lock(m_env_mutex);
1088 ScopeProfiler sp(g_profiler, "SEnv step");
1089 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1093 const float map_timer_and_unload_dtime = 2.92;
1094 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1096 JMutexAutoLock lock(m_env_mutex);
1097 // Run Map's timers and unload unused data
1098 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1099 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1100 g_settings->getFloat("server_unload_unused_data_timeout"));
1111 JMutexAutoLock lock(m_env_mutex);
1112 JMutexAutoLock lock2(m_con_mutex);
1114 ScopeProfiler sp(g_profiler, "Server: handle players");
1116 for(std::map<u16, RemoteClient*>::iterator
1117 i = m_clients.begin();
1118 i != m_clients.end(); ++i)
1120 RemoteClient *client = i->second;
1121 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1122 if(playersao == NULL)
1126 Handle player HPs (die if hp=0)
1128 if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
1130 if(playersao->getHP() == 0)
1131 DiePlayer(client->peer_id);
1133 SendPlayerHP(client->peer_id);
1137 Send player inventories if necessary
1139 if(playersao->m_moved){
1140 SendMovePlayer(client->peer_id);
1141 playersao->m_moved = false;
1143 if(playersao->m_inventory_not_sent){
1144 UpdateCrafting(client->peer_id);
1145 SendInventory(client->peer_id);
1150 /* Transform liquids */
1151 m_liquid_transform_timer += dtime;
1152 if(m_liquid_transform_timer >= m_liquid_transform_every)
1154 m_liquid_transform_timer -= m_liquid_transform_every;
1156 JMutexAutoLock lock(m_env_mutex);
1158 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1160 std::map<v3s16, MapBlock*> modified_blocks;
1161 m_env->getMap().transformLiquids(modified_blocks);
1166 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1167 ServerMap &map = ((ServerMap&)m_env->getMap());
1168 map.updateLighting(modified_blocks, lighting_modified_blocks);
1170 // Add blocks modified by lighting to modified_blocks
1171 for(core::map<v3s16, MapBlock*>::Iterator
1172 i = lighting_modified_blocks.getIterator();
1173 i.atEnd() == false; i++)
1175 MapBlock *block = i.getNode()->getValue();
1176 modified_blocks.insert(block->getPos(), block);
1180 Set the modified blocks unsent for all the clients
1183 JMutexAutoLock lock2(m_con_mutex);
1185 for(std::map<u16, RemoteClient*>::iterator
1186 i = m_clients.begin();
1187 i != m_clients.end(); ++i)
1189 RemoteClient *client = i->second;
1191 if(modified_blocks.size() > 0)
1193 // Remove block from sent history
1194 client->SetBlocksNotSent(modified_blocks);
1199 // Periodically print some info
1201 float &counter = m_print_info_timer;
1207 JMutexAutoLock lock2(m_con_mutex);
1208 m_clients_number = 0;
1209 if(m_clients.size() != 0)
1210 infostream<<"Players:"<<std::endl;
1211 for(std::map<u16, RemoteClient*>::iterator
1212 i = m_clients.begin();
1213 i != m_clients.end(); ++i)
1215 //u16 peer_id = i.getNode()->getKey();
1216 RemoteClient *client = i->second;
1217 Player *player = m_env->getPlayer(client->peer_id);
1220 infostream<<"* "<<player->getName()<<"\t";
1221 client->PrintInfo(infostream);
1229 // send masterserver announce
1231 float &counter = m_masterserver_timer;
1232 if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true)
1234 ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id);
1241 //if(g_settings->getBool("enable_experimental"))
1245 Check added and deleted active objects
1248 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1249 JMutexAutoLock envlock(m_env_mutex);
1250 JMutexAutoLock conlock(m_con_mutex);
1252 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1254 // Radius inside which objects are active
1255 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1256 radius *= MAP_BLOCKSIZE;
1258 for(std::map<u16, RemoteClient*>::iterator
1259 i = m_clients.begin();
1260 i != m_clients.end(); ++i)
1262 RemoteClient *client = i->second;
1264 // If definitions and textures have not been sent, don't
1265 // send objects either
1266 if(!client->definitions_sent)
1269 Player *player = m_env->getPlayer(client->peer_id);
1272 // This can happen if the client timeouts somehow
1273 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1275 <<" has no associated player"<<std::endl;*/
1278 v3s16 pos = floatToInt(player->getPosition(), BS);
1280 std::set<u16> removed_objects;
1281 std::set<u16> added_objects;
1282 m_env->getRemovedActiveObjects(pos, radius,
1283 client->m_known_objects, removed_objects);
1284 m_env->getAddedActiveObjects(pos, radius,
1285 client->m_known_objects, added_objects);
1287 // Ignore if nothing happened
1288 if(removed_objects.size() == 0 && added_objects.size() == 0)
1290 //infostream<<"active objects: none changed"<<std::endl;
1294 std::string data_buffer;
1298 // Handle removed objects
1299 writeU16((u8*)buf, removed_objects.size());
1300 data_buffer.append(buf, 2);
1301 for(std::set<u16>::iterator
1302 i = removed_objects.begin();
1303 i != removed_objects.end(); ++i)
1307 ServerActiveObject* obj = m_env->getActiveObject(id);
1309 // Add to data buffer for sending
1310 writeU16((u8*)buf, id);
1311 data_buffer.append(buf, 2);
1313 // Remove from known objects
1314 client->m_known_objects.erase(id);
1316 if(obj && obj->m_known_by_count > 0)
1317 obj->m_known_by_count--;
1320 // Handle added objects
1321 writeU16((u8*)buf, added_objects.size());
1322 data_buffer.append(buf, 2);
1323 for(std::set<u16>::iterator
1324 i = added_objects.begin();
1325 i != added_objects.end(); ++i)
1329 ServerActiveObject* obj = m_env->getActiveObject(id);
1332 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1334 infostream<<"WARNING: "<<__FUNCTION_NAME
1335 <<": NULL object"<<std::endl;
1337 type = obj->getSendType();
1339 // Add to data buffer for sending
1340 writeU16((u8*)buf, id);
1341 data_buffer.append(buf, 2);
1342 writeU8((u8*)buf, type);
1343 data_buffer.append(buf, 1);
1346 data_buffer.append(serializeLongString(
1347 obj->getClientInitializationData(client->net_proto_version)));
1349 data_buffer.append(serializeLongString(""));
1351 // Add to known objects
1352 client->m_known_objects.insert(id);
1355 obj->m_known_by_count++;
1359 SharedBuffer<u8> reply(2 + data_buffer.size());
1360 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1361 memcpy((char*)&reply[2], data_buffer.c_str(),
1362 data_buffer.size());
1364 m_con.Send(client->peer_id, 0, reply, true);
1366 verbosestream<<"Server: Sent object remove/add: "
1367 <<removed_objects.size()<<" removed, "
1368 <<added_objects.size()<<" added, "
1369 <<"packet size is "<<reply.getSize()<<std::endl;
1374 Collect a list of all the objects known by the clients
1375 and report it back to the environment.
1378 core::map<u16, bool> all_known_objects;
1380 for(core::map<u16, RemoteClient*>::Iterator
1381 i = m_clients.getIterator();
1382 i.atEnd() == false; i++)
1384 RemoteClient *client = i.getNode()->getValue();
1385 // Go through all known objects of client
1386 for(core::map<u16, bool>::Iterator
1387 i = client->m_known_objects.getIterator();
1388 i.atEnd()==false; i++)
1390 u16 id = i.getNode()->getKey();
1391 all_known_objects[id] = true;
1395 m_env->setKnownActiveObjects(whatever);
1401 Send object messages
1404 JMutexAutoLock envlock(m_env_mutex);
1405 JMutexAutoLock conlock(m_con_mutex);
1407 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1410 // Value = data sent by object
1411 std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
1413 // Get active object messages from environment
1416 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1420 std::list<ActiveObjectMessage>* message_list = NULL;
1421 std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
1422 n = buffered_messages.find(aom.id);
1423 if(n == buffered_messages.end())
1425 message_list = new std::list<ActiveObjectMessage>;
1426 buffered_messages[aom.id] = message_list;
1430 message_list = n->second;
1432 message_list->push_back(aom);
1435 // Route data to every client
1436 for(std::map<u16, RemoteClient*>::iterator
1437 i = m_clients.begin();
1438 i != m_clients.end(); ++i)
1440 RemoteClient *client = i->second;
1441 std::string reliable_data;
1442 std::string unreliable_data;
1443 // Go through all objects in message buffer
1444 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1445 j = buffered_messages.begin();
1446 j != buffered_messages.end(); ++j)
1448 // If object is not known by client, skip it
1450 if(client->m_known_objects.find(id) == client->m_known_objects.end())
1452 // Get message list of object
1453 std::list<ActiveObjectMessage>* list = j->second;
1454 // Go through every message
1455 for(std::list<ActiveObjectMessage>::iterator
1456 k = list->begin(); k != list->end(); ++k)
1458 // Compose the full new data with header
1459 ActiveObjectMessage aom = *k;
1460 std::string new_data;
1463 writeU16((u8*)&buf[0], aom.id);
1464 new_data.append(buf, 2);
1466 new_data += serializeString(aom.datastring);
1467 // Add data to buffer
1469 reliable_data += new_data;
1471 unreliable_data += new_data;
1475 reliable_data and unreliable_data are now ready.
1478 if(reliable_data.size() > 0)
1480 SharedBuffer<u8> reply(2 + reliable_data.size());
1481 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1482 memcpy((char*)&reply[2], reliable_data.c_str(),
1483 reliable_data.size());
1485 m_con.Send(client->peer_id, 0, reply, true);
1487 if(unreliable_data.size() > 0)
1489 SharedBuffer<u8> reply(2 + unreliable_data.size());
1490 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1491 memcpy((char*)&reply[2], unreliable_data.c_str(),
1492 unreliable_data.size());
1493 // Send as unreliable
1494 m_con.Send(client->peer_id, 0, reply, false);
1497 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1499 infostream<<"Server: Size of object message data: "
1500 <<"reliable: "<<reliable_data.size()
1501 <<", unreliable: "<<unreliable_data.size()
1506 // Clear buffered_messages
1507 for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
1508 i = buffered_messages.begin();
1509 i != buffered_messages.end(); ++i)
1515 } // enable_experimental
1518 Send queued-for-sending map edit events.
1521 // We will be accessing the environment and the connection
1522 JMutexAutoLock lock(m_env_mutex);
1523 JMutexAutoLock conlock(m_con_mutex);
1525 // Don't send too many at a time
1528 // Single change sending is disabled if queue size is not small
1529 bool disable_single_change_sending = false;
1530 if(m_unsent_map_edit_queue.size() >= 4)
1531 disable_single_change_sending = true;
1533 int event_count = m_unsent_map_edit_queue.size();
1535 // We'll log the amount of each
1538 while(m_unsent_map_edit_queue.size() != 0)
1540 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1542 // Players far away from the change are stored here.
1543 // Instead of sending the changes, MapBlocks are set not sent
1545 std::list<u16> far_players;
1547 if(event->type == MEET_ADDNODE)
1549 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1550 prof.add("MEET_ADDNODE", 1);
1551 if(disable_single_change_sending)
1552 sendAddNode(event->p, event->n, event->already_known_by_peer,
1555 sendAddNode(event->p, event->n, event->already_known_by_peer,
1558 else if(event->type == MEET_REMOVENODE)
1560 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1561 prof.add("MEET_REMOVENODE", 1);
1562 if(disable_single_change_sending)
1563 sendRemoveNode(event->p, event->already_known_by_peer,
1566 sendRemoveNode(event->p, event->already_known_by_peer,
1569 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1571 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1572 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1573 setBlockNotSent(event->p);
1575 else if(event->type == MEET_OTHER)
1577 infostream<<"Server: MEET_OTHER"<<std::endl;
1578 prof.add("MEET_OTHER", 1);
1579 for(std::set<v3s16>::iterator
1580 i = event->modified_blocks.begin();
1581 i != event->modified_blocks.end(); ++i)
1583 setBlockNotSent(*i);
1588 prof.add("unknown", 1);
1589 infostream<<"WARNING: Server: Unknown MapEditEvent "
1590 <<((u32)event->type)<<std::endl;
1594 Set blocks not sent to far players
1596 if(far_players.size() > 0)
1598 // Convert list format to that wanted by SetBlocksNotSent
1599 std::map<v3s16, MapBlock*> modified_blocks2;
1600 for(std::set<v3s16>::iterator
1601 i = event->modified_blocks.begin();
1602 i != event->modified_blocks.end(); ++i)
1604 modified_blocks2[*i] =
1605 m_env->getMap().getBlockNoCreateNoEx(*i);
1607 // Set blocks not sent
1608 for(std::list<u16>::iterator
1609 i = far_players.begin();
1610 i != far_players.end(); ++i)
1613 RemoteClient *client = getClient(peer_id);
1616 client->SetBlocksNotSent(modified_blocks2);
1622 /*// Don't send too many at a time
1624 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1628 if(event_count >= 5){
1629 infostream<<"Server: MapEditEvents:"<<std::endl;
1630 prof.print(infostream);
1631 } else if(event_count != 0){
1632 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1633 prof.print(verbosestream);
1639 Trigger emergethread (it somehow gets to a non-triggered but
1640 bysy state sometimes)
1643 float &counter = m_emergethread_trigger_timer;
1649 for (unsigned int i = 0; i != m_emerge->emergethread.size(); i++)
1650 m_emerge->emergethread[i]->trigger();
1652 // Update m_enable_rollback_recording here too
1653 m_enable_rollback_recording =
1654 g_settings->getBool("enable_rollback_recording");
1658 // Save map, players and auth stuff
1660 float &counter = m_savemap_timer;
1662 if(counter >= g_settings->getFloat("server_map_save_interval"))
1665 JMutexAutoLock lock(m_env_mutex);
1667 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1670 if(m_banmanager.isModified())
1671 m_banmanager.save();
1673 // Save changed parts of map
1674 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1677 m_env->serializePlayers(m_path_world);
1679 // Save environment metadata
1680 m_env->saveMeta(m_path_world);
1685 void Server::Receive()
1687 DSTACK(__FUNCTION_NAME);
1688 SharedBuffer<u8> data;
1693 JMutexAutoLock conlock(m_con_mutex);
1694 datasize = m_con.Receive(peer_id, data);
1697 // This has to be called so that the client list gets synced
1698 // with the peer list of the connection
1699 handlePeerChanges();
1701 ProcessData(*data, datasize, peer_id);
1703 catch(con::InvalidIncomingDataException &e)
1705 infostream<<"Server::Receive(): "
1706 "InvalidIncomingDataException: what()="
1707 <<e.what()<<std::endl;
1709 catch(con::PeerNotFoundException &e)
1711 //NOTE: This is not needed anymore
1713 // The peer has been disconnected.
1714 // Find the associated player and remove it.
1716 /*JMutexAutoLock envlock(m_env_mutex);
1718 infostream<<"ServerThread: peer_id="<<peer_id
1719 <<" has apparently closed connection. "
1720 <<"Removing player."<<std::endl;
1722 m_env->removePlayer(peer_id);*/
1726 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1728 DSTACK(__FUNCTION_NAME);
1729 // Environment is locked first.
1730 JMutexAutoLock envlock(m_env_mutex);
1731 JMutexAutoLock conlock(m_con_mutex);
1733 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1736 Address address = m_con.GetPeerAddress(peer_id);
1737 std::string addr_s = address.serializeString();
1739 // drop player if is ip is banned
1740 if(m_banmanager.isIpBanned(addr_s)){
1741 infostream<<"Server: A banned client tried to connect from "
1742 <<addr_s<<"; banned name was "
1743 <<m_banmanager.getBanName(addr_s)<<std::endl;
1744 // This actually doesn't seem to transfer to the client
1745 SendAccessDenied(m_con, peer_id,
1746 L"Your ip is banned. Banned name was "
1747 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1748 m_con.DeletePeer(peer_id);
1752 catch(con::PeerNotFoundException &e)
1754 infostream<<"Server::ProcessData(): Cancelling: peer "
1755 <<peer_id<<" not found"<<std::endl;
1759 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1761 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1769 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1771 if(command == TOSERVER_INIT)
1773 // [0] u16 TOSERVER_INIT
1774 // [2] u8 SER_FMT_VER_HIGHEST
1775 // [3] u8[20] player_name
1776 // [23] u8[28] password <--- can be sent without this, from old versions
1778 if(datasize < 2+1+PLAYERNAME_SIZE)
1781 verbosestream<<"Server: Got TOSERVER_INIT from "
1782 <<peer_id<<std::endl;
1784 // First byte after command is maximum supported
1785 // serialization version
1786 u8 client_max = data[2];
1787 u8 our_max = SER_FMT_VER_HIGHEST;
1788 // Use the highest version supported by both
1789 u8 deployed = std::min(client_max, our_max);
1790 // If it's lower than the lowest supported, give up.
1791 if(deployed < SER_FMT_VER_LOWEST)
1792 deployed = SER_FMT_VER_INVALID;
1794 //peer->serialization_version = deployed;
1795 getClient(peer_id)->pending_serialization_version = deployed;
1797 if(deployed == SER_FMT_VER_INVALID)
1799 actionstream<<"Server: A mismatched client tried to connect from "
1800 <<addr_s<<std::endl;
1801 infostream<<"Server: Cannot negotiate "
1802 "serialization version with peer "
1803 <<peer_id<<std::endl;
1804 SendAccessDenied(m_con, peer_id, std::wstring(
1805 L"Your client's version is not supported.\n"
1806 L"Server version is ")
1807 + narrow_to_wide(VERSION_STRING) + L"."
1813 Read and check network protocol version
1816 u16 min_net_proto_version = 0;
1817 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1818 min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1820 // Use same version as minimum and maximum if maximum version field
1821 // doesn't exist (backwards compatibility)
1822 u16 max_net_proto_version = min_net_proto_version;
1823 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
1824 max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
1826 // Start with client's maximum version
1827 u16 net_proto_version = max_net_proto_version;
1829 // Figure out a working version if it is possible at all
1830 if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
1831 min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
1833 // If maximum is larger than our maximum, go with our maximum
1834 if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1835 net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
1836 // Else go with client's maximum
1838 net_proto_version = max_net_proto_version;
1841 verbosestream<<"Server: "<<peer_id<<" Protocol version: min: "
1842 <<min_net_proto_version<<", max: "<<max_net_proto_version
1843 <<", chosen: "<<net_proto_version<<std::endl;
1845 getClient(peer_id)->net_proto_version = net_proto_version;
1847 if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
1848 net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
1850 actionstream<<"Server: A mismatched client tried to connect from "<<addr_s
1852 SendAccessDenied(m_con, peer_id, std::wstring(
1853 L"Your client's version is not supported.\n"
1854 L"Server version is ")
1855 + narrow_to_wide(VERSION_STRING) + L",\n"
1856 + L"server's PROTOCOL_VERSION is "
1857 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
1859 + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
1860 + L", client's PROTOCOL_VERSION is "
1861 + narrow_to_wide(itos(min_net_proto_version))
1863 + narrow_to_wide(itos(max_net_proto_version))
1868 if(g_settings->getBool("strict_protocol_version_checking"))
1870 if(net_proto_version != LATEST_PROTOCOL_VERSION)
1872 actionstream<<"Server: A mismatched (strict) client tried to "
1873 <<"connect from "<<addr_s<<std::endl;
1874 SendAccessDenied(m_con, peer_id, std::wstring(
1875 L"Your client's version is not supported.\n"
1876 L"Server version is ")
1877 + narrow_to_wide(VERSION_STRING) + L",\n"
1878 + L"server's PROTOCOL_VERSION (strict) is "
1879 + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
1880 + L", client's PROTOCOL_VERSION is "
1881 + narrow_to_wide(itos(min_net_proto_version))
1883 + narrow_to_wide(itos(max_net_proto_version))
1894 char playername[PLAYERNAME_SIZE];
1895 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1897 playername[i] = data[3+i];
1899 playername[PLAYERNAME_SIZE-1] = 0;
1901 if(playername[0]=='\0')
1903 actionstream<<"Server: Player with an empty name "
1904 <<"tried to connect from "<<addr_s<<std::endl;
1905 SendAccessDenied(m_con, peer_id,
1910 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1912 actionstream<<"Server: Player with an invalid name "
1913 <<"tried to connect from "<<addr_s<<std::endl;
1914 SendAccessDenied(m_con, peer_id,
1915 L"Name contains unallowed characters");
1919 infostream<<"Server: New connection: \""<<playername<<"\" from "
1920 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
1923 char given_password[PASSWORD_SIZE];
1924 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1926 // old version - assume blank password
1927 given_password[0] = 0;
1931 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1933 given_password[i] = data[23+i];
1935 given_password[PASSWORD_SIZE-1] = 0;
1938 if(!base64_is_valid(given_password)){
1939 infostream<<"Server: "<<playername
1940 <<" supplied invalid password hash"<<std::endl;
1941 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
1945 std::string checkpwd; // Password hash to check against
1946 bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1948 // If no authentication info exists for user, create it
1950 if(!isSingleplayer() &&
1951 g_settings->getBool("disallow_empty_password") &&
1952 std::string(given_password) == ""){
1953 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
1954 L"disallowed. Set a password and try again.");
1957 std::wstring raw_default_password =
1958 narrow_to_wide(g_settings->get("default_password"));
1959 std::string initial_password =
1960 translatePassword(playername, raw_default_password);
1962 // If default_password is empty, allow any initial password
1963 if (raw_default_password.length() == 0)
1964 initial_password = given_password;
1966 m_script->createAuth(playername, initial_password);
1969 has_auth = m_script->getAuth(playername, &checkpwd, NULL);
1972 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
1976 if(given_password != checkpwd){
1977 infostream<<"Server: peer_id="<<peer_id
1978 <<": supplied invalid password for "
1979 <<playername<<std::endl;
1980 SendAccessDenied(m_con, peer_id, L"Invalid password");
1984 // Do not allow multiple players in simple singleplayer mode.
1985 // This isn't a perfect way to do it, but will suffice for now.
1986 if(m_simple_singleplayer_mode && m_clients.size() > 1){
1987 infostream<<"Server: Not allowing another client to connect in"
1988 <<" simple singleplayer mode"<<std::endl;
1989 SendAccessDenied(m_con, peer_id,
1990 L"Running in simple singleplayer mode.");
1994 // Enforce user limit.
1995 // Don't enforce for users that have some admin right
1996 if(m_clients.size() >= g_settings->getU16("max_users") &&
1997 !checkPriv(playername, "server") &&
1998 !checkPriv(playername, "ban") &&
1999 !checkPriv(playername, "privs") &&
2000 !checkPriv(playername, "password") &&
2001 playername != g_settings->get("name"))
2003 actionstream<<"Server: "<<playername<<" tried to join, but there"
2004 <<" are already max_users="
2005 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2006 SendAccessDenied(m_con, peer_id, L"Too many users.");
2011 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2013 // If failed, cancel
2014 if(playersao == NULL)
2016 errorstream<<"Server: peer_id="<<peer_id
2017 <<": failed to emerge player"<<std::endl;
2022 Answer with a TOCLIENT_INIT
2025 SharedBuffer<u8> reply(2+1+6+8+4);
2026 writeU16(&reply[0], TOCLIENT_INIT);
2027 writeU8(&reply[2], deployed);
2028 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2029 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2030 writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
2033 m_con.Send(peer_id, 0, reply, true);
2037 Send complete position information
2039 SendMovePlayer(peer_id);
2044 if(command == TOSERVER_INIT2)
2046 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2047 <<peer_id<<std::endl;
2049 Player *player = m_env->getPlayer(peer_id);
2051 verbosestream<<"Server: TOSERVER_INIT2: "
2052 <<"Player not found; ignoring."<<std::endl;
2056 RemoteClient *client = getClient(peer_id);
2057 client->serialization_version =
2058 getClient(peer_id)->pending_serialization_version;
2061 Send some initialization data
2064 infostream<<"Server: Sending content to "
2065 <<getPlayerName(peer_id)<<std::endl;
2067 // Send player movement settings
2068 SendMovement(m_con, peer_id);
2070 // Send item definitions
2071 SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version);
2073 // Send node definitions
2074 SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version);
2076 // Send media announcement
2077 sendMediaAnnouncement(peer_id);
2080 SendPlayerPrivileges(peer_id);
2082 // Send inventory formspec
2083 SendPlayerInventoryFormspec(peer_id);
2086 UpdateCrafting(peer_id);
2087 SendInventory(peer_id);
2090 if(g_settings->getBool("enable_damage"))
2091 SendPlayerHP(peer_id);
2093 // Send detached inventories
2094 sendDetachedInventories(peer_id);
2096 // Show death screen if necessary
2098 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2102 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2103 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2104 m_con.Send(peer_id, 0, data, true);
2107 // Note things in chat if not in simple singleplayer mode
2108 if(!m_simple_singleplayer_mode)
2110 // Send information about server to player in chat
2111 SendChatMessage(peer_id, getStatusString());
2113 // Send information about joining in chat
2115 std::wstring name = L"unknown";
2116 Player *player = m_env->getPlayer(peer_id);
2118 name = narrow_to_wide(player->getName());
2120 std::wstring message;
2123 message += L" joined the game.";
2124 BroadcastChatMessage(message);
2128 // Warnings about protocol version can be issued here
2129 if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
2131 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
2132 L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
2139 std::ostringstream os(std::ios_base::binary);
2140 for(std::map<u16, RemoteClient*>::iterator
2141 i = m_clients.begin();
2142 i != m_clients.end(); ++i)
2144 RemoteClient *client = i->second;
2145 assert(client->peer_id == i->first);
2146 if(client->serialization_version == SER_FMT_VER_INVALID)
2149 Player *player = m_env->getPlayer(client->peer_id);
2152 // Get name of player
2153 os<<player->getName()<<" ";
2156 actionstream<<player->getName()<<" joins game. List of players: "
2157 <<os.str()<<std::endl;
2163 if(peer_ser_ver == SER_FMT_VER_INVALID)
2165 infostream<<"Server::ProcessData(): Cancelling: Peer"
2166 " serialization format invalid or not initialized."
2167 " Skipping incoming command="<<command<<std::endl;
2171 Player *player = m_env->getPlayer(peer_id);
2173 infostream<<"Server::ProcessData(): Cancelling: "
2174 "No player for peer_id="<<peer_id
2179 PlayerSAO *playersao = player->getPlayerSAO();
2180 if(playersao == NULL){
2181 infostream<<"Server::ProcessData(): Cancelling: "
2182 "No player object for peer_id="<<peer_id
2187 if(command == TOSERVER_PLAYERPOS)
2189 if(datasize < 2+12+12+4+4)
2193 v3s32 ps = readV3S32(&data[start+2]);
2194 v3s32 ss = readV3S32(&data[start+2+12]);
2195 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2196 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2198 if(datasize >= 2+12+12+4+4+4)
2199 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2200 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2201 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2202 pitch = wrapDegrees(pitch);
2203 yaw = wrapDegrees(yaw);
2205 player->setPosition(position);
2206 player->setSpeed(speed);
2207 player->setPitch(pitch);
2208 player->setYaw(yaw);
2209 player->keyPressed=keyPressed;
2210 player->control.up = (bool)(keyPressed&1);
2211 player->control.down = (bool)(keyPressed&2);
2212 player->control.left = (bool)(keyPressed&4);
2213 player->control.right = (bool)(keyPressed&8);
2214 player->control.jump = (bool)(keyPressed&16);
2215 player->control.aux1 = (bool)(keyPressed&32);
2216 player->control.sneak = (bool)(keyPressed&64);
2217 player->control.LMB = (bool)(keyPressed&128);
2218 player->control.RMB = (bool)(keyPressed&256);
2220 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2221 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2222 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2224 else if(command == TOSERVER_GOTBLOCKS)
2237 u16 count = data[2];
2238 for(u16 i=0; i<count; i++)
2240 if((s16)datasize < 2+1+(i+1)*6)
2241 throw con::InvalidIncomingDataException
2242 ("GOTBLOCKS length is too short");
2243 v3s16 p = readV3S16(&data[2+1+i*6]);
2244 /*infostream<<"Server: GOTBLOCKS ("
2245 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2246 RemoteClient *client = getClient(peer_id);
2247 client->GotBlock(p);
2250 else if(command == TOSERVER_DELETEDBLOCKS)
2263 u16 count = data[2];
2264 for(u16 i=0; i<count; i++)
2266 if((s16)datasize < 2+1+(i+1)*6)
2267 throw con::InvalidIncomingDataException
2268 ("DELETEDBLOCKS length is too short");
2269 v3s16 p = readV3S16(&data[2+1+i*6]);
2270 /*infostream<<"Server: DELETEDBLOCKS ("
2271 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2272 RemoteClient *client = getClient(peer_id);
2273 client->SetBlockNotSent(p);
2276 else if(command == TOSERVER_CLICK_OBJECT)
2278 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2281 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2283 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2286 else if(command == TOSERVER_GROUND_ACTION)
2288 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2292 else if(command == TOSERVER_RELEASE)
2294 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2297 else if(command == TOSERVER_SIGNTEXT)
2299 infostream<<"Server: SIGNTEXT not supported anymore"
2303 else if(command == TOSERVER_SIGNNODETEXT)
2305 infostream<<"Server: SIGNNODETEXT not supported anymore"
2309 else if(command == TOSERVER_INVENTORY_ACTION)
2311 // Strip command and create a stream
2312 std::string datastring((char*)&data[2], datasize-2);
2313 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2314 std::istringstream is(datastring, std::ios_base::binary);
2316 InventoryAction *a = InventoryAction::deSerialize(is);
2319 infostream<<"TOSERVER_INVENTORY_ACTION: "
2320 <<"InventoryAction::deSerialize() returned NULL"
2325 // If something goes wrong, this player is to blame
2326 RollbackScopeActor rollback_scope(m_rollback,
2327 std::string("player:")+player->getName());
2330 Note: Always set inventory not sent, to repair cases
2331 where the client made a bad prediction.
2335 Handle restrictions and special cases of the move action
2337 if(a->getType() == IACTION_MOVE)
2339 IMoveAction *ma = (IMoveAction*)a;
2341 ma->from_inv.applyCurrentPlayer(player->getName());
2342 ma->to_inv.applyCurrentPlayer(player->getName());
2344 setInventoryModified(ma->from_inv);
2345 setInventoryModified(ma->to_inv);
2347 bool from_inv_is_current_player =
2348 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2349 (ma->from_inv.name == player->getName());
2351 bool to_inv_is_current_player =
2352 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2353 (ma->to_inv.name == player->getName());
2356 Disable moving items out of craftpreview
2358 if(ma->from_list == "craftpreview")
2360 infostream<<"Ignoring IMoveAction from "
2361 <<(ma->from_inv.dump())<<":"<<ma->from_list
2362 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2363 <<" because src is "<<ma->from_list<<std::endl;
2369 Disable moving items into craftresult and craftpreview
2371 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2373 infostream<<"Ignoring IMoveAction from "
2374 <<(ma->from_inv.dump())<<":"<<ma->from_list
2375 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2376 <<" because dst is "<<ma->to_list<<std::endl;
2381 // Disallow moving items in elsewhere than player's inventory
2382 // if not allowed to interact
2383 if(!checkPriv(player->getName(), "interact") &&
2384 (!from_inv_is_current_player ||
2385 !to_inv_is_current_player))
2387 infostream<<"Cannot move outside of player's inventory: "
2388 <<"No interact privilege"<<std::endl;
2394 Handle restrictions and special cases of the drop action
2396 else if(a->getType() == IACTION_DROP)
2398 IDropAction *da = (IDropAction*)a;
2400 da->from_inv.applyCurrentPlayer(player->getName());
2402 setInventoryModified(da->from_inv);
2405 Disable dropping items out of craftpreview
2407 if(da->from_list == "craftpreview")
2409 infostream<<"Ignoring IDropAction from "
2410 <<(da->from_inv.dump())<<":"<<da->from_list
2411 <<" because src is "<<da->from_list<<std::endl;
2416 // Disallow dropping items if not allowed to interact
2417 if(!checkPriv(player->getName(), "interact"))
2424 Handle restrictions and special cases of the craft action
2426 else if(a->getType() == IACTION_CRAFT)
2428 ICraftAction *ca = (ICraftAction*)a;
2430 ca->craft_inv.applyCurrentPlayer(player->getName());
2432 setInventoryModified(ca->craft_inv);
2434 //bool craft_inv_is_current_player =
2435 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2436 // (ca->craft_inv.name == player->getName());
2438 // Disallow crafting if not allowed to interact
2439 if(!checkPriv(player->getName(), "interact"))
2441 infostream<<"Cannot craft: "
2442 <<"No interact privilege"<<std::endl;
2449 a->apply(this, playersao, this);
2453 else if(command == TOSERVER_CHAT_MESSAGE)
2461 std::string datastring((char*)&data[2], datasize-2);
2462 std::istringstream is(datastring, std::ios_base::binary);
2465 is.read((char*)buf, 2);
2466 u16 len = readU16(buf);
2468 std::wstring message;
2469 for(u16 i=0; i<len; i++)
2471 is.read((char*)buf, 2);
2472 message += (wchar_t)readU16(buf);
2475 // If something goes wrong, this player is to blame
2476 RollbackScopeActor rollback_scope(m_rollback,
2477 std::string("player:")+player->getName());
2479 // Get player name of this client
2480 std::wstring name = narrow_to_wide(player->getName());
2483 bool ate = m_script->on_chat_message(player->getName(),
2484 wide_to_narrow(message));
2485 // If script ate the message, don't proceed
2489 // Line to send to players
2491 // Whether to send to the player that sent the line
2492 bool send_to_sender = false;
2493 // Whether to send to other players
2494 bool send_to_others = false;
2496 // Commands are implemented in Lua, so only catch invalid
2497 // commands that were not "eaten" and send an error back
2498 if(message[0] == L'/')
2500 message = message.substr(1);
2501 send_to_sender = true;
2502 if(message.length() == 0)
2503 line += L"-!- Empty command";
2505 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2509 if(checkPriv(player->getName(), "shout")){
2514 send_to_others = true;
2516 line += L"-!- You don't have permission to shout.";
2517 send_to_sender = true;
2524 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2527 Send the message to clients
2529 for(std::map<u16, RemoteClient*>::iterator
2530 i = m_clients.begin();
2531 i != m_clients.end(); ++i)
2533 // Get client and check that it is valid
2534 RemoteClient *client = i->second;
2535 assert(client->peer_id == i->first);
2536 if(client->serialization_version == SER_FMT_VER_INVALID)
2540 bool sender_selected = (peer_id == client->peer_id);
2541 if(sender_selected == true && send_to_sender == false)
2543 if(sender_selected == false && send_to_others == false)
2546 SendChatMessage(client->peer_id, line);
2550 else if(command == TOSERVER_DAMAGE)
2552 std::string datastring((char*)&data[2], datasize-2);
2553 std::istringstream is(datastring, std::ios_base::binary);
2554 u8 damage = readU8(is);
2556 if(g_settings->getBool("enable_damage"))
2558 actionstream<<player->getName()<<" damaged by "
2559 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2562 playersao->setHP(playersao->getHP() - damage);
2564 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2567 if(playersao->m_hp_not_sent)
2568 SendPlayerHP(peer_id);
2571 else if(command == TOSERVER_PASSWORD)
2574 [0] u16 TOSERVER_PASSWORD
2575 [2] u8[28] old password
2576 [30] u8[28] new password
2579 if(datasize != 2+PASSWORD_SIZE*2)
2581 /*char password[PASSWORD_SIZE];
2582 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2583 password[i] = data[2+i];
2584 password[PASSWORD_SIZE-1] = 0;*/
2586 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2594 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2596 char c = data[2+PASSWORD_SIZE+i];
2602 if(!base64_is_valid(newpwd)){
2603 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2604 // Wrong old password supplied!!
2605 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2609 infostream<<"Server: Client requests a password change from "
2610 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2612 std::string playername = player->getName();
2614 std::string checkpwd;
2615 m_script->getAuth(playername, &checkpwd, NULL);
2617 if(oldpwd != checkpwd)
2619 infostream<<"Server: invalid old password"<<std::endl;
2620 // Wrong old password supplied!!
2621 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2625 bool success = m_script->setPassword(playername, newpwd);
2627 actionstream<<player->getName()<<" changes password"<<std::endl;
2628 SendChatMessage(peer_id, L"Password change successful.");
2630 actionstream<<player->getName()<<" tries to change password but "
2631 <<"it fails"<<std::endl;
2632 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2635 else if(command == TOSERVER_PLAYERITEM)
2640 u16 item = readU16(&data[2]);
2641 playersao->setWieldIndex(item);
2643 else if(command == TOSERVER_RESPAWN)
2645 if(player->hp != 0 || !g_settings->getBool("enable_damage"))
2648 RespawnPlayer(peer_id);
2650 actionstream<<player->getName()<<" respawns at "
2651 <<PP(player->getPosition()/BS)<<std::endl;
2653 // ActiveObject is added to environment in AsyncRunStep after
2654 // the previous addition has been succesfully removed
2656 else if(command == TOSERVER_REQUEST_MEDIA) {
2657 std::string datastring((char*)&data[2], datasize-2);
2658 std::istringstream is(datastring, std::ios_base::binary);
2660 std::list<MediaRequest> tosend;
2661 u16 numfiles = readU16(is);
2663 infostream<<"Sending "<<numfiles<<" files to "
2664 <<getPlayerName(peer_id)<<std::endl;
2665 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2667 for(int i = 0; i < numfiles; i++) {
2668 std::string name = deSerializeString(is);
2669 tosend.push_back(MediaRequest(name));
2670 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2674 sendRequestedMedia(peer_id, tosend);
2676 // Now the client should know about everything
2677 // (definitions and files)
2678 getClient(peer_id)->definitions_sent = true;
2680 else if(command == TOSERVER_RECEIVED_MEDIA) {
2681 getClient(peer_id)->definitions_sent = true;
2683 else if(command == TOSERVER_INTERACT)
2685 std::string datastring((char*)&data[2], datasize-2);
2686 std::istringstream is(datastring, std::ios_base::binary);
2692 [5] u32 length of the next item
2693 [9] serialized PointedThing
2695 0: start digging (from undersurface) or use
2696 1: stop digging (all parameters ignored)
2697 2: digging completed
2698 3: place block or item (to abovesurface)
2701 u8 action = readU8(is);
2702 u16 item_i = readU16(is);
2703 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2704 PointedThing pointed;
2705 pointed.deSerialize(tmp_is);
2707 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2708 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2712 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2713 <<" tried to interact, but is dead!"<<std::endl;
2717 v3f player_pos = playersao->getLastGoodPosition();
2719 // Update wielded item
2720 playersao->setWieldIndex(item_i);
2722 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2723 v3s16 p_under = pointed.node_undersurface;
2724 v3s16 p_above = pointed.node_abovesurface;
2726 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2727 ServerActiveObject *pointed_object = NULL;
2728 if(pointed.type == POINTEDTHING_OBJECT)
2730 pointed_object = m_env->getActiveObject(pointed.object_id);
2731 if(pointed_object == NULL)
2733 verbosestream<<"TOSERVER_INTERACT: "
2734 "pointed object is NULL"<<std::endl;
2740 v3f pointed_pos_under = player_pos;
2741 v3f pointed_pos_above = player_pos;
2742 if(pointed.type == POINTEDTHING_NODE)
2744 pointed_pos_under = intToFloat(p_under, BS);
2745 pointed_pos_above = intToFloat(p_above, BS);
2747 else if(pointed.type == POINTEDTHING_OBJECT)
2749 pointed_pos_under = pointed_object->getBasePosition();
2750 pointed_pos_above = pointed_pos_under;
2754 Check that target is reasonably close
2755 (only when digging or placing things)
2757 if(action == 0 || action == 2 || action == 3)
2759 float d = player_pos.getDistanceFrom(pointed_pos_under);
2760 float max_d = BS * 14; // Just some large enough value
2762 actionstream<<"Player "<<player->getName()
2763 <<" tried to access "<<pointed.dump()
2765 <<"d="<<d<<", max_d="<<max_d
2766 <<". ignoring."<<std::endl;
2767 // Re-send block to revert change on client-side
2768 RemoteClient *client = getClient(peer_id);
2769 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2770 client->SetBlockNotSent(blockpos);
2777 Make sure the player is allowed to do it
2779 if(!checkPriv(player->getName(), "interact"))
2781 actionstream<<player->getName()<<" attempted to interact with "
2782 <<pointed.dump()<<" without 'interact' privilege"
2784 // Re-send block to revert change on client-side
2785 RemoteClient *client = getClient(peer_id);
2786 // Digging completed -> under
2788 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2789 client->SetBlockNotSent(blockpos);
2791 // Placement -> above
2793 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2794 client->SetBlockNotSent(blockpos);
2800 If something goes wrong, this player is to blame
2802 RollbackScopeActor rollback_scope(m_rollback,
2803 std::string("player:")+player->getName());
2806 0: start digging or punch object
2810 if(pointed.type == POINTEDTHING_NODE)
2813 NOTE: This can be used in the future to check if
2814 somebody is cheating, by checking the timing.
2816 MapNode n(CONTENT_IGNORE);
2819 n = m_env->getMap().getNode(p_under);
2821 catch(InvalidPositionException &e)
2823 infostream<<"Server: Not punching: Node not found."
2824 <<" Adding block to emerge queue."
2826 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2828 if(n.getContent() != CONTENT_IGNORE)
2829 m_script->node_on_punch(p_under, n, playersao);
2831 playersao->noCheatDigStart(p_under);
2833 else if(pointed.type == POINTEDTHING_OBJECT)
2835 // Skip if object has been removed
2836 if(pointed_object->m_removed)
2839 actionstream<<player->getName()<<" punches object "
2840 <<pointed.object_id<<": "
2841 <<pointed_object->getDescription()<<std::endl;
2843 ItemStack punchitem = playersao->getWieldedItem();
2844 ToolCapabilities toolcap =
2845 punchitem.getToolCapabilities(m_itemdef);
2846 v3f dir = (pointed_object->getBasePosition() -
2847 (player->getPosition() + player->getEyeOffset())
2849 float time_from_last_punch =
2850 playersao->resetTimeFromLastPunch();
2851 pointed_object->punch(dir, &toolcap, playersao,
2852 time_from_last_punch);
2860 else if(action == 1)
2865 2: Digging completed
2867 else if(action == 2)
2869 // Only digging of nodes
2870 if(pointed.type == POINTEDTHING_NODE)
2872 MapNode n(CONTENT_IGNORE);
2875 n = m_env->getMap().getNode(p_under);
2877 catch(InvalidPositionException &e)
2879 infostream<<"Server: Not finishing digging: Node not found."
2880 <<" Adding block to emerge queue."
2882 m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
2885 /* Cheat prevention */
2886 bool is_valid_dig = true;
2887 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
2889 v3s16 nocheat_p = playersao->getNoCheatDigPos();
2890 float nocheat_t = playersao->getNoCheatDigTime();
2891 playersao->noCheatDigEnd();
2892 // If player didn't start digging this, ignore dig
2893 if(nocheat_p != p_under){
2894 infostream<<"Server: NoCheat: "<<player->getName()
2895 <<" started digging "
2896 <<PP(nocheat_p)<<" and completed digging "
2897 <<PP(p_under)<<"; not digging."<<std::endl;
2898 is_valid_dig = false;
2900 // Get player's wielded item
2901 ItemStack playeritem;
2902 InventoryList *mlist = playersao->getInventory()->getList("main");
2904 playeritem = mlist->getItem(playersao->getWieldIndex());
2905 ToolCapabilities playeritem_toolcap =
2906 playeritem.getToolCapabilities(m_itemdef);
2907 // Get diggability and expected digging time
2908 DigParams params = getDigParams(m_nodedef->get(n).groups,
2909 &playeritem_toolcap);
2910 // If can't dig, try hand
2911 if(!params.diggable){
2912 const ItemDefinition &hand = m_itemdef->get("");
2913 const ToolCapabilities *tp = hand.tool_capabilities;
2915 params = getDigParams(m_nodedef->get(n).groups, tp);
2917 // If can't dig, ignore dig
2918 if(!params.diggable){
2919 infostream<<"Server: NoCheat: "<<player->getName()
2920 <<" completed digging "<<PP(p_under)
2921 <<", which is not diggable with tool. not digging."
2923 is_valid_dig = false;
2925 // If time is considerably too short, ignore dig
2926 // Check time only for medium and slow timed digs
2927 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
2928 infostream<<"Server: NoCheat: "<<player->getName()
2929 <<" completed digging "
2930 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
2931 <<params.time<<"s; not digging."<<std::endl;
2932 is_valid_dig = false;
2936 /* Actually dig node */
2938 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
2939 m_script->node_on_dig(p_under, n, playersao);
2941 // Send unusual result (that is, node not being removed)
2942 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
2944 // Re-send block to revert change on client-side
2945 RemoteClient *client = getClient(peer_id);
2946 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2947 client->SetBlockNotSent(blockpos);
2953 3: place block or right-click object
2955 else if(action == 3)
2957 ItemStack item = playersao->getWieldedItem();
2959 // Reset build time counter
2960 if(pointed.type == POINTEDTHING_NODE &&
2961 item.getDefinition(m_itemdef).type == ITEM_NODE)
2962 getClient(peer_id)->m_time_from_building = 0.0;
2964 if(pointed.type == POINTEDTHING_OBJECT)
2966 // Right click object
2968 // Skip if object has been removed
2969 if(pointed_object->m_removed)
2972 actionstream<<player->getName()<<" right-clicks object "
2973 <<pointed.object_id<<": "
2974 <<pointed_object->getDescription()<<std::endl;
2977 pointed_object->rightClick(playersao);
2979 else if(m_script->item_OnPlace(
2980 item, playersao, pointed))
2982 // Placement was handled in lua
2984 // Apply returned ItemStack
2985 playersao->setWieldedItem(item);
2988 // If item has node placement prediction, always send the
2989 // blocks to make sure the client knows what exactly happened
2990 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
2991 RemoteClient *client = getClient(peer_id);
2992 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2993 client->SetBlockNotSent(blockpos);
2994 v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2995 if(blockpos2 != blockpos){
2996 client->SetBlockNotSent(blockpos2);
3004 else if(action == 4)
3006 ItemStack item = playersao->getWieldedItem();
3008 actionstream<<player->getName()<<" uses "<<item.name
3009 <<", pointing at "<<pointed.dump()<<std::endl;
3011 if(m_script->item_OnUse(
3012 item, playersao, pointed))
3014 // Apply returned ItemStack
3015 playersao->setWieldedItem(item);
3022 Catch invalid actions
3026 infostream<<"WARNING: Server: Invalid action "
3027 <<action<<std::endl;
3030 else if(command == TOSERVER_REMOVED_SOUNDS)
3032 std::string datastring((char*)&data[2], datasize-2);
3033 std::istringstream is(datastring, std::ios_base::binary);
3035 int num = readU16(is);
3036 for(int k=0; k<num; k++){
3037 s32 id = readS32(is);
3038 std::map<s32, ServerPlayingSound>::iterator i =
3039 m_playing_sounds.find(id);
3040 if(i == m_playing_sounds.end())
3042 ServerPlayingSound &psound = i->second;
3043 psound.clients.erase(peer_id);
3044 if(psound.clients.size() == 0)
3045 m_playing_sounds.erase(i++);
3048 else if(command == TOSERVER_NODEMETA_FIELDS)
3050 std::string datastring((char*)&data[2], datasize-2);
3051 std::istringstream is(datastring, std::ios_base::binary);
3053 v3s16 p = readV3S16(is);
3054 std::string formname = deSerializeString(is);
3055 int num = readU16(is);
3056 std::map<std::string, std::string> fields;
3057 for(int k=0; k<num; k++){
3058 std::string fieldname = deSerializeString(is);
3059 std::string fieldvalue = deSerializeLongString(is);
3060 fields[fieldname] = fieldvalue;
3063 // If something goes wrong, this player is to blame
3064 RollbackScopeActor rollback_scope(m_rollback,
3065 std::string("player:")+player->getName());
3067 // Check the target node for rollback data; leave others unnoticed
3068 RollbackNode rn_old(&m_env->getMap(), p, this);
3070 m_script->node_on_receive_fields(p, formname, fields,playersao);
3072 // Report rollback data
3073 RollbackNode rn_new(&m_env->getMap(), p, this);
3074 if(rollback() && rn_new != rn_old){
3075 RollbackAction action;
3076 action.setSetNode(p, rn_old, rn_new);
3077 rollback()->reportAction(action);
3080 else if(command == TOSERVER_INVENTORY_FIELDS)
3082 std::string datastring((char*)&data[2], datasize-2);
3083 std::istringstream is(datastring, std::ios_base::binary);
3085 std::string formname = deSerializeString(is);
3086 int num = readU16(is);
3087 std::map<std::string, std::string> fields;
3088 for(int k=0; k<num; k++){
3089 std::string fieldname = deSerializeString(is);
3090 std::string fieldvalue = deSerializeLongString(is);
3091 fields[fieldname] = fieldvalue;
3094 m_script->on_playerReceiveFields(playersao, formname, fields);
3098 infostream<<"Server::ProcessData(): Ignoring "
3099 "unknown command "<<command<<std::endl;
3103 catch(SendFailedException &e)
3105 errorstream<<"Server::ProcessData(): SendFailedException: "
3111 void Server::onMapEditEvent(MapEditEvent *event)
3113 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3114 if(m_ignore_map_edit_events)
3116 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3118 MapEditEvent *e = event->clone();
3119 m_unsent_map_edit_queue.push_back(e);
3122 Inventory* Server::getInventory(const InventoryLocation &loc)
3125 case InventoryLocation::UNDEFINED:
3128 case InventoryLocation::CURRENT_PLAYER:
3131 case InventoryLocation::PLAYER:
3133 Player *player = m_env->getPlayer(loc.name.c_str());
3136 PlayerSAO *playersao = player->getPlayerSAO();
3139 return playersao->getInventory();
3142 case InventoryLocation::NODEMETA:
3144 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3147 return meta->getInventory();
3150 case InventoryLocation::DETACHED:
3152 if(m_detached_inventories.count(loc.name) == 0)
3154 return m_detached_inventories[loc.name];
3162 void Server::setInventoryModified(const InventoryLocation &loc)
3165 case InventoryLocation::UNDEFINED:
3168 case InventoryLocation::PLAYER:
3170 Player *player = m_env->getPlayer(loc.name.c_str());
3173 PlayerSAO *playersao = player->getPlayerSAO();
3176 playersao->m_inventory_not_sent = true;
3177 playersao->m_wielded_item_not_sent = true;
3180 case InventoryLocation::NODEMETA:
3182 v3s16 blockpos = getNodeBlockPos(loc.p);
3184 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3186 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3188 setBlockNotSent(blockpos);
3191 case InventoryLocation::DETACHED:
3193 sendDetachedInventoryToAll(loc.name);
3201 //std::list<PlayerInfo> Server::getPlayerInfo()
3203 // DSTACK(__FUNCTION_NAME);
3204 // JMutexAutoLock envlock(m_env_mutex);
3205 // JMutexAutoLock conlock(m_con_mutex);
3207 // std::list<PlayerInfo> list;
3209 // std::list<Player*> players = m_env->getPlayers();
3211 // std::list<Player*>::iterator i;
3212 // for(i = players.begin();
3213 // i != players.end(); ++i)
3217 // Player *player = *i;
3220 // // Copy info from connection to info struct
3221 // info.id = player->peer_id;
3222 // info.address = m_con.GetPeerAddress(player->peer_id);
3223 // info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3225 // catch(con::PeerNotFoundException &e)
3227 // // Set dummy peer info
3229 // info.address = Address(0,0,0,0,0);
3230 // info.avg_rtt = 0.0;
3233 // snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3234 // info.position = player->getPosition();
3236 // list.push_back(info);
3243 void Server::peerAdded(con::Peer *peer)
3245 DSTACK(__FUNCTION_NAME);
3246 verbosestream<<"Server::peerAdded(): peer->id="
3247 <<peer->id<<std::endl;
3250 c.type = PEER_ADDED;
3251 c.peer_id = peer->id;
3253 m_peer_change_queue.push_back(c);
3256 void Server::deletingPeer(con::Peer *peer, bool timeout)
3258 DSTACK(__FUNCTION_NAME);
3259 verbosestream<<"Server::deletingPeer(): peer->id="
3260 <<peer->id<<", timeout="<<timeout<<std::endl;
3263 c.type = PEER_REMOVED;
3264 c.peer_id = peer->id;
3265 c.timeout = timeout;
3266 m_peer_change_queue.push_back(c);
3273 void Server::SendMovement(con::Connection &con, u16 peer_id)
3275 DSTACK(__FUNCTION_NAME);
3276 std::ostringstream os(std::ios_base::binary);
3278 writeU16(os, TOCLIENT_MOVEMENT);
3279 writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
3280 writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
3281 writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
3282 writeF1000(os, g_settings->getFloat("movement_speed_walk"));
3283 writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
3284 writeF1000(os, g_settings->getFloat("movement_speed_fast"));
3285 writeF1000(os, g_settings->getFloat("movement_speed_climb"));
3286 writeF1000(os, g_settings->getFloat("movement_speed_jump"));
3287 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
3288 writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
3289 writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
3290 writeF1000(os, g_settings->getFloat("movement_gravity"));
3293 std::string s = os.str();
3294 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3296 con.Send(peer_id, 0, data, true);
3299 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3301 DSTACK(__FUNCTION_NAME);
3302 std::ostringstream os(std::ios_base::binary);
3304 writeU16(os, TOCLIENT_HP);
3308 std::string s = os.str();
3309 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3311 con.Send(peer_id, 0, data, true);
3314 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3315 const std::wstring &reason)
3317 DSTACK(__FUNCTION_NAME);
3318 std::ostringstream os(std::ios_base::binary);
3320 writeU16(os, TOCLIENT_ACCESS_DENIED);
3321 os<<serializeWideString(reason);
3324 std::string s = os.str();
3325 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3327 con.Send(peer_id, 0, data, true);
3330 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3331 bool set_camera_point_target, v3f camera_point_target)
3333 DSTACK(__FUNCTION_NAME);
3334 std::ostringstream os(std::ios_base::binary);
3336 writeU16(os, TOCLIENT_DEATHSCREEN);
3337 writeU8(os, set_camera_point_target);
3338 writeV3F1000(os, camera_point_target);
3341 std::string s = os.str();
3342 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3344 con.Send(peer_id, 0, data, true);
3347 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3348 IItemDefManager *itemdef, u16 protocol_version)
3350 DSTACK(__FUNCTION_NAME);
3351 std::ostringstream os(std::ios_base::binary);
3355 u32 length of the next item
3356 zlib-compressed serialized ItemDefManager
3358 writeU16(os, TOCLIENT_ITEMDEF);
3359 std::ostringstream tmp_os(std::ios::binary);
3360 itemdef->serialize(tmp_os, protocol_version);
3361 std::ostringstream tmp_os2(std::ios::binary);
3362 compressZlib(tmp_os.str(), tmp_os2);
3363 os<<serializeLongString(tmp_os2.str());
3366 std::string s = os.str();
3367 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3368 <<"): size="<<s.size()<<std::endl;
3369 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3371 con.Send(peer_id, 0, data, true);
3374 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3375 INodeDefManager *nodedef, u16 protocol_version)
3377 DSTACK(__FUNCTION_NAME);
3378 std::ostringstream os(std::ios_base::binary);
3382 u32 length of the next item
3383 zlib-compressed serialized NodeDefManager
3385 writeU16(os, TOCLIENT_NODEDEF);
3386 std::ostringstream tmp_os(std::ios::binary);
3387 nodedef->serialize(tmp_os, protocol_version);
3388 std::ostringstream tmp_os2(std::ios::binary);
3389 compressZlib(tmp_os.str(), tmp_os2);
3390 os<<serializeLongString(tmp_os2.str());
3393 std::string s = os.str();
3394 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3395 <<"): size="<<s.size()<<std::endl;
3396 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3398 con.Send(peer_id, 0, data, true);
3402 Non-static send methods
3405 void Server::SendInventory(u16 peer_id)
3407 DSTACK(__FUNCTION_NAME);
3409 PlayerSAO *playersao = getPlayerSAO(peer_id);
3412 playersao->m_inventory_not_sent = false;
3418 std::ostringstream os;
3419 playersao->getInventory()->serialize(os);
3421 std::string s = os.str();
3423 SharedBuffer<u8> data(s.size()+2);
3424 writeU16(&data[0], TOCLIENT_INVENTORY);
3425 memcpy(&data[2], s.c_str(), s.size());
3428 m_con.Send(peer_id, 0, data, true);
3431 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3433 DSTACK(__FUNCTION_NAME);
3435 std::ostringstream os(std::ios_base::binary);
3439 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3440 os.write((char*)buf, 2);
3443 writeU16(buf, message.size());
3444 os.write((char*)buf, 2);
3447 for(u32 i=0; i<message.size(); i++)
3451 os.write((char*)buf, 2);
3455 std::string s = os.str();
3456 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3458 m_con.Send(peer_id, 0, data, true);
3461 void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec,
3462 const std::string formname)
3464 DSTACK(__FUNCTION_NAME);
3466 std::ostringstream os(std::ios_base::binary);
3470 writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
3471 os.write((char*)buf, 2);
3472 os<<serializeLongString(formspec);
3473 os<<serializeString(formname);
3476 std::string s = os.str();
3477 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3479 m_con.Send(peer_id, 0, data, true);
3482 // Spawns a particle on peer with peer_id
3483 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
3484 float expirationtime, float size, bool collisiondetection,
3485 std::string texture)
3487 DSTACK(__FUNCTION_NAME);
3489 std::ostringstream os(std::ios_base::binary);
3490 writeU16(os, TOCLIENT_SPAWN_PARTICLE);
3491 writeV3F1000(os, pos);
3492 writeV3F1000(os, velocity);
3493 writeV3F1000(os, acceleration);
3494 writeF1000(os, expirationtime);
3495 writeF1000(os, size);
3496 writeU8(os, collisiondetection);
3497 os<<serializeLongString(texture);
3500 std::string s = os.str();
3501 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3503 m_con.Send(peer_id, 0, data, true);
3506 // Spawns a particle on all peers
3507 void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
3508 float expirationtime, float size, bool collisiondetection,
3509 std::string texture)
3511 for(std::map<u16, RemoteClient*>::iterator
3512 i = m_clients.begin();
3513 i != m_clients.end(); i++)
3515 // Get client and check that it is valid
3516 RemoteClient *client = i->second;
3517 assert(client->peer_id == i->first);
3518 if(client->serialization_version == SER_FMT_VER_INVALID)
3521 SendSpawnParticle(client->peer_id, pos, velocity, acceleration,
3522 expirationtime, size, collisiondetection, texture);
3526 // Adds a ParticleSpawner on peer with peer_id
3527 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
3528 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3529 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3531 DSTACK(__FUNCTION_NAME);
3533 std::ostringstream os(std::ios_base::binary);
3534 writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
3536 writeU16(os, amount);
3537 writeF1000(os, spawntime);
3538 writeV3F1000(os, minpos);
3539 writeV3F1000(os, maxpos);
3540 writeV3F1000(os, minvel);
3541 writeV3F1000(os, maxvel);
3542 writeV3F1000(os, minacc);
3543 writeV3F1000(os, maxacc);
3544 writeF1000(os, minexptime);
3545 writeF1000(os, maxexptime);
3546 writeF1000(os, minsize);
3547 writeF1000(os, maxsize);
3548 writeU8(os, collisiondetection);
3549 os<<serializeLongString(texture);
3553 std::string s = os.str();
3554 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3556 m_con.Send(peer_id, 0, data, true);
3559 // Adds a ParticleSpawner on all peers
3560 void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos,
3561 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
3562 float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id)
3564 for(std::map<u16, RemoteClient*>::iterator
3565 i = m_clients.begin();
3566 i != m_clients.end(); i++)
3568 // Get client and check that it is valid
3569 RemoteClient *client = i->second;
3570 assert(client->peer_id == i->first);
3571 if(client->serialization_version == SER_FMT_VER_INVALID)
3574 SendAddParticleSpawner(client->peer_id, amount, spawntime,
3575 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3576 minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id);
3580 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
3582 DSTACK(__FUNCTION_NAME);
3584 std::ostringstream os(std::ios_base::binary);
3585 writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
3590 std::string s = os.str();
3591 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3593 m_con.Send(peer_id, 0, data, true);
3596 void Server::SendDeleteParticleSpawnerAll(u32 id)
3598 for(std::map<u16, RemoteClient*>::iterator
3599 i = m_clients.begin();
3600 i != m_clients.end(); i++)
3602 // Get client and check that it is valid
3603 RemoteClient *client = i->second;
3604 assert(client->peer_id == i->first);
3605 if(client->serialization_version == SER_FMT_VER_INVALID)
3608 SendDeleteParticleSpawner(client->peer_id, id);
3612 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
3614 std::ostringstream os(std::ios_base::binary);
3617 writeU16(os, TOCLIENT_HUDADD);
3619 writeU8(os, (u8)form->type);
3620 writeV2F1000(os, form->pos);
3621 os << serializeString(form->name);
3622 writeV2F1000(os, form->scale);
3623 os << serializeString(form->text);
3624 writeU32(os, form->number);
3625 writeU32(os, form->item);
3626 writeU32(os, form->dir);
3627 writeV2F1000(os, form->align);
3628 writeV2F1000(os, form->offset);
3631 std::string s = os.str();
3632 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3634 m_con.Send(peer_id, 0, data, true);
3637 void Server::SendHUDRemove(u16 peer_id, u32 id)
3639 std::ostringstream os(std::ios_base::binary);
3642 writeU16(os, TOCLIENT_HUDRM);
3646 std::string s = os.str();
3647 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3649 m_con.Send(peer_id, 0, data, true);
3652 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
3654 std::ostringstream os(std::ios_base::binary);
3657 writeU16(os, TOCLIENT_HUDCHANGE);
3659 writeU8(os, (u8)stat);
3662 case HUD_STAT_SCALE:
3663 case HUD_STAT_ALIGN:
3664 case HUD_STAT_OFFSET:
3665 writeV2F1000(os, *(v2f *)value);
3669 os << serializeString(*(std::string *)value);
3671 case HUD_STAT_NUMBER:
3675 writeU32(os, *(u32 *)value);
3680 std::string s = os.str();
3681 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3683 m_con.Send(peer_id, 0, data, true);
3686 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
3688 std::ostringstream os(std::ios_base::binary);
3691 writeU16(os, TOCLIENT_HUD_SET_FLAGS);
3692 writeU32(os, flags);
3696 std::string s = os.str();
3697 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3699 m_con.Send(peer_id, 0, data, true);
3702 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
3704 std::ostringstream os(std::ios_base::binary);
3707 writeU16(os, TOCLIENT_HUD_SET_PARAM);
3708 writeU16(os, param);
3709 os<<serializeString(value);
3712 std::string s = os.str();
3713 SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
3715 m_con.Send(peer_id, 0, data, true);
3718 void Server::BroadcastChatMessage(const std::wstring &message)
3720 for(std::map<u16, RemoteClient*>::iterator
3721 i = m_clients.begin();
3722 i != m_clients.end(); ++i)
3724 // Get client and check that it is valid
3725 RemoteClient *client = i->second;
3726 assert(client->peer_id == i->first);
3727 if(client->serialization_version == SER_FMT_VER_INVALID)
3730 SendChatMessage(client->peer_id, message);
3734 void Server::SendPlayerHP(u16 peer_id)
3736 DSTACK(__FUNCTION_NAME);
3737 PlayerSAO *playersao = getPlayerSAO(peer_id);
3739 playersao->m_hp_not_sent = false;
3740 SendHP(m_con, peer_id, playersao->getHP());
3743 void Server::SendMovePlayer(u16 peer_id)
3745 DSTACK(__FUNCTION_NAME);
3746 Player *player = m_env->getPlayer(peer_id);
3749 std::ostringstream os(std::ios_base::binary);
3750 writeU16(os, TOCLIENT_MOVE_PLAYER);
3751 writeV3F1000(os, player->getPosition());
3752 writeF1000(os, player->getPitch());
3753 writeF1000(os, player->getYaw());
3756 v3f pos = player->getPosition();
3757 f32 pitch = player->getPitch();
3758 f32 yaw = player->getYaw();
3759 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3760 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3767 std::string s = os.str();
3768 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3770 m_con.Send(peer_id, 0, data, true);
3773 void Server::SendPlayerPrivileges(u16 peer_id)
3775 Player *player = m_env->getPlayer(peer_id);
3777 if(player->peer_id == PEER_ID_INEXISTENT)
3780 std::set<std::string> privs;
3781 m_script->getAuth(player->getName(), NULL, &privs);
3783 std::ostringstream os(std::ios_base::binary);
3784 writeU16(os, TOCLIENT_PRIVILEGES);
3785 writeU16(os, privs.size());
3786 for(std::set<std::string>::const_iterator i = privs.begin();
3787 i != privs.end(); i++){
3788 os<<serializeString(*i);
3792 std::string s = os.str();
3793 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3795 m_con.Send(peer_id, 0, data, true);
3798 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3800 Player *player = m_env->getPlayer(peer_id);
3802 if(player->peer_id == PEER_ID_INEXISTENT)
3805 std::ostringstream os(std::ios_base::binary);
3806 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3807 os<<serializeLongString(player->inventory_formspec);
3810 std::string s = os.str();
3811 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3813 m_con.Send(peer_id, 0, data, true);
3816 s32 Server::playSound(const SimpleSoundSpec &spec,
3817 const ServerSoundParams ¶ms)
3819 // Find out initial position of sound
3820 bool pos_exists = false;
3821 v3f pos = params.getPos(m_env, &pos_exists);
3822 // If position is not found while it should be, cancel sound
3823 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3825 // Filter destination clients
3826 std::set<RemoteClient*> dst_clients;
3827 if(params.to_player != "")
3829 Player *player = m_env->getPlayer(params.to_player.c_str());
3831 infostream<<"Server::playSound: Player \""<<params.to_player
3832 <<"\" not found"<<std::endl;
3835 if(player->peer_id == PEER_ID_INEXISTENT){
3836 infostream<<"Server::playSound: Player \""<<params.to_player
3837 <<"\" not connected"<<std::endl;
3840 RemoteClient *client = getClient(player->peer_id);
3841 dst_clients.insert(client);
3845 for(std::map<u16, RemoteClient*>::iterator
3846 i = m_clients.begin(); i != m_clients.end(); ++i)
3848 RemoteClient *client = i->second;
3849 Player *player = m_env->getPlayer(client->peer_id);
3853 if(player->getPosition().getDistanceFrom(pos) >
3854 params.max_hear_distance)
3857 dst_clients.insert(client);
3860 if(dst_clients.size() == 0)
3863 s32 id = m_next_sound_id++;
3864 // The sound will exist as a reference in m_playing_sounds
3865 m_playing_sounds[id] = ServerPlayingSound();
3866 ServerPlayingSound &psound = m_playing_sounds[id];
3867 psound.params = params;
3868 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3869 i != dst_clients.end(); i++)
3870 psound.clients.insert((*i)->peer_id);
3872 std::ostringstream os(std::ios_base::binary);
3873 writeU16(os, TOCLIENT_PLAY_SOUND);
3875 os<<serializeString(spec.name);
3876 writeF1000(os, spec.gain * params.gain);
3877 writeU8(os, params.type);
3878 writeV3F1000(os, pos);
3879 writeU16(os, params.object);
3880 writeU8(os, params.loop);
3882 std::string s = os.str();
3883 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3885 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3886 i != dst_clients.end(); i++){
3888 m_con.Send((*i)->peer_id, 0, data, true);
3892 void Server::stopSound(s32 handle)
3894 // Get sound reference
3895 std::map<s32, ServerPlayingSound>::iterator i =
3896 m_playing_sounds.find(handle);
3897 if(i == m_playing_sounds.end())
3899 ServerPlayingSound &psound = i->second;
3901 std::ostringstream os(std::ios_base::binary);
3902 writeU16(os, TOCLIENT_STOP_SOUND);
3903 writeS32(os, handle);
3905 std::string s = os.str();
3906 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3908 for(std::set<u16>::iterator i = psound.clients.begin();
3909 i != psound.clients.end(); i++){
3911 m_con.Send(*i, 0, data, true);
3913 // Remove sound reference
3914 m_playing_sounds.erase(i);
3917 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3918 std::list<u16> *far_players, float far_d_nodes)
3920 float maxd = far_d_nodes*BS;
3921 v3f p_f = intToFloat(p, BS);
3925 SharedBuffer<u8> reply(replysize);
3926 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3927 writeS16(&reply[2], p.X);
3928 writeS16(&reply[4], p.Y);
3929 writeS16(&reply[6], p.Z);
3931 for(std::map<u16, RemoteClient*>::iterator
3932 i = m_clients.begin();
3933 i != m_clients.end(); ++i)
3935 // Get client and check that it is valid
3936 RemoteClient *client = i->second;
3937 assert(client->peer_id == i->first);
3938 if(client->serialization_version == SER_FMT_VER_INVALID)
3941 // Don't send if it's the same one
3942 if(client->peer_id == ignore_id)
3948 Player *player = m_env->getPlayer(client->peer_id);
3951 // If player is far away, only set modified blocks not sent
3952 v3f player_pos = player->getPosition();
3953 if(player_pos.getDistanceFrom(p_f) > maxd)
3955 far_players->push_back(client->peer_id);
3962 m_con.Send(client->peer_id, 0, reply, true);
3966 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3967 std::list<u16> *far_players, float far_d_nodes)
3969 float maxd = far_d_nodes*BS;
3970 v3f p_f = intToFloat(p, BS);
3972 for(std::map<u16, RemoteClient*>::iterator
3973 i = m_clients.begin();
3974 i != m_clients.end(); ++i)
3976 // Get client and check that it is valid
3977 RemoteClient *client = i->second;
3978 assert(client->peer_id == i->first);
3979 if(client->serialization_version == SER_FMT_VER_INVALID)
3982 // Don't send if it's the same one
3983 if(client->peer_id == ignore_id)
3989 Player *player = m_env->getPlayer(client->peer_id);
3992 // If player is far away, only set modified blocks not sent
3993 v3f player_pos = player->getPosition();
3994 if(player_pos.getDistanceFrom(p_f) > maxd)
3996 far_players->push_back(client->peer_id);
4003 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4004 SharedBuffer<u8> reply(replysize);
4005 writeU16(&reply[0], TOCLIENT_ADDNODE);
4006 writeS16(&reply[2], p.X);
4007 writeS16(&reply[4], p.Y);
4008 writeS16(&reply[6], p.Z);
4009 n.serialize(&reply[8], client->serialization_version);
4012 m_con.Send(client->peer_id, 0, reply, true);
4016 void Server::setBlockNotSent(v3s16 p)
4018 for(std::map<u16, RemoteClient*>::iterator
4019 i = m_clients.begin();
4020 i != m_clients.end(); ++i)
4022 RemoteClient *client = i->second;
4023 client->SetBlockNotSent(p);
4027 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4029 DSTACK(__FUNCTION_NAME);
4031 v3s16 p = block->getPos();
4035 bool completely_air = true;
4036 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4037 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4038 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4040 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4042 completely_air = false;
4043 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4048 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4050 infostream<<"[completely air] ";
4051 infostream<<std::endl;
4055 Create a packet with the block in the right format
4058 std::ostringstream os(std::ios_base::binary);
4059 block->serialize(os, ver, false);
4060 std::string s = os.str();
4061 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4063 u32 replysize = 8 + blockdata.getSize();
4064 SharedBuffer<u8> reply(replysize);
4065 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4066 writeS16(&reply[2], p.X);
4067 writeS16(&reply[4], p.Y);
4068 writeS16(&reply[6], p.Z);
4069 memcpy(&reply[8], *blockdata, blockdata.getSize());
4071 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4072 <<": \tpacket size: "<<replysize<<std::endl;*/
4077 m_con.Send(peer_id, 1, reply, true);
4080 void Server::SendBlocks(float dtime)
4082 DSTACK(__FUNCTION_NAME);
4084 JMutexAutoLock envlock(m_env_mutex);
4085 JMutexAutoLock conlock(m_con_mutex);
4087 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
4089 std::vector<PrioritySortedBlockTransfer> queue;
4091 s32 total_sending = 0;
4094 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4096 for(std::map<u16, RemoteClient*>::iterator
4097 i = m_clients.begin();
4098 i != m_clients.end(); ++i)
4100 RemoteClient *client = i->second;
4101 assert(client->peer_id == i->first);
4103 // If definitions and textures have not been sent, don't
4104 // send MapBlocks either
4105 if(!client->definitions_sent)
4108 total_sending += client->SendingCount();
4110 if(client->serialization_version == SER_FMT_VER_INVALID)
4113 client->GetNextBlocks(this, dtime, queue);
4118 // Lowest priority number comes first.
4119 // Lowest is most important.
4120 std::sort(queue.begin(), queue.end());
4122 for(u32 i=0; i<queue.size(); i++)
4124 //TODO: Calculate limit dynamically
4125 if(total_sending >= g_settings->getS32
4126 ("max_simultaneous_block_sends_server_total"))
4129 PrioritySortedBlockTransfer q = queue[i];
4131 MapBlock *block = NULL;
4134 block = m_env->getMap().getBlockNoCreate(q.pos);
4136 catch(InvalidPositionException &e)
4141 RemoteClient *client = getClient(q.peer_id);
4143 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4145 client->SentBlock(q.pos);
4151 void Server::fillMediaCache()
4153 DSTACK(__FUNCTION_NAME);
4155 infostream<<"Server: Calculating media file checksums"<<std::endl;
4157 // Collect all media file paths
4158 std::list<std::string> paths;
4159 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4160 i != m_mods.end(); i++){
4161 const ModSpec &mod = *i;
4162 paths.push_back(mod.path + DIR_DELIM + "textures");
4163 paths.push_back(mod.path + DIR_DELIM + "sounds");
4164 paths.push_back(mod.path + DIR_DELIM + "media");
4165 paths.push_back(mod.path + DIR_DELIM + "models");
4167 std::string path_all = "textures";
4168 paths.push_back(path_all + DIR_DELIM + "all");
4170 // Collect media file information from paths into cache
4171 for(std::list<std::string>::iterator i = paths.begin();
4172 i != paths.end(); i++)
4174 std::string mediapath = *i;
4175 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4176 for(u32 j=0; j<dirlist.size(); j++){
4177 if(dirlist[j].dir) // Ignode dirs
4179 std::string filename = dirlist[j].name;
4180 // If name contains illegal characters, ignore the file
4181 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4182 infostream<<"Server: ignoring illegal file name: \""
4183 <<filename<<"\""<<std::endl;
4186 // If name is not in a supported format, ignore it
4187 const char *supported_ext[] = {
4188 ".png", ".jpg", ".bmp", ".tga",
4189 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4191 ".x", ".b3d", ".md2", ".obj",
4194 if(removeStringEnd(filename, supported_ext) == ""){
4195 infostream<<"Server: ignoring unsupported file extension: \""
4196 <<filename<<"\""<<std::endl;
4199 // Ok, attempt to load the file and add to cache
4200 std::string filepath = mediapath + DIR_DELIM + filename;
4202 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4203 if(fis.good() == false){
4204 errorstream<<"Server::fillMediaCache(): Could not open \""
4205 <<filename<<"\" for reading"<<std::endl;
4208 std::ostringstream tmp_os(std::ios_base::binary);
4212 fis.read(buf, 1024);
4213 std::streamsize len = fis.gcount();
4214 tmp_os.write(buf, len);
4223 errorstream<<"Server::fillMediaCache(): Failed to read \""
4224 <<filename<<"\""<<std::endl;
4227 if(tmp_os.str().length() == 0){
4228 errorstream<<"Server::fillMediaCache(): Empty file \""
4229 <<filepath<<"\""<<std::endl;
4234 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4236 unsigned char *digest = sha1.getDigest();
4237 std::string sha1_base64 = base64_encode(digest, 20);
4238 std::string sha1_hex = hex_encode((char*)digest, 20);
4242 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4243 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4248 struct SendableMediaAnnouncement
4251 std::string sha1_digest;
4253 SendableMediaAnnouncement(const std::string name_="",
4254 const std::string sha1_digest_=""):
4256 sha1_digest(sha1_digest_)
4260 void Server::sendMediaAnnouncement(u16 peer_id)
4262 DSTACK(__FUNCTION_NAME);
4264 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4267 std::list<SendableMediaAnnouncement> file_announcements;
4269 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4270 i != m_media.end(); i++){
4272 file_announcements.push_back(
4273 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4277 std::ostringstream os(std::ios_base::binary);
4285 u16 length of sha1_digest
4290 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4291 writeU16(os, file_announcements.size());
4293 for(std::list<SendableMediaAnnouncement>::iterator
4294 j = file_announcements.begin();
4295 j != file_announcements.end(); ++j){
4296 os<<serializeString(j->name);
4297 os<<serializeString(j->sha1_digest);
4299 os<<serializeString(g_settings->get("remote_media"));
4302 std::string s = os.str();
4303 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4306 m_con.Send(peer_id, 0, data, true);
4309 struct SendableMedia
4315 SendableMedia(const std::string &name_="", const std::string path_="",
4316 const std::string &data_=""):
4323 void Server::sendRequestedMedia(u16 peer_id,
4324 const std::list<MediaRequest> &tosend)
4326 DSTACK(__FUNCTION_NAME);
4328 verbosestream<<"Server::sendRequestedMedia(): "
4329 <<"Sending files to client"<<std::endl;
4333 // Put 5kB in one bunch (this is not accurate)
4334 u32 bytes_per_bunch = 5000;
4336 std::vector< std::list<SendableMedia> > file_bunches;
4337 file_bunches.push_back(std::list<SendableMedia>());
4339 u32 file_size_bunch_total = 0;
4341 for(std::list<MediaRequest>::const_iterator i = tosend.begin();
4342 i != tosend.end(); ++i)
4344 if(m_media.find(i->name) == m_media.end()){
4345 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4346 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4350 //TODO get path + name
4351 std::string tpath = m_media[(*i).name].path;
4354 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4355 if(fis.good() == false){
4356 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4357 <<tpath<<"\" for reading"<<std::endl;
4360 std::ostringstream tmp_os(std::ios_base::binary);
4364 fis.read(buf, 1024);
4365 std::streamsize len = fis.gcount();
4366 tmp_os.write(buf, len);
4367 file_size_bunch_total += len;
4376 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4377 <<(*i).name<<"\""<<std::endl;
4380 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4381 <<tname<<"\""<<std::endl;*/
4383 file_bunches[file_bunches.size()-1].push_back(
4384 SendableMedia((*i).name, tpath, tmp_os.str()));
4386 // Start next bunch if got enough data
4387 if(file_size_bunch_total >= bytes_per_bunch){
4388 file_bunches.push_back(std::list<SendableMedia>());
4389 file_size_bunch_total = 0;
4394 /* Create and send packets */
4396 u32 num_bunches = file_bunches.size();
4397 for(u32 i=0; i<num_bunches; i++)
4399 std::ostringstream os(std::ios_base::binary);
4403 u16 total number of texture bunches
4404 u16 index of this bunch
4405 u32 number of files in this bunch
4414 writeU16(os, TOCLIENT_MEDIA);
4415 writeU16(os, num_bunches);
4417 writeU32(os, file_bunches[i].size());
4419 for(std::list<SendableMedia>::iterator
4420 j = file_bunches[i].begin();
4421 j != file_bunches[i].end(); ++j){
4422 os<<serializeString(j->name);
4423 os<<serializeLongString(j->data);
4427 std::string s = os.str();
4428 verbosestream<<"Server::sendRequestedMedia(): bunch "
4429 <<i<<"/"<<num_bunches
4430 <<" files="<<file_bunches[i].size()
4431 <<" size=" <<s.size()<<std::endl;
4432 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4434 m_con.Send(peer_id, 0, data, true);
4438 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4440 if(m_detached_inventories.count(name) == 0){
4441 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4444 Inventory *inv = m_detached_inventories[name];
4446 std::ostringstream os(std::ios_base::binary);
4447 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4448 os<<serializeString(name);
4452 std::string s = os.str();
4453 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4455 m_con.Send(peer_id, 0, data, true);
4458 void Server::sendDetachedInventoryToAll(const std::string &name)
4460 DSTACK(__FUNCTION_NAME);
4462 for(std::map<u16, RemoteClient*>::iterator
4463 i = m_clients.begin();
4464 i != m_clients.end(); ++i){
4465 RemoteClient *client = i->second;
4466 sendDetachedInventory(name, client->peer_id);
4470 void Server::sendDetachedInventories(u16 peer_id)
4472 DSTACK(__FUNCTION_NAME);
4474 for(std::map<std::string, Inventory*>::iterator
4475 i = m_detached_inventories.begin();
4476 i != m_detached_inventories.end(); i++){
4477 const std::string &name = i->first;
4478 //Inventory *inv = i->second;
4479 sendDetachedInventory(name, peer_id);
4487 void Server::DiePlayer(u16 peer_id)
4489 DSTACK(__FUNCTION_NAME);
4491 PlayerSAO *playersao = getPlayerSAO(peer_id);
4494 infostream<<"Server::DiePlayer(): Player "
4495 <<playersao->getPlayer()->getName()
4496 <<" dies"<<std::endl;
4498 playersao->setHP(0);
4500 // Trigger scripted stuff
4501 m_script->on_dieplayer(playersao);
4503 SendPlayerHP(peer_id);
4504 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4507 void Server::RespawnPlayer(u16 peer_id)
4509 DSTACK(__FUNCTION_NAME);
4511 PlayerSAO *playersao = getPlayerSAO(peer_id);
4514 infostream<<"Server::RespawnPlayer(): Player "
4515 <<playersao->getPlayer()->getName()
4516 <<" respawns"<<std::endl;
4518 playersao->setHP(PLAYER_MAX_HP);
4520 bool repositioned = m_script->on_respawnplayer(playersao);
4522 v3f pos = findSpawnPos(m_env->getServerMap());
4523 playersao->setPos(pos);
4527 void Server::UpdateCrafting(u16 peer_id)
4529 DSTACK(__FUNCTION_NAME);
4531 Player* player = m_env->getPlayer(peer_id);
4534 // Get a preview for crafting
4536 getCraftingResult(&player->inventory, preview, false, this);
4538 // Put the new preview in
4539 InventoryList *plist = player->inventory.getList("craftpreview");
4541 assert(plist->getSize() >= 1);
4542 plist->changeItem(0, preview);
4545 RemoteClient* Server::getClient(u16 peer_id)
4547 DSTACK(__FUNCTION_NAME);
4548 //JMutexAutoLock lock(m_con_mutex);
4549 std::map<u16, RemoteClient*>::iterator n;
4550 n = m_clients.find(peer_id);
4551 // A client should exist for all peers
4552 assert(n != m_clients.end());
4556 std::wstring Server::getStatusString()
4558 std::wostringstream os(std::ios_base::binary);
4561 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4563 os<<L", uptime="<<m_uptime.get();
4564 // Information about clients
4565 std::map<u16, RemoteClient*>::iterator i;
4568 for(i = m_clients.begin(), first = true;
4569 i != m_clients.end(); ++i)
4571 // Get client and check that it is valid
4572 RemoteClient *client = i->second;
4573 assert(client->peer_id == i->first);
4574 if(client->serialization_version == SER_FMT_VER_INVALID)
4577 Player *player = m_env->getPlayer(client->peer_id);
4578 // Get name of player
4579 std::wstring name = L"unknown";
4581 name = narrow_to_wide(player->getName());
4582 // Add name to information string
4590 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4591 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4592 if(g_settings->get("motd") != "")
4593 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4597 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4599 std::set<std::string> privs;
4600 m_script->getAuth(name, NULL, &privs);
4604 bool Server::checkPriv(const std::string &name, const std::string &priv)
4606 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4607 return (privs.count(priv) != 0);
4610 void Server::reportPrivsModified(const std::string &name)
4613 for(std::map<u16, RemoteClient*>::iterator
4614 i = m_clients.begin();
4615 i != m_clients.end(); ++i){
4616 RemoteClient *client = i->second;
4617 Player *player = m_env->getPlayer(client->peer_id);
4618 reportPrivsModified(player->getName());
4621 Player *player = m_env->getPlayer(name.c_str());
4624 SendPlayerPrivileges(player->peer_id);
4625 PlayerSAO *sao = player->getPlayerSAO();
4628 sao->updatePrivileges(
4629 getPlayerEffectivePrivs(name),
4634 void Server::reportInventoryFormspecModified(const std::string &name)
4636 Player *player = m_env->getPlayer(name.c_str());
4639 SendPlayerInventoryFormspec(player->peer_id);
4642 // Saves g_settings to configpath given at initialization
4643 void Server::saveConfig()
4645 if(m_path_config != "")
4646 g_settings->updateConfigFile(m_path_config.c_str());
4649 void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true)
4651 Player *player = m_env->getPlayer(name);
4655 SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg);
4657 SendChatMessage(player->peer_id, msg);
4660 bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
4662 Player *player = m_env->getPlayer(playername);
4666 infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
4670 SendShowFormspecMessage(player->peer_id, formspec, formname);
4674 u32 Server::hudAdd(Player *player, HudElement *form) {
4678 u32 id = hud_get_free_id(player);
4679 if (id < player->hud.size())
4680 player->hud[id] = form;
4682 player->hud.push_back(form);
4684 SendHUDAdd(player->peer_id, id, form);
4688 bool Server::hudRemove(Player *player, u32 id) {
4689 if (!player || id >= player->hud.size() || !player->hud[id])
4692 delete player->hud[id];
4693 player->hud[id] = NULL;
4695 SendHUDRemove(player->peer_id, id);
4699 bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
4703 SendHUDChange(player->peer_id, id, stat, data);
4707 bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
4711 SendHUDSetFlags(player->peer_id, flags, mask);
4715 bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
4718 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
4721 std::ostringstream os(std::ios::binary);
4722 writeS32(os, hotbar_itemcount);
4723 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
4727 void Server::notifyPlayers(const std::wstring msg)
4729 BroadcastChatMessage(msg);
4732 void Server::spawnParticle(const char *playername, v3f pos,
4733 v3f velocity, v3f acceleration,
4734 float expirationtime, float size, bool
4735 collisiondetection, std::string texture)
4737 Player *player = m_env->getPlayer(playername);
4740 SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
4741 expirationtime, size, collisiondetection, texture);
4744 void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
4745 float expirationtime, float size,
4746 bool collisiondetection, std::string texture)
4748 SendSpawnParticleAll(pos, velocity, acceleration,
4749 expirationtime, size, collisiondetection, texture);
4752 u32 Server::addParticleSpawner(const char *playername,
4753 u16 amount, float spawntime,
4754 v3f minpos, v3f maxpos,
4755 v3f minvel, v3f maxvel,
4756 v3f minacc, v3f maxacc,
4757 float minexptime, float maxexptime,
4758 float minsize, float maxsize,
4759 bool collisiondetection, std::string texture)
4761 Player *player = m_env->getPlayer(playername);
4766 for(;;) // look for unused particlespawner id
4769 if (std::find(m_particlespawner_ids.begin(),
4770 m_particlespawner_ids.end(), id)
4771 == m_particlespawner_ids.end())
4773 m_particlespawner_ids.push_back(id);
4778 SendAddParticleSpawner(player->peer_id, amount, spawntime,
4779 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4780 minexptime, maxexptime, minsize, maxsize,
4781 collisiondetection, texture, id);
4786 u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
4787 v3f minpos, v3f maxpos,
4788 v3f minvel, v3f maxvel,
4789 v3f minacc, v3f maxacc,
4790 float minexptime, float maxexptime,
4791 float minsize, float maxsize,
4792 bool collisiondetection, std::string texture)
4795 for(;;) // look for unused particlespawner id
4798 if (std::find(m_particlespawner_ids.begin(),
4799 m_particlespawner_ids.end(), id)
4800 == m_particlespawner_ids.end())
4802 m_particlespawner_ids.push_back(id);
4807 SendAddParticleSpawnerAll(amount, spawntime,
4808 minpos, maxpos, minvel, maxvel, minacc, maxacc,
4809 minexptime, maxexptime, minsize, maxsize,
4810 collisiondetection, texture, id);
4815 void Server::deleteParticleSpawner(const char *playername, u32 id)
4817 Player *player = m_env->getPlayer(playername);
4821 m_particlespawner_ids.erase(
4822 std::remove(m_particlespawner_ids.begin(),
4823 m_particlespawner_ids.end(), id),
4824 m_particlespawner_ids.end());
4825 SendDeleteParticleSpawner(player->peer_id, id);
4828 void Server::deleteParticleSpawnerAll(u32 id)
4830 m_particlespawner_ids.erase(
4831 std::remove(m_particlespawner_ids.begin(),
4832 m_particlespawner_ids.end(), id),
4833 m_particlespawner_ids.end());
4834 SendDeleteParticleSpawnerAll(id);
4837 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4839 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate);
4842 Inventory* Server::createDetachedInventory(const std::string &name)
4844 if(m_detached_inventories.count(name) > 0){
4845 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4846 delete m_detached_inventories[name];
4848 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4850 Inventory *inv = new Inventory(m_itemdef);
4852 m_detached_inventories[name] = inv;
4853 sendDetachedInventoryToAll(name);
4860 BoolScopeSet(bool *dst, bool val):
4863 m_orig_state = *m_dst;
4868 *m_dst = m_orig_state;
4875 // actions: time-reversed list
4876 // Return value: success/failure
4877 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4878 std::list<std::string> *log)
4880 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4881 ServerMap *map = (ServerMap*)(&m_env->getMap());
4882 // Disable rollback report sink while reverting
4883 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4885 // Fail if no actions to handle
4886 if(actions.empty()){
4887 log->push_back("Nothing to do.");
4894 for(std::list<RollbackAction>::const_iterator
4895 i = actions.begin();
4896 i != actions.end(); i++)
4898 const RollbackAction &action = *i;
4900 bool success = action.applyRevert(map, this, this);
4903 std::ostringstream os;
4904 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4905 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4907 log->push_back(os.str());
4909 std::ostringstream os;
4910 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
4911 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4913 log->push_back(os.str());
4917 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4918 <<" failed"<<std::endl;
4920 // Call it done if less than half failed
4921 return num_failed <= num_tried/2;
4924 // IGameDef interface
4926 IItemDefManager* Server::getItemDefManager()
4930 INodeDefManager* Server::getNodeDefManager()
4934 ICraftDefManager* Server::getCraftDefManager()
4938 ITextureSource* Server::getTextureSource()
4942 IShaderSource* Server::getShaderSource()
4946 u16 Server::allocateUnknownNodeId(const std::string &name)
4948 return m_nodedef->allocateDummy(name);
4950 ISoundManager* Server::getSoundManager()
4952 return &dummySoundManager;
4954 MtEventManager* Server::getEventManager()
4958 IRollbackReportSink* Server::getRollbackReportSink()
4960 if(!m_enable_rollback_recording)
4962 if(!m_rollback_sink_enabled)
4967 IWritableItemDefManager* Server::getWritableItemDefManager()
4971 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4975 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4980 const ModSpec* Server::getModSpec(const std::string &modname)
4982 for(std::vector<ModSpec>::iterator i = m_mods.begin();
4983 i != m_mods.end(); i++){
4984 const ModSpec &mod = *i;
4985 if(mod.name == modname)
4990 void Server::getModNames(std::list<std::string> &modlist)
4992 for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
4994 modlist.push_back(i->name);
4997 std::string Server::getBuiltinLuaPath()
4999 return porting::path_share + DIR_DELIM + "builtin";
5002 v3f findSpawnPos(ServerMap &map)
5004 //return v3f(50,50,50)*BS;
5009 nodepos = v2s16(0,0);
5014 s16 water_level = map.m_mgparams->water_level;
5016 // Try to find a good place a few times
5017 for(s32 i=0; i<1000; i++)
5020 // We're going to try to throw the player to this position
5021 v2s16 nodepos2d = v2s16(
5022 -range + (myrand() % (range * 2)),
5023 -range + (myrand() % (range * 2)));
5025 // Get ground height at point
5026 s16 groundheight = map.findGroundLevel(nodepos2d);
5027 if (groundheight <= water_level) // Don't go underwater
5029 if (groundheight > water_level + 6) // Don't go to high places
5032 nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
5033 bool is_good = false;
5035 for (s32 i = 0; i < 10; i++) {
5036 v3s16 blockpos = getNodeBlockPos(nodepos);
5037 map.emergeBlock(blockpos, true);
5038 content_t c = map.getNodeNoEx(nodepos).getContent();
5039 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
5041 if (air_count >= 2){
5049 // Found a good place
5050 //infostream<<"Searched through "<<i<<" places."<<std::endl;
5056 return intToFloat(nodepos, BS);
5059 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
5061 RemotePlayer *player = NULL;
5062 bool newplayer = false;
5065 Try to get an existing player
5067 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
5069 // If player is already connected, cancel
5070 if(player != NULL && player->peer_id != 0)
5072 infostream<<"emergePlayer(): Player already connected"<<std::endl;
5077 If player with the wanted peer_id already exists, cancel.
5079 if(m_env->getPlayer(peer_id) != NULL)
5081 infostream<<"emergePlayer(): Player with wrong name but same"
5082 " peer_id already exists"<<std::endl;
5087 Create a new player if it doesn't exist yet
5092 player = new RemotePlayer(this);
5093 player->updateName(name);
5095 /* Set player position */
5096 infostream<<"Server: Finding spawn place for player \""
5097 <<name<<"\""<<std::endl;
5098 v3f pos = findSpawnPos(m_env->getServerMap());
5099 player->setPosition(pos);
5101 /* Add player to environment */
5102 m_env->addPlayer(player);
5106 Create a new player active object
5108 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
5109 getPlayerEffectivePrivs(player->getName()),
5112 /* Clean up old HUD elements from previous sessions */
5113 player->hud.clear();
5115 /* Add object to environment */
5116 m_env->addActiveObject(playersao);
5120 m_script->on_newplayer(playersao);
5122 m_script->on_joinplayer(playersao);
5127 void Server::handlePeerChange(PeerChange &c)
5129 JMutexAutoLock envlock(m_env_mutex);
5130 JMutexAutoLock conlock(m_con_mutex);
5132 if(c.type == PEER_ADDED)
5139 std::map<u16, RemoteClient*>::iterator n;
5140 n = m_clients.find(c.peer_id);
5141 // The client shouldn't already exist
5142 assert(n == m_clients.end());
5145 RemoteClient *client = new RemoteClient();
5146 client->peer_id = c.peer_id;
5147 m_clients[client->peer_id] = client;
5150 else if(c.type == PEER_REMOVED)
5157 std::map<u16, RemoteClient*>::iterator n;
5158 n = m_clients.find(c.peer_id);
5159 // The client should exist
5160 assert(n != m_clients.end());
5163 Mark objects to be not known by the client
5165 RemoteClient *client = n->second;
5167 for(std::set<u16>::iterator
5168 i = client->m_known_objects.begin();
5169 i != client->m_known_objects.end(); ++i)
5173 ServerActiveObject* obj = m_env->getActiveObject(id);
5175 if(obj && obj->m_known_by_count > 0)
5176 obj->m_known_by_count--;
5180 Clear references to playing sounds
5182 for(std::map<s32, ServerPlayingSound>::iterator
5183 i = m_playing_sounds.begin();
5184 i != m_playing_sounds.end();)
5186 ServerPlayingSound &psound = i->second;
5187 psound.clients.erase(c.peer_id);
5188 if(psound.clients.size() == 0)
5189 m_playing_sounds.erase(i++);
5194 Player *player = m_env->getPlayer(c.peer_id);
5196 // Collect information about leaving in chat
5197 std::wstring message;
5201 std::wstring name = narrow_to_wide(player->getName());
5204 message += L" left the game.";
5206 message += L" (timed out)";
5210 /* Run scripts and remove from environment */
5214 PlayerSAO *playersao = player->getPlayerSAO();
5217 m_script->on_leaveplayer(playersao);
5219 playersao->disconnected();
5229 std::ostringstream os(std::ios_base::binary);
5230 for(std::map<u16, RemoteClient*>::iterator
5231 i = m_clients.begin();
5232 i != m_clients.end(); ++i)
5234 RemoteClient *client = i->second;
5235 assert(client->peer_id == i->first);
5236 if(client->serialization_version == SER_FMT_VER_INVALID)
5239 Player *player = m_env->getPlayer(client->peer_id);
5242 // Get name of player
5243 os<<player->getName()<<" ";
5246 actionstream<<player->getName()<<" "
5247 <<(c.timeout?"times out.":"leaves game.")
5248 <<" List of players: "
5249 <<os.str()<<std::endl;
5254 delete m_clients[c.peer_id];
5255 m_clients.erase(c.peer_id);
5257 // Send player info to all remaining clients
5258 //SendPlayerInfos();
5260 // Send leave chat message to all remaining clients
5261 if(message.length() != 0)
5262 BroadcastChatMessage(message);
5271 void Server::handlePeerChanges()
5273 while(m_peer_change_queue.size() > 0)
5275 PeerChange c = m_peer_change_queue.pop_front();
5277 verbosestream<<"Server: Handling peer change: "
5278 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5281 handlePeerChange(c);
5285 void dedicated_server_loop(Server &server, bool &kill)
5287 DSTACK(__FUNCTION_NAME);
5289 verbosestream<<"dedicated_server_loop()"<<std::endl;
5291 IntervalLimiter m_profiler_interval;
5295 float steplen = g_settings->getFloat("dedicated_server_step");
5296 // This is kind of a hack but can be done like this
5297 // because server.step() is very light
5299 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5300 sleep_ms((int)(steplen*1000.0));
5302 server.step(steplen);
5304 if(server.getShutdownRequested() || kill)
5306 infostream<<"Dedicated server quitting"<<std::endl;
5308 if(g_settings->getBool("server_announce") == true)
5309 ServerList::sendAnnounce("delete");
5317 float profiler_print_interval =
5318 g_settings->getFloat("profiler_print_interval");
5319 if(profiler_print_interval != 0)
5321 if(m_profiler_interval.step(steplen, profiler_print_interval))
5323 infostream<<"Profiler:"<<std::endl;
5324 g_profiler->print(infostream);
5325 g_profiler->clear();