]> git.lizzy.rs Git - minetest.git/blob - src/serverenvironment.cpp
f3711652ca728067212beef00937da097ff052f2
[minetest.git] / src / serverenvironment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include <algorithm>
21 #include "serverenvironment.h"
22 #include "settings.h"
23 #include "log.h"
24 #include "mapblock.h"
25 #include "nodedef.h"
26 #include "nodemetadata.h"
27 #include "gamedef.h"
28 #include "map.h"
29 #include "porting.h"
30 #include "profiler.h"
31 #include "raycast.h"
32 #include "remoteplayer.h"
33 #include "scripting_server.h"
34 #include "server.h"
35 #include "util/serialize.h"
36 #include "util/basic_macros.h"
37 #include "util/pointedthing.h"
38 #include "threading/mutex_auto_lock.h"
39 #include "filesys.h"
40 #include "gameparams.h"
41 #include "database/database-dummy.h"
42 #include "database/database-files.h"
43 #include "database/database-sqlite3.h"
44 #if USE_POSTGRESQL
45 #include "database/database-postgresql.h"
46 #endif
47 #if USE_LEVELDB
48 #include "database/database-leveldb.h"
49 #endif
50 #include "server/luaentity_sao.h"
51 #include "server/player_sao.h"
52
53 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
54
55 // A number that is much smaller than the timeout for particle spawners should/could ever be
56 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57
58 /*
59         ABMWithState
60 */
61
62 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
63         abm(abm_)
64 {
65         // Initialize timer to random value to spread processing
66         float itv = abm->getTriggerInterval();
67         itv = MYMAX(0.001, itv); // No less than 1ms
68         int minval = MYMAX(-0.51*itv, -60); // Clamp to
69         int maxval = MYMIN(0.51*itv, 60);   // +-60 seconds
70         timer = myrand_range(minval, maxval);
71 }
72
73 /*
74         LBMManager
75 */
76
77 void LBMContentMapping::deleteContents()
78 {
79         for (auto &it : lbm_list) {
80                 delete it;
81         }
82 }
83
84 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
85 {
86         // Add the lbm_def to the LBMContentMapping.
87         // Unknown names get added to the global NameIdMapping.
88         const NodeDefManager *nodedef = gamedef->ndef();
89
90         lbm_list.push_back(lbm_def);
91
92         for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
93                 std::vector<content_t> c_ids;
94                 bool found = nodedef->getIds(nodeTrigger, c_ids);
95                 if (!found) {
96                         content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
97                         if (c_id == CONTENT_IGNORE) {
98                                 // Seems it can't be allocated.
99                                 warningstream << "Could not internalize node name \"" << nodeTrigger
100                                         << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
101                                 continue;
102                         }
103                         c_ids.push_back(c_id);
104                 }
105
106                 for (content_t c_id : c_ids) {
107                         map[c_id].push_back(lbm_def);
108                 }
109         }
110 }
111
112 const std::vector<LoadingBlockModifierDef *> *
113 LBMContentMapping::lookup(content_t c) const
114 {
115         lbm_map::const_iterator it = map.find(c);
116         if (it == map.end())
117                 return NULL;
118         // This first dereferences the iterator, returning
119         // a std::vector<LoadingBlockModifierDef *>
120         // reference, then we convert it to a pointer.
121         return &(it->second);
122 }
123
124 LBMManager::~LBMManager()
125 {
126         for (auto &m_lbm_def : m_lbm_defs) {
127                 delete m_lbm_def.second;
128         }
129
130         for (auto &it : m_lbm_lookup) {
131                 (it.second).deleteContents();
132         }
133 }
134
135 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
136 {
137         // Precondition, in query mode the map isn't used anymore
138         FATAL_ERROR_IF(m_query_mode,
139                 "attempted to modify LBMManager in query mode");
140
141         if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
142                 throw ModError("Error adding LBM \"" + lbm_def->name +
143                         "\": Does not follow naming conventions: "
144                                 "Only characters [a-z0-9_:] are allowed.");
145         }
146
147         m_lbm_defs[lbm_def->name] = lbm_def;
148 }
149
150 void LBMManager::loadIntroductionTimes(const std::string &times,
151         IGameDef *gamedef, u32 now)
152 {
153         m_query_mode = true;
154
155         // name -> time map.
156         // Storing it in a map first instead of
157         // handling the stuff directly in the loop
158         // removes all duplicate entries.
159         // TODO make this std::unordered_map
160         std::map<std::string, u32> introduction_times;
161
162         /*
163         The introduction times string consists of name~time entries,
164         with each entry terminated by a semicolon. The time is decimal.
165          */
166
167         size_t idx = 0;
168         size_t idx_new;
169         while ((idx_new = times.find(';', idx)) != std::string::npos) {
170                 std::string entry = times.substr(idx, idx_new - idx);
171                 std::vector<std::string> components = str_split(entry, '~');
172                 if (components.size() != 2)
173                         throw SerializationError("Introduction times entry \""
174                                 + entry + "\" requires exactly one '~'!");
175                 const std::string &name = components[0];
176                 u32 time = from_string<u32>(components[1]);
177                 introduction_times[name] = time;
178                 idx = idx_new + 1;
179         }
180
181         // Put stuff from introduction_times into m_lbm_lookup
182         for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
183                 it != introduction_times.end(); ++it) {
184                 const std::string &name = it->first;
185                 u32 time = it->second;
186
187                 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
188                         m_lbm_defs.find(name);
189                 if (def_it == m_lbm_defs.end()) {
190                         // This seems to be an LBM entry for
191                         // an LBM we haven't loaded. Discard it.
192                         continue;
193                 }
194                 LoadingBlockModifierDef *lbm_def = def_it->second;
195                 if (lbm_def->run_at_every_load) {
196                         // This seems to be an LBM entry for
197                         // an LBM that runs at every load.
198                         // Don't add it just yet.
199                         continue;
200                 }
201
202                 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
203
204                 // Erase the entry so that we know later
205                 // what elements didn't get put into m_lbm_lookup
206                 m_lbm_defs.erase(name);
207         }
208
209         // Now also add the elements from m_lbm_defs to m_lbm_lookup
210         // that weren't added in the previous step.
211         // They are introduced first time to this world,
212         // or are run at every load (introducement time hardcoded to U32_MAX).
213
214         LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
215         LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
216
217         for (auto &m_lbm_def : m_lbm_defs) {
218                 if (m_lbm_def.second->run_at_every_load) {
219                         lbms_running_always.addLBM(m_lbm_def.second, gamedef);
220                 } else {
221                         lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
222                 }
223         }
224
225         // Clear the list, so that we don't delete remaining elements
226         // twice in the destructor
227         m_lbm_defs.clear();
228 }
229
230 std::string LBMManager::createIntroductionTimesString()
231 {
232         // Precondition, we must be in query mode
233         FATAL_ERROR_IF(!m_query_mode,
234                 "attempted to query on non fully set up LBMManager");
235
236         std::ostringstream oss;
237         for (const auto &it : m_lbm_lookup) {
238                 u32 time = it.first;
239                 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
240                 for (const auto &lbm_def : lbm_list) {
241                         // Don't add if the LBM runs at every load,
242                         // then introducement time is hardcoded
243                         // and doesn't need to be stored
244                         if (lbm_def->run_at_every_load)
245                                 continue;
246                         oss << lbm_def->name << "~" << time << ";";
247                 }
248         }
249         return oss.str();
250 }
251
252 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
253 {
254         // Precondition, we need m_lbm_lookup to be initialized
255         FATAL_ERROR_IF(!m_query_mode,
256                 "attempted to query on non fully set up LBMManager");
257         v3s16 pos_of_block = block->getPosRelative();
258         v3s16 pos;
259         MapNode n;
260         content_t c;
261         lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
262         for (; it != m_lbm_lookup.end(); ++it) {
263                 // Cache previous version to speedup lookup which has a very high performance
264                 // penalty on each call
265                 content_t previous_c{};
266                 std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
267
268                 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
269                         for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
270                                 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
271                                         n = block->getNodeNoEx(pos);
272                                         c = n.getContent();
273
274                                         // If content_t are not matching perform an LBM lookup
275                                         if (previous_c != c) {
276                                                 lbm_list = (std::vector<LoadingBlockModifierDef *> *)
277                                                         it->second.lookup(c);
278                                                 previous_c = c;
279                                         }
280
281                                         if (!lbm_list)
282                                                 continue;
283                                         for (auto lbmdef : *lbm_list) {
284                                                 lbmdef->trigger(env, pos + pos_of_block, n);
285                                         }
286                                 }
287         }
288 }
289
290 /*
291         ActiveBlockList
292 */
293
294 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
295 {
296         v3s16 p;
297         for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
298                 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
299                         for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
300                         {
301                                 // limit to a sphere
302                                 if (p.getDistanceFrom(p0) <= r) {
303                                         // Set in list
304                                         list.insert(p);
305                                 }
306                         }
307 }
308
309 void fillViewConeBlock(v3s16 p0,
310         const s16 r,
311         const v3f camera_pos,
312         const v3f camera_dir,
313         const float camera_fov,
314         std::set<v3s16> &list)
315 {
316         v3s16 p;
317         const s16 r_nodes = r * BS * MAP_BLOCKSIZE;
318         for (p.X = p0.X - r; p.X <= p0.X+r; p.X++)
319         for (p.Y = p0.Y - r; p.Y <= p0.Y+r; p.Y++)
320         for (p.Z = p0.Z - r; p.Z <= p0.Z+r; p.Z++) {
321                 if (isBlockInSight(p, camera_pos, camera_dir, camera_fov, r_nodes)) {
322                         list.insert(p);
323                 }
324         }
325 }
326
327 void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
328         s16 active_block_range,
329         s16 active_object_range,
330         std::set<v3s16> &blocks_removed,
331         std::set<v3s16> &blocks_added)
332 {
333         /*
334                 Create the new list
335         */
336         std::set<v3s16> newlist = m_forceloaded_list;
337         m_abm_list = m_forceloaded_list;
338         for (const PlayerSAO *playersao : active_players) {
339                 v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS));
340                 fillRadiusBlock(pos, active_block_range, m_abm_list);
341                 fillRadiusBlock(pos, active_block_range, newlist);
342
343                 s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange());
344                 // only do this if this would add blocks
345                 if (player_ao_range > active_block_range) {
346                         v3f camera_dir = v3f(0,0,1);
347                         camera_dir.rotateYZBy(playersao->getLookPitch());
348                         camera_dir.rotateXZBy(playersao->getRotation().Y);
349                         fillViewConeBlock(pos,
350                                 player_ao_range,
351                                 playersao->getEyePosition(),
352                                 camera_dir,
353                                 playersao->getFov(),
354                                 newlist);
355                 }
356         }
357
358         /*
359                 Find out which blocks on the old list are not on the new list
360         */
361         // Go through old list
362         for (v3s16 p : m_list) {
363                 // If not on new list, it's been removed
364                 if (newlist.find(p) == newlist.end())
365                         blocks_removed.insert(p);
366         }
367
368         /*
369                 Find out which blocks on the new list are not on the old list
370         */
371         // Go through new list
372         for (v3s16 p : newlist) {
373                 // If not on old list, it's been added
374                 if(m_list.find(p) == m_list.end())
375                         blocks_added.insert(p);
376         }
377
378         /*
379                 Update m_list
380         */
381         m_list.clear();
382         for (v3s16 p : newlist) {
383                 m_list.insert(p);
384         }
385 }
386
387 /*
388         ServerEnvironment
389 */
390
391 // Random device to seed pseudo random generators.
392 static std::random_device seed;
393
394 ServerEnvironment::ServerEnvironment(ServerMap *map,
395         ServerScripting *scriptIface, Server *server,
396         const std::string &path_world):
397         Environment(server),
398         m_map(map),
399         m_script(scriptIface),
400         m_server(server),
401         m_path_world(path_world),
402         m_rgen(seed())
403 {
404         // Determine which database backend to use
405         std::string conf_path = path_world + DIR_DELIM + "world.mt";
406         Settings conf;
407
408         std::string player_backend_name = "sqlite3";
409         std::string auth_backend_name = "sqlite3";
410
411         bool succeeded = conf.readConfigFile(conf_path.c_str());
412
413         // If we open world.mt read the backend configurations.
414         if (succeeded) {
415                 // Read those values before setting defaults
416                 bool player_backend_exists = conf.exists("player_backend");
417                 bool auth_backend_exists = conf.exists("auth_backend");
418
419                 // player backend is not set, assume it's legacy file backend.
420                 if (!player_backend_exists) {
421                         // fall back to files
422                         conf.set("player_backend", "files");
423                         player_backend_name = "files";
424
425                         if (!conf.updateConfigFile(conf_path.c_str())) {
426                                 errorstream << "ServerEnvironment::ServerEnvironment(): "
427                                                 << "Failed to update world.mt!" << std::endl;
428                         }
429                 } else {
430                         conf.getNoEx("player_backend", player_backend_name);
431                 }
432
433                 // auth backend is not set, assume it's legacy file backend.
434                 if (!auth_backend_exists) {
435                         conf.set("auth_backend", "files");
436                         auth_backend_name = "files";
437
438                         if (!conf.updateConfigFile(conf_path.c_str())) {
439                                 errorstream << "ServerEnvironment::ServerEnvironment(): "
440                                                 << "Failed to update world.mt!" << std::endl;
441                         }
442                 } else {
443                         conf.getNoEx("auth_backend", auth_backend_name);
444                 }
445         }
446
447         if (player_backend_name == "files") {
448                 warningstream << "/!\\ You are using old player file backend. "
449                                 << "This backend is deprecated and will be removed in a future release /!\\"
450                                 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
451                                 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
452         }
453
454         if (auth_backend_name == "files") {
455                 warningstream << "/!\\ You are using old auth file backend. "
456                                 << "This backend is deprecated and will be removed in a future release /!\\"
457                                 << std::endl << "Switching to SQLite3 is advised, "
458                                 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
459         }
460
461         m_player_database = openPlayerDatabase(player_backend_name, path_world, conf);
462         m_auth_database = openAuthDatabase(auth_backend_name, path_world, conf);
463 }
464
465 ServerEnvironment::~ServerEnvironment()
466 {
467         // Clear active block list.
468         // This makes the next one delete all active objects.
469         m_active_blocks.clear();
470
471         // Convert all objects to static and delete the active objects
472         deactivateFarObjects(true);
473
474         // Drop/delete map
475         m_map->drop();
476
477         // Delete ActiveBlockModifiers
478         for (ABMWithState &m_abm : m_abms) {
479                 delete m_abm.abm;
480         }
481
482         // Deallocate players
483         for (RemotePlayer *m_player : m_players) {
484                 delete m_player;
485         }
486
487         delete m_player_database;
488         delete m_auth_database;
489 }
490
491 Map & ServerEnvironment::getMap()
492 {
493         return *m_map;
494 }
495
496 ServerMap & ServerEnvironment::getServerMap()
497 {
498         return *m_map;
499 }
500
501 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
502 {
503         for (RemotePlayer *player : m_players) {
504                 if (player->getPeerId() == peer_id)
505                         return player;
506         }
507         return NULL;
508 }
509
510 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
511 {
512         for (RemotePlayer *player : m_players) {
513                 if (strcmp(player->getName(), name) == 0)
514                         return player;
515         }
516         return NULL;
517 }
518
519 void ServerEnvironment::addPlayer(RemotePlayer *player)
520 {
521         /*
522                 Check that peer_ids are unique.
523                 Also check that names are unique.
524                 Exception: there can be multiple players with peer_id=0
525         */
526         // If peer id is non-zero, it has to be unique.
527         if (player->getPeerId() != PEER_ID_INEXISTENT)
528                 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
529         // Name has to be unique.
530         FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
531         // Add.
532         m_players.push_back(player);
533 }
534
535 void ServerEnvironment::removePlayer(RemotePlayer *player)
536 {
537         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
538                 it != m_players.end(); ++it) {
539                 if ((*it) == player) {
540                         delete *it;
541                         m_players.erase(it);
542                         return;
543                 }
544         }
545 }
546
547 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
548 {
549         return m_player_database->removePlayer(name);
550 }
551
552 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
553         const std::string &str_reason, bool reconnect)
554 {
555         for (RemotePlayer *player : m_players) {
556                 m_server->DenyAccessVerCompliant(player->getPeerId(),
557                         player->protocol_version, reason, str_reason, reconnect);
558         }
559 }
560
561 void ServerEnvironment::saveLoadedPlayers(bool force)
562 {
563         for (RemotePlayer *player : m_players) {
564                 if (force || player->checkModified() || (player->getPlayerSAO() &&
565                                 player->getPlayerSAO()->getMeta().isModified())) {
566                         try {
567                                 m_player_database->savePlayer(player);
568                         } catch (DatabaseException &e) {
569                                 errorstream << "Failed to save player " << player->getName() << " exception: "
570                                         << e.what() << std::endl;
571                                 throw;
572                         }
573                 }
574         }
575 }
576
577 void ServerEnvironment::savePlayer(RemotePlayer *player)
578 {
579         try {
580                 m_player_database->savePlayer(player);
581         } catch (DatabaseException &e) {
582                 errorstream << "Failed to save player " << player->getName() << " exception: "
583                         << e.what() << std::endl;
584                 throw;
585         }
586 }
587
588 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
589         session_t peer_id, bool is_singleplayer)
590 {
591         PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
592         // Create player if it doesn't exist
593         if (!m_player_database->loadPlayer(player, playersao)) {
594                 *new_player = true;
595                 // Set player position
596                 infostream << "Server: Finding spawn place for player \""
597                         << player->getName() << "\"" << std::endl;
598                 playersao->setBasePosition(m_server->findSpawnPos());
599
600                 // Make sure the player is saved
601                 player->setModified(true);
602         } else {
603                 // If the player exists, ensure that they respawn inside legal bounds
604                 // This fixes an assert crash when the player can't be added
605                 // to the environment
606                 if (objectpos_over_limit(playersao->getBasePosition())) {
607                         actionstream << "Respawn position for player \""
608                                 << player->getName() << "\" outside limits, resetting" << std::endl;
609                         playersao->setBasePosition(m_server->findSpawnPos());
610                 }
611         }
612
613         // Add player to environment
614         addPlayer(player);
615
616         /* Clean up old HUD elements from previous sessions */
617         player->clearHud();
618
619         /* Add object to environment */
620         addActiveObject(playersao);
621
622         return playersao;
623 }
624
625 void ServerEnvironment::saveMeta()
626 {
627         if (!m_meta_loaded)
628                 return;
629
630         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
631
632         // Open file and serialize
633         std::ostringstream ss(std::ios_base::binary);
634
635         Settings args("EnvArgsEnd");
636         args.setU64("game_time", m_game_time);
637         args.setU64("time_of_day", getTimeOfDay());
638         args.setU64("last_clear_objects_time", m_last_clear_objects_time);
639         args.setU64("lbm_introduction_times_version", 1);
640         args.set("lbm_introduction_times",
641                 m_lbm_mgr.createIntroductionTimesString());
642         args.setU64("day_count", m_day_count);
643         args.writeLines(ss);
644
645         if(!fs::safeWriteToFile(path, ss.str()))
646         {
647                 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
648                         <<path<<std::endl;
649                 throw SerializationError("Couldn't save env meta");
650         }
651 }
652
653 void ServerEnvironment::loadMeta()
654 {
655         SANITY_CHECK(!m_meta_loaded);
656         m_meta_loaded = true;
657
658         // If file doesn't exist, load default environment metadata
659         if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
660                 infostream << "ServerEnvironment: Loading default environment metadata"
661                         << std::endl;
662                 loadDefaultMeta();
663                 return;
664         }
665
666         infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
667
668         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
669
670         // Open file and deserialize
671         std::ifstream is(path.c_str(), std::ios_base::binary);
672         if (!is.good()) {
673                 infostream << "ServerEnvironment::loadMeta(): Failed to open "
674                         << path << std::endl;
675                 throw SerializationError("Couldn't load env meta");
676         }
677
678         Settings args("EnvArgsEnd");
679
680         if (!args.parseConfigLines(is)) {
681                 throw SerializationError("ServerEnvironment::loadMeta(): "
682                         "EnvArgsEnd not found!");
683         }
684
685         try {
686                 m_game_time = args.getU64("game_time");
687         } catch (SettingNotFoundException &e) {
688                 // Getting this is crucial, otherwise timestamps are useless
689                 throw SerializationError("Couldn't load env meta game_time");
690         }
691
692         setTimeOfDay(args.exists("time_of_day") ?
693                 // set day to early morning by default
694                 args.getU64("time_of_day") : 5250);
695
696         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
697                 // If missing, do as if clearObjects was never called
698                 args.getU64("last_clear_objects_time") : 0;
699
700         std::string lbm_introduction_times;
701         try {
702                 u64 ver = args.getU64("lbm_introduction_times_version");
703                 if (ver == 1) {
704                         lbm_introduction_times = args.get("lbm_introduction_times");
705                 } else {
706                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
707                                 << " introduction time version " << ver << std::endl;
708                 }
709         } catch (SettingNotFoundException &e) {
710                 // No problem, this is expected. Just continue with an empty string
711         }
712         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
713
714         m_day_count = args.exists("day_count") ?
715                 args.getU64("day_count") : 0;
716 }
717
718 /**
719  * called if env_meta.txt doesn't exist (e.g. new world)
720  */
721 void ServerEnvironment::loadDefaultMeta()
722 {
723         m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
724 }
725
726 struct ActiveABM
727 {
728         ActiveBlockModifier *abm;
729         int chance;
730         std::vector<content_t> required_neighbors;
731         bool check_required_neighbors; // false if required_neighbors is known to be empty
732         s16 min_y;
733         s16 max_y;
734 };
735
736 class ABMHandler
737 {
738 private:
739         ServerEnvironment *m_env;
740         std::vector<std::vector<ActiveABM> *> m_aabms;
741 public:
742         ABMHandler(std::vector<ABMWithState> &abms,
743                 float dtime_s, ServerEnvironment *env,
744                 bool use_timers):
745                 m_env(env)
746         {
747                 if(dtime_s < 0.001)
748                         return;
749                 const NodeDefManager *ndef = env->getGameDef()->ndef();
750                 for (ABMWithState &abmws : abms) {
751                         ActiveBlockModifier *abm = abmws.abm;
752                         float trigger_interval = abm->getTriggerInterval();
753                         if(trigger_interval < 0.001)
754                                 trigger_interval = 0.001;
755                         float actual_interval = dtime_s;
756                         if(use_timers){
757                                 abmws.timer += dtime_s;
758                                 if(abmws.timer < trigger_interval)
759                                         continue;
760                                 abmws.timer -= trigger_interval;
761                                 actual_interval = trigger_interval;
762                         }
763                         float chance = abm->getTriggerChance();
764                         if(chance == 0)
765                                 chance = 1;
766                         ActiveABM aabm;
767                         aabm.abm = abm;
768                         if (abm->getSimpleCatchUp()) {
769                                 float intervals = actual_interval / trigger_interval;
770                                 if(intervals == 0)
771                                         continue;
772                                 aabm.chance = chance / intervals;
773                                 if(aabm.chance == 0)
774                                         aabm.chance = 1;
775                         } else {
776                                 aabm.chance = chance;
777                         }
778                         // y limits
779                         aabm.min_y = abm->getMinY();
780                         aabm.max_y = abm->getMaxY();
781
782                         // Trigger neighbors
783                         const std::vector<std::string> &required_neighbors_s =
784                                 abm->getRequiredNeighbors();
785                         for (const std::string &required_neighbor_s : required_neighbors_s) {
786                                 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
787                         }
788                         aabm.check_required_neighbors = !required_neighbors_s.empty();
789
790                         // Trigger contents
791                         const std::vector<std::string> &contents_s = abm->getTriggerContents();
792                         for (const std::string &content_s : contents_s) {
793                                 std::vector<content_t> ids;
794                                 ndef->getIds(content_s, ids);
795                                 for (content_t c : ids) {
796                                         if (c >= m_aabms.size())
797                                                 m_aabms.resize(c + 256, NULL);
798                                         if (!m_aabms[c])
799                                                 m_aabms[c] = new std::vector<ActiveABM>;
800                                         m_aabms[c]->push_back(aabm);
801                                 }
802                         }
803                 }
804         }
805
806         ~ABMHandler()
807         {
808                 for (auto &aabms : m_aabms)
809                         delete aabms;
810         }
811
812         // Find out how many objects the given block and its neighbours contain.
813         // Returns the number of objects in the block, and also in 'wider' the
814         // number of objects in the block and all its neighbours. The latter
815         // may an estimate if any neighbours are unloaded.
816         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
817         {
818                 wider = 0;
819                 u32 wider_unknown_count = 0;
820                 for(s16 x=-1; x<=1; x++)
821                         for(s16 y=-1; y<=1; y++)
822                                 for(s16 z=-1; z<=1; z++)
823                                 {
824                                         MapBlock *block2 = map->getBlockNoCreateNoEx(
825                                                 block->getPos() + v3s16(x,y,z));
826                                         if(block2==NULL){
827                                                 wider_unknown_count++;
828                                                 continue;
829                                         }
830                                         wider += block2->m_static_objects.m_active.size()
831                                                 + block2->m_static_objects.m_stored.size();
832                                 }
833                 // Extrapolate
834                 u32 active_object_count = block->m_static_objects.m_active.size();
835                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
836                 wider += wider_unknown_count * wider / wider_known_count;
837                 return active_object_count;
838
839         }
840         void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
841         {
842                 if(m_aabms.empty() || block->isDummy())
843                         return;
844
845                 // Check the content type cache first
846                 // to see whether there are any ABMs
847                 // to be run at all for this block.
848                 if (block->contents_cached) {
849                         blocks_cached++;
850                         bool run_abms = false;
851                         for (content_t c : block->contents) {
852                                 if (c < m_aabms.size() && m_aabms[c]) {
853                                         run_abms = true;
854                                         break;
855                                 }
856                         }
857                         if (!run_abms)
858                                 return;
859                 } else {
860                         // Clear any caching
861                         block->contents.clear();
862                 }
863                 blocks_scanned++;
864
865                 ServerMap *map = &m_env->getServerMap();
866
867                 u32 active_object_count_wider;
868                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
869                 m_env->m_added_objects = 0;
870
871                 v3s16 p0;
872                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
873                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
874                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
875                 {
876                         const MapNode &n = block->getNodeUnsafe(p0);
877                         content_t c = n.getContent();
878                         // Cache content types as we go
879                         if (!block->contents_cached && !block->do_not_cache_contents) {
880                                 block->contents.insert(c);
881                                 if (block->contents.size() > 64) {
882                                         // Too many different nodes... don't try to cache
883                                         block->do_not_cache_contents = true;
884                                         block->contents.clear();
885                                 }
886                         }
887
888                         if (c >= m_aabms.size() || !m_aabms[c])
889                                 continue;
890
891                         v3s16 p = p0 + block->getPosRelative();
892                         for (ActiveABM &aabm : *m_aabms[c]) {
893                                 if ((p.Y < aabm.min_y) || (p.Y > aabm.max_y))
894                                         continue;
895                                 
896                                 if (myrand() % aabm.chance != 0)
897                                         continue;
898
899                                 // Check neighbors
900                                 if (aabm.check_required_neighbors) {
901                                         v3s16 p1;
902                                         for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
903                                         for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
904                                         for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
905                                         {
906                                                 if(p1 == p0)
907                                                         continue;
908                                                 content_t c;
909                                                 if (block->isValidPosition(p1)) {
910                                                         // if the neighbor is found on the same map block
911                                                         // get it straight from there
912                                                         const MapNode &n = block->getNodeUnsafe(p1);
913                                                         c = n.getContent();
914                                                 } else {
915                                                         // otherwise consult the map
916                                                         MapNode n = map->getNode(p1 + block->getPosRelative());
917                                                         c = n.getContent();
918                                                 }
919                                                 if (CONTAINS(aabm.required_neighbors, c))
920                                                         goto neighbor_found;
921                                         }
922                                         // No required neighbor found
923                                         continue;
924                                 }
925                                 neighbor_found:
926
927                                 abms_run++;
928                                 // Call all the trigger variations
929                                 aabm.abm->trigger(m_env, p, n);
930                                 aabm.abm->trigger(m_env, p, n,
931                                         active_object_count, active_object_count_wider);
932
933                                 // Count surrounding objects again if the abms added any
934                                 if(m_env->m_added_objects > 0) {
935                                         active_object_count = countObjects(block, map, active_object_count_wider);
936                                         m_env->m_added_objects = 0;
937                                 }
938                         }
939                 }
940                 block->contents_cached = !block->do_not_cache_contents;
941         }
942 };
943
944 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
945 {
946         // Reset usage timer immediately, otherwise a block that becomes active
947         // again at around the same time as it would normally be unloaded will
948         // get unloaded incorrectly. (I think this still leaves a small possibility
949         // of a race condition between this and server::AsyncRunStep, which only
950         // some kind of synchronisation will fix, but it at least reduces the window
951         // of opportunity for it to break from seconds to nanoseconds)
952         block->resetUsageTimer();
953
954         // Get time difference
955         u32 dtime_s = 0;
956         u32 stamp = block->getTimestamp();
957         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
958                 dtime_s = m_game_time - stamp;
959         dtime_s += additional_dtime;
960
961         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
962                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
963
964         // Remove stored static objects if clearObjects was called since block's timestamp
965         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
966                 block->m_static_objects.m_stored.clear();
967                 // do not set changed flag to avoid unnecessary mapblock writes
968         }
969
970         // Set current time as timestamp
971         block->setTimestampNoChangedFlag(m_game_time);
972
973         /*infostream<<"ServerEnvironment::activateBlock(): block is "
974                         <<dtime_s<<" seconds old."<<std::endl;*/
975
976         // Activate stored objects
977         activateObjects(block, dtime_s);
978
979         /* Handle LoadingBlockModifiers */
980         m_lbm_mgr.applyLBMs(this, block, stamp);
981
982         // Run node timers
983         std::vector<NodeTimer> elapsed_timers =
984                 block->m_node_timers.step((float)dtime_s);
985         if (!elapsed_timers.empty()) {
986                 MapNode n;
987                 for (const NodeTimer &elapsed_timer : elapsed_timers) {
988                         n = block->getNodeNoEx(elapsed_timer.position);
989                         v3s16 p = elapsed_timer.position + block->getPosRelative();
990                         if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
991                                 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
992                                         elapsed_timer.position));
993                 }
994         }
995 }
996
997 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
998 {
999         m_abms.emplace_back(abm);
1000 }
1001
1002 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1003 {
1004         m_lbm_mgr.addLBMDef(lbm);
1005 }
1006
1007 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1008 {
1009         const NodeDefManager *ndef = m_server->ndef();
1010         MapNode n_old = m_map->getNode(p);
1011
1012         const ContentFeatures &cf_old = ndef->get(n_old);
1013
1014         // Call destructor
1015         if (cf_old.has_on_destruct)
1016                 m_script->node_on_destruct(p, n_old);
1017
1018         // Replace node
1019         if (!m_map->addNodeWithEvent(p, n))
1020                 return false;
1021
1022         // Update active VoxelManipulator if a mapgen thread
1023         m_map->updateVManip(p);
1024
1025         // Call post-destructor
1026         if (cf_old.has_after_destruct)
1027                 m_script->node_after_destruct(p, n_old);
1028
1029         // Retrieve node content features
1030         // if new node is same as old, reuse old definition to prevent a lookup
1031         const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1032
1033         // Call constructor
1034         if (cf_new.has_on_construct)
1035                 m_script->node_on_construct(p, n);
1036
1037         return true;
1038 }
1039
1040 bool ServerEnvironment::removeNode(v3s16 p)
1041 {
1042         const NodeDefManager *ndef = m_server->ndef();
1043         MapNode n_old = m_map->getNode(p);
1044
1045         // Call destructor
1046         if (ndef->get(n_old).has_on_destruct)
1047                 m_script->node_on_destruct(p, n_old);
1048
1049         // Replace with air
1050         // This is slightly optimized compared to addNodeWithEvent(air)
1051         if (!m_map->removeNodeWithEvent(p))
1052                 return false;
1053
1054         // Update active VoxelManipulator if a mapgen thread
1055         m_map->updateVManip(p);
1056
1057         // Call post-destructor
1058         if (ndef->get(n_old).has_after_destruct)
1059                 m_script->node_after_destruct(p, n_old);
1060
1061         // Air doesn't require constructor
1062         return true;
1063 }
1064
1065 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1066 {
1067         if (!m_map->addNodeWithEvent(p, n, false))
1068                 return false;
1069
1070         // Update active VoxelManipulator if a mapgen thread
1071         m_map->updateVManip(p);
1072
1073         return true;
1074 }
1075
1076 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1077 {
1078         // Directions for neighbouring nodes with specified order
1079         static const v3s16 dirs[] = {
1080                 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1081                 v3s16(0, -1, 0), v3s16(0, 1, 0)
1082         };
1083
1084         const NodeDefManager *ndef = m_server->ndef();
1085
1086         // found_light remembers the highest known sunlight value at pos
1087         u8 found_light = 0;
1088
1089         struct stack_entry {
1090                 v3s16 pos;
1091                 s16 dist;
1092         };
1093         std::stack<stack_entry> stack;
1094         stack.push({pos, 0});
1095
1096         std::unordered_map<s64, s8> dists;
1097         dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1098
1099         while (!stack.empty()) {
1100                 struct stack_entry e = stack.top();
1101                 stack.pop();
1102
1103                 v3s16 currentPos = e.pos;
1104                 s8 dist = e.dist + 1;
1105
1106                 for (const v3s16& off : dirs) {
1107                         v3s16 neighborPos = currentPos + off;
1108                         s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1109
1110                         // Do not walk neighborPos multiple times unless the distance to the start
1111                         // position is shorter
1112                         auto it = dists.find(neighborHash);
1113                         if (it != dists.end() && dist >= it->second)
1114                                 continue;
1115
1116                         // Position to walk
1117                         bool is_position_ok;
1118                         MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1119                         if (!is_position_ok) {
1120                                 // This happens very rarely because the map at currentPos is loaded
1121                                 m_map->emergeBlock(neighborPos, false);
1122                                 node = m_map->getNode(neighborPos, &is_position_ok);
1123                                 if (!is_position_ok)
1124                                         continue;  // not generated
1125                         }
1126
1127                         const ContentFeatures &def = ndef->get(node);
1128                         if (!def.sunlight_propagates) {
1129                                 // Do not test propagation here again
1130                                 dists[neighborHash] = -1;
1131                                 continue;
1132                         }
1133
1134                         // Sunlight could have come from here
1135                         dists[neighborHash] = dist;
1136                         u8 daylight = node.param1 & 0x0f;
1137
1138                         // In the special case where sunlight shines from above and thus
1139                         // does not decrease with upwards distance, daylight is always
1140                         // bigger than nightlight, which never reaches 15
1141                         int possible_finlight = daylight - dist;
1142                         if (possible_finlight <= found_light) {
1143                                 // Light from here cannot make a brighter light at currentPos than
1144                                 // found_light
1145                                 continue;
1146                         }
1147
1148                         u8 nightlight = node.param1 >> 4;
1149                         if (daylight > nightlight) {
1150                                 // Found a valid daylight
1151                                 found_light = possible_finlight;
1152                         } else {
1153                                 // Sunlight may be darker, so walk the neighbours
1154                                 stack.push({neighborPos, dist});
1155                         }
1156                 }
1157         }
1158         return found_light;
1159 }
1160
1161 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1162 {
1163         infostream << "ServerEnvironment::clearObjects(): "
1164                 << "Removing all active objects" << std::endl;
1165         auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1166                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1167                         return false;
1168
1169                 // Delete static object if block is loaded
1170                 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1171
1172                 // If known by some client, don't delete immediately
1173                 if (obj->m_known_by_count > 0) {
1174                         obj->markForRemoval();
1175                         return false;
1176                 }
1177
1178                 // Tell the object about removal
1179                 obj->removingFromEnvironment();
1180                 // Deregister in scripting api
1181                 m_script->removeObjectReference(obj);
1182
1183                 // Delete active object
1184                 if (obj->environmentDeletes())
1185                         delete obj;
1186
1187                 return true;
1188         };
1189
1190         m_ao_manager.clear(cb_removal);
1191
1192         // Get list of loaded blocks
1193         std::vector<v3s16> loaded_blocks;
1194         infostream << "ServerEnvironment::clearObjects(): "
1195                 << "Listing all loaded blocks" << std::endl;
1196         m_map->listAllLoadedBlocks(loaded_blocks);
1197         infostream << "ServerEnvironment::clearObjects(): "
1198                 << "Done listing all loaded blocks: "
1199                 << loaded_blocks.size()<<std::endl;
1200
1201         // Get list of loadable blocks
1202         std::vector<v3s16> loadable_blocks;
1203         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1204                 infostream << "ServerEnvironment::clearObjects(): "
1205                         << "Listing all loadable blocks" << std::endl;
1206                 m_map->listAllLoadableBlocks(loadable_blocks);
1207                 infostream << "ServerEnvironment::clearObjects(): "
1208                         << "Done listing all loadable blocks: "
1209                         << loadable_blocks.size() << std::endl;
1210         } else {
1211                 loadable_blocks = loaded_blocks;
1212         }
1213
1214         actionstream << "ServerEnvironment::clearObjects(): "
1215                 << "Now clearing objects in " << loadable_blocks.size()
1216                 << " blocks" << std::endl;
1217
1218         // Grab a reference on each loaded block to avoid unloading it
1219         for (v3s16 p : loaded_blocks) {
1220                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1221                 assert(block != NULL);
1222                 block->refGrab();
1223         }
1224
1225         // Remove objects in all loadable blocks
1226         u32 unload_interval = U32_MAX;
1227         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1228                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1229                 unload_interval = MYMAX(unload_interval, 1);
1230         }
1231         u32 report_interval = loadable_blocks.size() / 10;
1232         u32 num_blocks_checked = 0;
1233         u32 num_blocks_cleared = 0;
1234         u32 num_objs_cleared = 0;
1235         for (auto i = loadable_blocks.begin();
1236                 i != loadable_blocks.end(); ++i) {
1237                 v3s16 p = *i;
1238                 MapBlock *block = m_map->emergeBlock(p, false);
1239                 if (!block) {
1240                         errorstream << "ServerEnvironment::clearObjects(): "
1241                                 << "Failed to emerge block " << PP(p) << std::endl;
1242                         continue;
1243                 }
1244                 u32 num_stored = block->m_static_objects.m_stored.size();
1245                 u32 num_active = block->m_static_objects.m_active.size();
1246                 if (num_stored != 0 || num_active != 0) {
1247                         block->m_static_objects.m_stored.clear();
1248                         block->m_static_objects.m_active.clear();
1249                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1250                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1251                         num_objs_cleared += num_stored + num_active;
1252                         num_blocks_cleared++;
1253                 }
1254                 num_blocks_checked++;
1255
1256                 if (report_interval != 0 &&
1257                         num_blocks_checked % report_interval == 0) {
1258                         float percent = 100.0 * (float)num_blocks_checked /
1259                                 loadable_blocks.size();
1260                         actionstream << "ServerEnvironment::clearObjects(): "
1261                                 << "Cleared " << num_objs_cleared << " objects"
1262                                 << " in " << num_blocks_cleared << " blocks ("
1263                                 << percent << "%)" << std::endl;
1264                 }
1265                 if (num_blocks_checked % unload_interval == 0) {
1266                         m_map->unloadUnreferencedBlocks();
1267                 }
1268         }
1269         m_map->unloadUnreferencedBlocks();
1270
1271         // Drop references that were added above
1272         for (v3s16 p : loaded_blocks) {
1273                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1274                 assert(block);
1275                 block->refDrop();
1276         }
1277
1278         m_last_clear_objects_time = m_game_time;
1279
1280         actionstream << "ServerEnvironment::clearObjects(): "
1281                 << "Finished: Cleared " << num_objs_cleared << " objects"
1282                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1283 }
1284
1285 void ServerEnvironment::step(float dtime)
1286 {
1287         ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1288         /* Step time of day */
1289         stepTimeOfDay(dtime);
1290
1291         // Update this one
1292         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1293         // really matter that much.
1294         static thread_local const float server_step =
1295                         g_settings->getFloat("dedicated_server_step");
1296         m_recommended_send_interval = server_step;
1297
1298         /*
1299                 Increment game time
1300         */
1301         {
1302                 m_game_time_fraction_counter += dtime;
1303                 u32 inc_i = (u32)m_game_time_fraction_counter;
1304                 m_game_time += inc_i;
1305                 m_game_time_fraction_counter -= (float)inc_i;
1306         }
1307
1308         /*
1309                 Handle players
1310         */
1311         {
1312                 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1313                 for (RemotePlayer *player : m_players) {
1314                         // Ignore disconnected players
1315                         if (player->getPeerId() == PEER_ID_INEXISTENT)
1316                                 continue;
1317
1318                         // Move
1319                         player->move(dtime, this, 100 * BS);
1320                 }
1321         }
1322
1323         /*
1324                 Manage active block list
1325         */
1326         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1327                 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1328                 /*
1329                         Get player block positions
1330                 */
1331                 std::vector<PlayerSAO*> players;
1332                 for (RemotePlayer *player: m_players) {
1333                         // Ignore disconnected players
1334                         if (player->getPeerId() == PEER_ID_INEXISTENT)
1335                                 continue;
1336
1337                         PlayerSAO *playersao = player->getPlayerSAO();
1338                         assert(playersao);
1339
1340                         players.push_back(playersao);
1341                 }
1342
1343                 /*
1344                         Update list of active blocks, collecting changes
1345                 */
1346                 // use active_object_send_range_blocks since that is max distance
1347                 // for active objects sent the client anyway
1348                 static thread_local const s16 active_object_range =
1349                                 g_settings->getS16("active_object_send_range_blocks");
1350                 static thread_local const s16 active_block_range =
1351                                 g_settings->getS16("active_block_range");
1352                 std::set<v3s16> blocks_removed;
1353                 std::set<v3s16> blocks_added;
1354                 m_active_blocks.update(players, active_block_range, active_object_range,
1355                         blocks_removed, blocks_added);
1356
1357                 /*
1358                         Handle removed blocks
1359                 */
1360
1361                 // Convert active objects that are no more in active blocks to static
1362                 deactivateFarObjects(false);
1363
1364                 for (const v3s16 &p: blocks_removed) {
1365                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1366                         if (!block)
1367                                 continue;
1368
1369                         // Set current time as timestamp (and let it set ChangedFlag)
1370                         block->setTimestamp(m_game_time);
1371                 }
1372
1373                 /*
1374                         Handle added blocks
1375                 */
1376
1377                 for (const v3s16 &p: blocks_added) {
1378                         MapBlock *block = m_map->getBlockOrEmerge(p);
1379                         if (!block) {
1380                                 m_active_blocks.m_list.erase(p);
1381                                 m_active_blocks.m_abm_list.erase(p);
1382                                 continue;
1383                         }
1384
1385                         activateBlock(block);
1386                 }
1387         }
1388
1389         /*
1390                 Mess around in active blocks
1391         */
1392         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1393                 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1394
1395                 float dtime = m_cache_nodetimer_interval;
1396
1397                 for (const v3s16 &p: m_active_blocks.m_list) {
1398                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1399                         if (!block)
1400                                 continue;
1401
1402                         // Reset block usage timer
1403                         block->resetUsageTimer();
1404
1405                         // Set current time as timestamp
1406                         block->setTimestampNoChangedFlag(m_game_time);
1407                         // If time has changed much from the one on disk,
1408                         // set block to be saved when it is unloaded
1409                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1410                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1411                                         MOD_REASON_BLOCK_EXPIRED);
1412
1413                         // Run node timers
1414                         std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1415                         if (!elapsed_timers.empty()) {
1416                                 MapNode n;
1417                                 v3s16 p2;
1418                                 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1419                                         n = block->getNodeNoEx(elapsed_timer.position);
1420                                         p2 = elapsed_timer.position + block->getPosRelative();
1421                                         if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1422                                                 block->setNodeTimer(NodeTimer(
1423                                                         elapsed_timer.timeout, 0, elapsed_timer.position));
1424                                         }
1425                                 }
1426                         }
1427                 }
1428         }
1429
1430         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1431                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1432                 TimeTaker timer("modify in active blocks per interval");
1433
1434                 // Initialize handling of ActiveBlockModifiers
1435                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1436
1437                 int blocks_scanned = 0;
1438                 int abms_run = 0;
1439                 int blocks_cached = 0;
1440
1441                 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1442
1443                 // Shuffle the active blocks so that each block gets an equal chance
1444                 // of having its ABMs run.
1445                 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1446                 std::shuffle(output.begin(), output.end(), m_rgen);
1447
1448                 int i = 0;
1449                 // determine the time budget for ABMs
1450                 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1451                 for (const v3s16 &p : output) {
1452                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1453                         if (!block)
1454                                 continue;
1455
1456                         i++;
1457
1458                         // Set current time as timestamp
1459                         block->setTimestampNoChangedFlag(m_game_time);
1460
1461                         /* Handle ActiveBlockModifiers */
1462                         abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1463
1464                         u32 time_ms = timer.getTimerTime();
1465
1466                         if (time_ms > max_time_ms) {
1467                                 warningstream << "active block modifiers took "
1468                                           << time_ms << "ms (processed " << i << " of "
1469                                           << output.size() << " active blocks)" << std::endl;
1470                                 break;
1471                         }
1472                 }
1473                 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1474                 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1475                 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1476                 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1477
1478                 timer.stop(true);
1479         }
1480
1481         /*
1482                 Step script environment (run global on_step())
1483         */
1484         m_script->environment_Step(dtime);
1485
1486         /*
1487                 Step active objects
1488         */
1489         {
1490                 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1491
1492                 // This helps the objects to send data at the same time
1493                 bool send_recommended = false;
1494                 m_send_recommended_timer += dtime;
1495                 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1496                         m_send_recommended_timer -= getSendRecommendedInterval();
1497                         send_recommended = true;
1498                 }
1499
1500                 auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
1501                         if (obj->isGone())
1502                                 return;
1503
1504                         // Step object
1505                         obj->step(dtime, send_recommended);
1506                         // Read messages from object
1507                         obj->dumpAOMessagesToQueue(m_active_object_messages);
1508                 };
1509                 m_ao_manager.step(dtime, cb_state);
1510         }
1511
1512         /*
1513                 Manage active objects
1514         */
1515         if (m_object_management_interval.step(dtime, 0.5)) {
1516                 removeRemovedObjects();
1517         }
1518
1519         /*
1520                 Manage particle spawner expiration
1521         */
1522         if (m_particle_management_interval.step(dtime, 1.0)) {
1523                 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1524                         i != m_particle_spawners.end(); ) {
1525                         //non expiring spawners
1526                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1527                                 ++i;
1528                                 continue;
1529                         }
1530
1531                         i->second -= 1.0f;
1532                         if (i->second <= 0.f)
1533                                 m_particle_spawners.erase(i++);
1534                         else
1535                                 ++i;
1536                 }
1537         }
1538
1539         // Send outdated player inventories
1540         for (RemotePlayer *player : m_players) {
1541                 if (player->getPeerId() == PEER_ID_INEXISTENT)
1542                         continue;
1543
1544                 PlayerSAO *sao = player->getPlayerSAO();
1545                 if (sao && player->inventory.checkModified())
1546                         m_server->SendInventory(sao, true);
1547         }
1548
1549         // Send outdated detached inventories
1550         m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1551 }
1552
1553 ServerEnvironment::BlockStatus ServerEnvironment::getBlockStatus(v3s16 blockpos)
1554 {
1555         if (m_active_blocks.contains(blockpos))
1556                 return BS_ACTIVE;
1557
1558         const MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1559         if (block && !block->isDummy())
1560                 return BS_LOADED;
1561
1562         if (m_map->isBlockInQueue(blockpos))
1563                 return BS_EMERGING;
1564
1565         return BS_UNKNOWN;
1566 }
1567
1568 u32 ServerEnvironment::addParticleSpawner(float exptime)
1569 {
1570         // Timers with lifetime 0 do not expire
1571         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1572
1573         u32 id = 0;
1574         for (;;) { // look for unused particlespawner id
1575                 id++;
1576                 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1577                 if (f == m_particle_spawners.end()) {
1578                         m_particle_spawners[id] = time;
1579                         break;
1580                 }
1581         }
1582         return id;
1583 }
1584
1585 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1586 {
1587         u32 id = addParticleSpawner(exptime);
1588         m_particle_spawner_attachments[id] = attached_id;
1589         if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1590                 obj->attachParticleSpawner(id);
1591         }
1592         return id;
1593 }
1594
1595 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1596 {
1597         m_particle_spawners.erase(id);
1598         const auto &it = m_particle_spawner_attachments.find(id);
1599         if (it != m_particle_spawner_attachments.end()) {
1600                 u16 obj_id = it->second;
1601                 ServerActiveObject *sao = getActiveObject(obj_id);
1602                 if (sao != NULL && remove_from_object) {
1603                         sao->detachParticleSpawner(id);
1604                 }
1605                 m_particle_spawner_attachments.erase(id);
1606         }
1607 }
1608
1609 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1610 {
1611         assert(object); // Pre-condition
1612         m_added_objects++;
1613         u16 id = addActiveObjectRaw(object, true, 0);
1614         return id;
1615 }
1616
1617 /*
1618         Finds out what new objects have been added to
1619         inside a radius around a position
1620 */
1621 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1622         s16 player_radius,
1623         std::set<u16> &current_objects,
1624         std::queue<u16> &added_objects)
1625 {
1626         f32 radius_f = radius * BS;
1627         f32 player_radius_f = player_radius * BS;
1628
1629         if (player_radius_f < 0.0f)
1630                 player_radius_f = 0.0f;
1631
1632         m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1633                 player_radius_f, current_objects, added_objects);
1634 }
1635
1636 /*
1637         Finds out what objects have been removed from
1638         inside a radius around a position
1639 */
1640 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1641         s16 player_radius,
1642         std::set<u16> &current_objects,
1643         std::queue<u16> &removed_objects)
1644 {
1645         f32 radius_f = radius * BS;
1646         f32 player_radius_f = player_radius * BS;
1647
1648         if (player_radius_f < 0)
1649                 player_radius_f = 0;
1650         /*
1651                 Go through current_objects; object is removed if:
1652                 - object is not found in m_active_objects (this is actually an
1653                   error condition; objects should be removed only after all clients
1654                   have been informed about removal), or
1655                 - object is to be removed or deactivated, or
1656                 - object is too far away
1657         */
1658         for (u16 id : current_objects) {
1659                 ServerActiveObject *object = getActiveObject(id);
1660
1661                 if (object == NULL) {
1662                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1663                                 << " object in current_objects is NULL" << std::endl;
1664                         removed_objects.push(id);
1665                         continue;
1666                 }
1667
1668                 if (object->isGone()) {
1669                         removed_objects.push(id);
1670                         continue;
1671                 }
1672
1673                 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1674                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1675                         if (distance_f <= player_radius_f || player_radius_f == 0)
1676                                 continue;
1677                 } else if (distance_f <= radius_f)
1678                         continue;
1679
1680                 // Object is no longer visible
1681                 removed_objects.push(id);
1682         }
1683 }
1684
1685 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1686         v3s16 blockpos, bool static_exists, v3s16 static_block)
1687 {
1688         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1689         if (!block)
1690                 return;
1691
1692         for (auto &so_it : block->m_static_objects.m_active) {
1693                 // Get the ServerActiveObject counterpart to this StaticObject
1694                 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1695                 if (!sao) {
1696                         // If this ever happens, there must be some kind of nasty bug.
1697                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1698                                 "Object from MapBlock::m_static_objects::m_active not found "
1699                                 "in m_active_objects";
1700                         continue;
1701                 }
1702
1703                 sao->m_static_exists = static_exists;
1704                 sao->m_static_block  = static_block;
1705         }
1706 }
1707
1708 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1709 {
1710         if(m_active_object_messages.empty())
1711                 return false;
1712
1713         *dest = std::move(m_active_object_messages.front());
1714         m_active_object_messages.pop();
1715         return true;
1716 }
1717
1718 void ServerEnvironment::getSelectedActiveObjects(
1719         const core::line3d<f32> &shootline_on_map,
1720         std::vector<PointedThing> &objects)
1721 {
1722         std::vector<ServerActiveObject *> objs;
1723         getObjectsInsideRadius(objs, shootline_on_map.start,
1724                 shootline_on_map.getLength() + 10.0f, nullptr);
1725         const v3f line_vector = shootline_on_map.getVector();
1726
1727         for (auto obj : objs) {
1728                 if (obj->isGone())
1729                         continue;
1730                 aabb3f selection_box;
1731                 if (!obj->getSelectionBox(&selection_box))
1732                         continue;
1733
1734                 v3f pos = obj->getBasePosition();
1735
1736                 aabb3f offsetted_box(selection_box.MinEdge + pos,
1737                         selection_box.MaxEdge + pos);
1738
1739                 v3f current_intersection;
1740                 v3s16 current_normal;
1741                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1742                                 &current_intersection, &current_normal)) {
1743                         objects.emplace_back(
1744                                 (s16) obj->getId(), current_intersection, current_normal,
1745                                 (current_intersection - shootline_on_map.start).getLengthSQ());
1746                 }
1747         }
1748 }
1749
1750 /*
1751         ************ Private methods *************
1752 */
1753
1754 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1755         bool set_changed, u32 dtime_s)
1756 {
1757         if (!m_ao_manager.registerObject(object)) {
1758                 return 0;
1759         }
1760
1761         // Register reference in scripting api (must be done before post-init)
1762         m_script->addObjectReference(object);
1763         // Post-initialize object
1764         object->addedToEnvironment(dtime_s);
1765
1766         // Add static data to block
1767         if (object->isStaticAllowed()) {
1768                 // Add static object to active static list of the block
1769                 v3f objectpos = object->getBasePosition();
1770                 StaticObject s_obj(object, objectpos);
1771                 // Add to the block where the object is located in
1772                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1773                 MapBlock *block = m_map->emergeBlock(blockpos);
1774                 if(block){
1775                         block->m_static_objects.m_active[object->getId()] = s_obj;
1776                         object->m_static_exists = true;
1777                         object->m_static_block = blockpos;
1778
1779                         if(set_changed)
1780                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1781                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1782                 } else {
1783                         v3s16 p = floatToInt(objectpos, BS);
1784                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1785                                 <<"could not emerge block for storing id="<<object->getId()
1786                                 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1787                 }
1788         }
1789
1790         return object->getId();
1791 }
1792
1793 /*
1794         Remove objects that satisfy (isGone() && m_known_by_count==0)
1795 */
1796 void ServerEnvironment::removeRemovedObjects()
1797 {
1798         ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1799
1800         auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1801                 // This shouldn't happen but check it
1802                 if (!obj) {
1803                         errorstream << "ServerEnvironment::removeRemovedObjects(): "
1804                                         << "NULL object found. id=" << id << std::endl;
1805                         return true;
1806                 }
1807
1808                 /*
1809                         We will handle objects marked for removal or deactivation
1810                 */
1811                 if (!obj->isGone())
1812                         return false;
1813
1814                 /*
1815                         Delete static data from block if removed
1816                 */
1817                 if (obj->isPendingRemoval())
1818                         deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1819
1820                 // If still known by clients, don't actually remove. On some future
1821                 // invocation this will be 0, which is when removal will continue.
1822                 if(obj->m_known_by_count > 0)
1823                         return false;
1824
1825                 /*
1826                         Move static data from active to stored if deactivated
1827                 */
1828                 if (!obj->isPendingRemoval() && obj->m_static_exists) {
1829                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1830                         if (block) {
1831                                 const auto i = block->m_static_objects.m_active.find(id);
1832                                 if (i != block->m_static_objects.m_active.end()) {
1833                                         block->m_static_objects.m_stored.push_back(i->second);
1834                                         block->m_static_objects.m_active.erase(id);
1835                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1836                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1837                                 } else {
1838                                         warningstream << "ServerEnvironment::removeRemovedObjects(): "
1839                                                         << "id=" << id << " m_static_exists=true but "
1840                                                         << "static data doesn't actually exist in "
1841                                                         << PP(obj->m_static_block) << std::endl;
1842                                 }
1843                         } else {
1844                                 infostream << "Failed to emerge block from which an object to "
1845                                                 << "be deactivated was loaded from. id=" << id << std::endl;
1846                         }
1847                 }
1848
1849                 // Tell the object about removal
1850                 obj->removingFromEnvironment();
1851                 // Deregister in scripting api
1852                 m_script->removeObjectReference(obj);
1853
1854                 // Delete
1855                 if (obj->environmentDeletes())
1856                         delete obj;
1857
1858                 return true;
1859         };
1860
1861         m_ao_manager.clear(clear_cb);
1862 }
1863
1864 static void print_hexdump(std::ostream &o, const std::string &data)
1865 {
1866         const int linelength = 16;
1867         for(int l=0; ; l++){
1868                 int i0 = linelength * l;
1869                 bool at_end = false;
1870                 int thislinelength = linelength;
1871                 if(i0 + thislinelength > (int)data.size()){
1872                         thislinelength = data.size() - i0;
1873                         at_end = true;
1874                 }
1875                 for(int di=0; di<linelength; di++){
1876                         int i = i0 + di;
1877                         char buf[4];
1878                         if(di<thislinelength)
1879                                 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1880                         else
1881                                 porting::mt_snprintf(buf, sizeof(buf), "   ");
1882                         o<<buf;
1883                 }
1884                 o<<" ";
1885                 for(int di=0; di<thislinelength; di++){
1886                         int i = i0 + di;
1887                         if(data[i] >= 32)
1888                                 o<<data[i];
1889                         else
1890                                 o<<".";
1891                 }
1892                 o<<std::endl;
1893                 if(at_end)
1894                         break;
1895         }
1896 }
1897
1898 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1899                 const std::string &data)
1900 {
1901         switch (type) {
1902                 case ACTIVEOBJECT_TYPE_LUAENTITY:
1903                         return new LuaEntitySAO(this, pos, data);
1904                 default:
1905                         warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1906         }
1907         return nullptr;
1908 }
1909
1910 /*
1911         Convert stored objects from blocks near the players to active.
1912 */
1913 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1914 {
1915         if(block == NULL)
1916                 return;
1917
1918         // Ignore if no stored objects (to not set changed flag)
1919         if(block->m_static_objects.m_stored.empty())
1920                 return;
1921
1922         verbosestream<<"ServerEnvironment::activateObjects(): "
1923                 <<"activating objects of block "<<PP(block->getPos())
1924                 <<" ("<<block->m_static_objects.m_stored.size()
1925                 <<" objects)"<<std::endl;
1926         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1927         if (large_amount) {
1928                 errorstream<<"suspiciously large amount of objects detected: "
1929                         <<block->m_static_objects.m_stored.size()<<" in "
1930                         <<PP(block->getPos())
1931                         <<"; removing all of them."<<std::endl;
1932                 // Clear stored list
1933                 block->m_static_objects.m_stored.clear();
1934                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1935                         MOD_REASON_TOO_MANY_OBJECTS);
1936                 return;
1937         }
1938
1939         // Activate stored objects
1940         std::vector<StaticObject> new_stored;
1941         for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1942                 // Create an active object from the data
1943                 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1944                         s_obj.data);
1945                 // If couldn't create object, store static data back.
1946                 if (!obj) {
1947                         errorstream<<"ServerEnvironment::activateObjects(): "
1948                                 <<"failed to create active object from static object "
1949                                 <<"in block "<<PP(s_obj.pos/BS)
1950                                 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1951                         print_hexdump(verbosestream, s_obj.data);
1952
1953                         new_stored.push_back(s_obj);
1954                         continue;
1955                 }
1956                 verbosestream<<"ServerEnvironment::activateObjects(): "
1957                         <<"activated static object pos="<<PP(s_obj.pos/BS)
1958                         <<" type="<<(int)s_obj.type<<std::endl;
1959                 // This will also add the object to the active static list
1960                 addActiveObjectRaw(obj, false, dtime_s);
1961         }
1962
1963         // Clear stored list
1964         block->m_static_objects.m_stored.clear();
1965         // Add leftover failed stuff to stored list
1966         for (const StaticObject &s_obj : new_stored) {
1967                 block->m_static_objects.m_stored.push_back(s_obj);
1968         }
1969
1970         /*
1971                 Note: Block hasn't really been modified here.
1972                 The objects have just been activated and moved from the stored
1973                 static list to the active static list.
1974                 As such, the block is essentially the same.
1975                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1976                 Otherwise there would be a huge amount of unnecessary I/O.
1977         */
1978 }
1979
1980 /*
1981         Convert objects that are not standing inside active blocks to static.
1982
1983         If m_known_by_count != 0, active object is not deleted, but static
1984         data is still updated.
1985
1986         If force_delete is set, active object is deleted nevertheless. It
1987         shall only be set so in the destructor of the environment.
1988
1989         If block wasn't generated (not in memory or on disk),
1990 */
1991 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1992 {
1993         auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
1994                 // force_delete might be overriden per object
1995                 bool force_delete = _force_delete;
1996
1997                 // Do not deactivate if disallowed
1998                 if (!force_delete && !obj->shouldUnload())
1999                         return false;
2000
2001                 // removeRemovedObjects() is responsible for these
2002                 if (!force_delete && obj->isGone())
2003                         return false;
2004
2005                 const v3f &objectpos = obj->getBasePosition();
2006
2007                 // The block in which the object resides in
2008                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2009
2010                 // If object's static data is stored in a deactivated block and object
2011                 // is actually located in an active block, re-save to the block in
2012                 // which the object is actually located in.
2013                 if (!force_delete && obj->m_static_exists &&
2014                    !m_active_blocks.contains(obj->m_static_block) &&
2015                    m_active_blocks.contains(blockpos_o)) {
2016
2017                         // Delete from block where object was located
2018                         deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
2019
2020                         StaticObject s_obj(obj, objectpos);
2021                         // Save to block where object is located
2022                         saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2023
2024                         return false;
2025                 }
2026
2027                 // If block is still active, don't remove
2028                 bool still_active = obj->isStaticAllowed() ?
2029                         m_active_blocks.contains(blockpos_o) :
2030                         getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2031                 if (!force_delete && still_active)
2032                         return false;
2033
2034                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2035                                           << "deactivating object id=" << id << " on inactive block "
2036                                           << PP(blockpos_o) << std::endl;
2037
2038                 // If known by some client, don't immediately delete.
2039                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2040
2041                 /*
2042                         Update the static data
2043                 */
2044                 if (obj->isStaticAllowed()) {
2045                         // Create new static object
2046                         StaticObject s_obj(obj, objectpos);
2047
2048                         bool stays_in_same_block = false;
2049                         bool data_changed = true;
2050
2051                         // Check if static data has changed considerably
2052                         if (obj->m_static_exists) {
2053                                 if (obj->m_static_block == blockpos_o)
2054                                         stays_in_same_block = true;
2055
2056                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2057
2058                                 if (block) {
2059                                         const auto n = block->m_static_objects.m_active.find(id);
2060                                         if (n != block->m_static_objects.m_active.end()) {
2061                                                 StaticObject static_old = n->second;
2062
2063                                                 float save_movem = obj->getMinimumSavedMovement();
2064
2065                                                 if (static_old.data == s_obj.data &&
2066                                                         (static_old.pos - objectpos).getLength() < save_movem)
2067                                                         data_changed = false;
2068                                         } else {
2069                                                 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2070                                                                 << "id=" << id << " m_static_exists=true but "
2071                                                                 << "static data doesn't actually exist in "
2072                                                                 << PP(obj->m_static_block) << std::endl;
2073                                         }
2074                                 }
2075                         }
2076
2077                         /*
2078                                 While changes are always saved, blocks are only marked as modified
2079                                 if the object has moved or different staticdata. (see above)
2080                         */
2081                         bool shall_be_written = (!stays_in_same_block || data_changed);
2082                         u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2083
2084                         // Delete old static object
2085                         deleteStaticFromBlock(obj, id, reason, false);
2086
2087                         // Add to the block where the object is located in
2088                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2089                         u16 store_id = pending_delete ? id : 0;
2090                         if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2091                                 force_delete = true;
2092                 }
2093
2094                 // Regardless of what happens to the object at this point, deactivate it first.
2095                 // This ensures that LuaEntity on_deactivate is always called.
2096                 obj->markForDeactivation();
2097
2098                 /*
2099                         If known by some client, set pending deactivation.
2100                         Otherwise delete it immediately.
2101                 */
2102                 if (pending_delete && !force_delete) {
2103                         verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2104                                                   << "object id=" << id << " is known by clients"
2105                                                   << "; not deleting yet" << std::endl;
2106
2107                         return false;
2108                 }
2109
2110                 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2111                                           << "object id=" << id << " is not known by clients"
2112                                           << "; deleting" << std::endl;
2113
2114                 // Tell the object about removal
2115                 obj->removingFromEnvironment();
2116                 // Deregister in scripting api
2117                 m_script->removeObjectReference(obj);
2118
2119                 // Delete active object
2120                 if (obj->environmentDeletes())
2121                         delete obj;
2122
2123                 return true;
2124         };
2125
2126         m_ao_manager.clear(cb_deactivate);
2127 }
2128
2129 void ServerEnvironment::deleteStaticFromBlock(
2130                 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2131 {
2132         if (!obj->m_static_exists)
2133                 return;
2134
2135         MapBlock *block;
2136         if (no_emerge)
2137                 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2138         else
2139                 block = m_map->emergeBlock(obj->m_static_block, false);
2140         if (!block) {
2141                 if (!no_emerge)
2142                         errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2143                                         << " when deleting static data of object from it. id=" << id << std::endl;
2144                 return;
2145         }
2146
2147         block->m_static_objects.remove(id);
2148         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2149                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2150
2151         obj->m_static_exists = false;
2152 }
2153
2154 bool ServerEnvironment::saveStaticToBlock(
2155                 v3s16 blockpos, u16 store_id,
2156                 ServerActiveObject *obj, const StaticObject &s_obj,
2157                 u32 mod_reason)
2158 {
2159         MapBlock *block = nullptr;
2160         try {
2161                 block = m_map->emergeBlock(blockpos);
2162         } catch (InvalidPositionException &e) {
2163                 // Handled via NULL pointer
2164                 // NOTE: emergeBlock's failure is usually determined by it
2165                 //       actually returning NULL
2166         }
2167
2168         if (!block) {
2169                 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2170                                 << " when saving static data of object to it. id=" << store_id << std::endl;
2171                 return false;
2172         }
2173         if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2174                 warningstream << "ServerEnv: Trying to store id = " << store_id
2175                                 << " statically but block " << PP(blockpos)
2176                                 << " already contains "
2177                                 << block->m_static_objects.m_stored.size()
2178                                 << " objects." << std::endl;
2179                 return false;
2180         }
2181
2182         block->m_static_objects.insert(store_id, s_obj);
2183         if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2184                 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2185
2186         obj->m_static_exists = true;
2187         obj->m_static_block = blockpos;
2188
2189         return true;
2190 }
2191
2192 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2193                 const std::string &savedir, const Settings &conf)
2194 {
2195
2196         if (name == "sqlite3")
2197                 return new PlayerDatabaseSQLite3(savedir);
2198
2199         if (name == "dummy")
2200                 return new Database_Dummy();
2201
2202 #if USE_POSTGRESQL
2203         if (name == "postgresql") {
2204                 std::string connect_string;
2205                 conf.getNoEx("pgsql_player_connection", connect_string);
2206                 return new PlayerDatabasePostgreSQL(connect_string);
2207         }
2208 #endif
2209
2210 #if USE_LEVELDB
2211         if (name == "leveldb")
2212                 return new PlayerDatabaseLevelDB(savedir);
2213 #endif
2214
2215         if (name == "files")
2216                 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2217
2218         throw BaseException(std::string("Database backend ") + name + " not supported.");
2219 }
2220
2221 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2222                 const Settings &cmd_args)
2223 {
2224         std::string migrate_to = cmd_args.get("migrate-players");
2225         Settings world_mt;
2226         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2227         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2228                 errorstream << "Cannot read world.mt!" << std::endl;
2229                 return false;
2230         }
2231
2232         if (!world_mt.exists("player_backend")) {
2233                 errorstream << "Please specify your current backend in world.mt:"
2234                         << std::endl
2235                         << "    player_backend = {files|sqlite3|leveldb|postgresql}"
2236                         << std::endl;
2237                 return false;
2238         }
2239
2240         std::string backend = world_mt.get("player_backend");
2241         if (backend == migrate_to) {
2242                 errorstream << "Cannot migrate: new backend is same"
2243                         << " as the old one" << std::endl;
2244                 return false;
2245         }
2246
2247         const std::string players_backup_path = game_params.world_path + DIR_DELIM
2248                 + "players.bak";
2249
2250         if (backend == "files") {
2251                 // Create backup directory
2252                 fs::CreateDir(players_backup_path);
2253         }
2254
2255         try {
2256                 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2257                         game_params.world_path, world_mt);
2258                 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2259                         game_params.world_path, world_mt);
2260
2261                 std::vector<std::string> player_list;
2262                 srcdb->listPlayers(player_list);
2263                 for (std::vector<std::string>::const_iterator it = player_list.begin();
2264                         it != player_list.end(); ++it) {
2265                         actionstream << "Migrating player " << it->c_str() << std::endl;
2266                         RemotePlayer player(it->c_str(), NULL);
2267                         PlayerSAO playerSAO(NULL, &player, 15000, false);
2268
2269                         srcdb->loadPlayer(&player, &playerSAO);
2270
2271                         playerSAO.finalize(&player, std::set<std::string>());
2272                         player.setPlayerSAO(&playerSAO);
2273
2274                         dstdb->savePlayer(&player);
2275
2276                         // For files source, move player files to backup dir
2277                         if (backend == "files") {
2278                                 fs::Rename(
2279                                         game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2280                                         players_backup_path + DIR_DELIM + (*it));
2281                         }
2282                 }
2283
2284                 actionstream << "Successfully migrated " << player_list.size() << " players"
2285                         << std::endl;
2286                 world_mt.set("player_backend", migrate_to);
2287                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2288                         errorstream << "Failed to update world.mt!" << std::endl;
2289                 else
2290                         actionstream << "world.mt updated" << std::endl;
2291
2292                 // When migration is finished from file backend, remove players directory if empty
2293                 if (backend == "files") {
2294                         fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2295                                 + "players");
2296                 }
2297
2298                 delete srcdb;
2299                 delete dstdb;
2300
2301         } catch (BaseException &e) {
2302                 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2303                 return false;
2304         }
2305         return true;
2306 }
2307
2308 AuthDatabase *ServerEnvironment::openAuthDatabase(
2309                 const std::string &name, const std::string &savedir, const Settings &conf)
2310 {
2311         if (name == "sqlite3")
2312                 return new AuthDatabaseSQLite3(savedir);
2313
2314 #if USE_POSTGRESQL
2315         if (name == "postgresql") {
2316                 std::string connect_string;
2317                 conf.getNoEx("pgsql_auth_connection", connect_string);
2318                 return new AuthDatabasePostgreSQL(connect_string);
2319         }
2320 #endif
2321
2322         if (name == "files")
2323                 return new AuthDatabaseFiles(savedir);
2324
2325 #if USE_LEVELDB
2326         if (name == "leveldb")
2327                 return new AuthDatabaseLevelDB(savedir);
2328 #endif
2329
2330         throw BaseException(std::string("Database backend ") + name + " not supported.");
2331 }
2332
2333 bool ServerEnvironment::migrateAuthDatabase(
2334                 const GameParams &game_params, const Settings &cmd_args)
2335 {
2336         std::string migrate_to = cmd_args.get("migrate-auth");
2337         Settings world_mt;
2338         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2339         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2340                 errorstream << "Cannot read world.mt!" << std::endl;
2341                 return false;
2342         }
2343
2344         std::string backend = "files";
2345         if (world_mt.exists("auth_backend"))
2346                 backend = world_mt.get("auth_backend");
2347         else
2348                 warningstream << "No auth_backend found in world.mt, "
2349                                 "assuming \"files\"." << std::endl;
2350
2351         if (backend == migrate_to) {
2352                 errorstream << "Cannot migrate: new backend is same"
2353                                 << " as the old one" << std::endl;
2354                 return false;
2355         }
2356
2357         try {
2358                 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2359                                 backend, game_params.world_path, world_mt));
2360                 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2361                                 migrate_to, game_params.world_path, world_mt));
2362
2363                 std::vector<std::string> names_list;
2364                 srcdb->listNames(names_list);
2365                 for (const std::string &name : names_list) {
2366                         actionstream << "Migrating auth entry for " << name << std::endl;
2367                         bool success;
2368                         AuthEntry authEntry;
2369                         success = srcdb->getAuth(name, authEntry);
2370                         success = success && dstdb->createAuth(authEntry);
2371                         if (!success)
2372                                 errorstream << "Failed to migrate " << name << std::endl;
2373                 }
2374
2375                 actionstream << "Successfully migrated " << names_list.size()
2376                                 << " auth entries" << std::endl;
2377                 world_mt.set("auth_backend", migrate_to);
2378                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2379                         errorstream << "Failed to update world.mt!" << std::endl;
2380                 else
2381                         actionstream << "world.mt updated" << std::endl;
2382
2383                 if (backend == "files") {
2384                         // special-case files migration:
2385                         // move auth.txt to auth.txt.bak if possible
2386                         std::string auth_txt_path =
2387                                         game_params.world_path + DIR_DELIM + "auth.txt";
2388                         std::string auth_bak_path = auth_txt_path + ".bak";
2389                         if (!fs::PathExists(auth_bak_path))
2390                                 if (fs::Rename(auth_txt_path, auth_bak_path))
2391                                         actionstream << "Renamed auth.txt to auth.txt.bak"
2392                                                         << std::endl;
2393                                 else
2394                                         errorstream << "Could not rename auth.txt to "
2395                                                         "auth.txt.bak" << std::endl;
2396                         else
2397                                 warningstream << "auth.txt.bak already exists, auth.txt "
2398                                                 "not renamed" << std::endl;
2399                 }
2400
2401         } catch (BaseException &e) {
2402                 errorstream << "An error occurred during migration: " << e.what()
2403                             << std::endl;
2404                 return false;
2405         }
2406         return true;
2407 }