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