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