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