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