a0cf9dca5dbbb9d08ca265cda641d202da5da1ba
[oweals/minetest.git] / src / environment.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 <fstream>
21 #include "environment.h"
22 #include "filesys.h"
23 #include "porting.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
26 #include "mapblock.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
29 #include "settings.h"
30 #include "log.h"
31 #include "profiler.h"
32 #include "scripting_game.h"
33 #include "nodedef.h"
34 #include "nodemetadata.h"
35 #include "gamedef.h"
36 #ifndef SERVER
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
40 #include "event.h"
41 #endif
42 #include "server.h"
43 #include "daynightratio.h"
44 #include "map.h"
45 #include "emerge.h"
46 #include "raycast.h"
47 #include "voxelalgorithms.h"
48 #include "util/serialize.h"
49 #include "util/basic_macros.h"
50 #include "util/pointedthing.h"
51 #include "threading/mutex_auto_lock.h"
52
53 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
54
55 // A number that is much smaller than the timeout for particle spawners should/could ever be
56 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57
58 Environment::Environment():
59         m_time_of_day_speed(0),
60         m_time_of_day(9000),
61         m_time_of_day_f(9000./24000),
62         m_time_conversion_skew(0.0f),
63         m_enable_day_night_ratio_override(false),
64         m_day_night_ratio_override(0.0f)
65 {
66         m_cache_enable_shaders = g_settings->getBool("enable_shaders");
67         m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
68         m_cache_abm_interval = g_settings->getFloat("abm_interval");
69         m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
70 }
71
72 Environment::~Environment()
73 {
74 }
75
76 u32 Environment::getDayNightRatio()
77 {
78         MutexAutoLock lock(this->m_time_lock);
79         if (m_enable_day_night_ratio_override)
80                 return m_day_night_ratio_override;
81         return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
82 }
83
84 void Environment::setTimeOfDaySpeed(float speed)
85 {
86         m_time_of_day_speed = speed;
87 }
88
89 void Environment::setDayNightRatioOverride(bool enable, u32 value)
90 {
91         MutexAutoLock lock(this->m_time_lock);
92         m_enable_day_night_ratio_override = enable;
93         m_day_night_ratio_override = value;
94 }
95
96 void Environment::setTimeOfDay(u32 time)
97 {
98         MutexAutoLock lock(this->m_time_lock);
99         if (m_time_of_day > time)
100                 m_day_count++;
101         m_time_of_day = time;
102         m_time_of_day_f = (float)time / 24000.0;
103 }
104
105 u32 Environment::getTimeOfDay()
106 {
107         MutexAutoLock lock(this->m_time_lock);
108         return m_time_of_day;
109 }
110
111 float Environment::getTimeOfDayF()
112 {
113         MutexAutoLock lock(this->m_time_lock);
114         return m_time_of_day_f;
115 }
116
117 void Environment::stepTimeOfDay(float dtime)
118 {
119         MutexAutoLock lock(this->m_time_lock);
120
121         // Cached in order to prevent the two reads we do to give
122         // different results (can be written by code not under the lock)
123         f32 cached_time_of_day_speed = m_time_of_day_speed;
124
125         f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
126         m_time_conversion_skew += dtime;
127         u32 units = (u32)(m_time_conversion_skew * speed);
128         bool sync_f = false;
129         if (units > 0) {
130                 // Sync at overflow
131                 if (m_time_of_day + units >= 24000) {
132                         sync_f = true;
133                         m_day_count++;
134                 }
135                 m_time_of_day = (m_time_of_day + units) % 24000;
136                 if (sync_f)
137                         m_time_of_day_f = (float)m_time_of_day / 24000.0;
138         }
139         if (speed > 0) {
140                 m_time_conversion_skew -= (f32)units / speed;
141         }
142         if (!sync_f) {
143                 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
144                 if (m_time_of_day_f > 1.0)
145                         m_time_of_day_f -= 1.0;
146                 if (m_time_of_day_f < 0.0)
147                         m_time_of_day_f += 1.0;
148         }
149 }
150
151 u32 Environment::getDayCount()
152 {
153         // Atomic<u32> counter
154         return m_day_count;
155 }
156
157
158 /*
159         ABMWithState
160 */
161
162 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
163         abm(abm_),
164         timer(0)
165 {
166         // Initialize timer to random value to spread processing
167         float itv = abm->getTriggerInterval();
168         itv = MYMAX(0.001, itv); // No less than 1ms
169         int minval = MYMAX(-0.51*itv, -60); // Clamp to
170         int maxval = MYMIN(0.51*itv, 60);   // +-60 seconds
171         timer = myrand_range(minval, maxval);
172 }
173
174 /*
175         LBMManager
176 */
177
178 void LBMContentMapping::deleteContents()
179 {
180         for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
181                         it != lbm_list.end(); ++it) {
182                 delete *it;
183         }
184 }
185
186 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
187 {
188         // Add the lbm_def to the LBMContentMapping.
189         // Unknown names get added to the global NameIdMapping.
190         INodeDefManager *nodedef = gamedef->ndef();
191
192         lbm_list.push_back(lbm_def);
193
194         for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
195                         it != lbm_def->trigger_contents.end(); ++it) {
196                 std::set<content_t> c_ids;
197                 bool found = nodedef->getIds(*it, c_ids);
198                 if (!found) {
199                         content_t c_id = gamedef->allocateUnknownNodeId(*it);
200                         if (c_id == CONTENT_IGNORE) {
201                                 // Seems it can't be allocated.
202                                 warningstream << "Could not internalize node name \"" << *it
203                                         << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
204                                 continue;
205                         }
206                         c_ids.insert(c_id);
207                 }
208
209                 for (std::set<content_t>::const_iterator iit =
210                                 c_ids.begin(); iit != c_ids.end(); ++iit) {
211                         content_t c_id = *iit;
212                         map[c_id].push_back(lbm_def);
213                 }
214         }
215 }
216
217 const std::vector<LoadingBlockModifierDef *> *
218                 LBMContentMapping::lookup(content_t c) const
219 {
220         container_map::const_iterator it = map.find(c);
221         if (it == map.end())
222                 return NULL;
223         // This first dereferences the iterator, returning
224         // a std::vector<LoadingBlockModifierDef *>
225         // reference, then we convert it to a pointer.
226         return &(it->second);
227 }
228
229 LBMManager::~LBMManager()
230 {
231         for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
232                         m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
233                 delete it->second;
234         }
235         for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
236                         it != m_lbm_lookup.end(); ++it) {
237                 (it->second).deleteContents();
238         }
239 }
240
241 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
242 {
243         // Precondition, in query mode the map isn't used anymore
244         FATAL_ERROR_IF(m_query_mode == true,
245                 "attempted to modify LBMManager in query mode");
246
247         if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
248                 throw ModError("Error adding LBM \"" + lbm_def->name +
249                         "\": Does not follow naming conventions: "
250                         "Only chararacters [a-z0-9_:] are allowed.");
251         }
252
253         m_lbm_defs[lbm_def->name] = lbm_def;
254 }
255
256 void LBMManager::loadIntroductionTimes(const std::string &times,
257                 IGameDef *gamedef, u32 now)
258 {
259         m_query_mode = true;
260
261         // name -> time map.
262         // Storing it in a map first instead of
263         // handling the stuff directly in the loop
264         // removes all duplicate entries.
265         // TODO make this std::unordered_map
266         std::map<std::string, u32> introduction_times;
267
268         /*
269         The introduction times string consists of name~time entries,
270         with each entry terminated by a semicolon. The time is decimal.
271          */
272
273         size_t idx = 0;
274         size_t idx_new;
275         while ((idx_new = times.find(";", idx)) != std::string::npos) {
276                 std::string entry = times.substr(idx, idx_new - idx);
277                 std::vector<std::string> components = str_split(entry, '~');
278                 if (components.size() != 2)
279                         throw SerializationError("Introduction times entry \""
280                                 + entry + "\" requires exactly one '~'!");
281                 const std::string &name = components[0];
282                 u32 time = from_string<u32>(components[1]);
283                 introduction_times[name] = time;
284                 idx = idx_new + 1;
285         }
286
287         // Put stuff from introduction_times into m_lbm_lookup
288         for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
289                         it != introduction_times.end(); ++it) {
290                 const std::string &name = it->first;
291                 u32 time = it->second;
292
293                 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
294                         m_lbm_defs.find(name);
295                 if (def_it == m_lbm_defs.end()) {
296                         // This seems to be an LBM entry for
297                         // an LBM we haven't loaded. Discard it.
298                         continue;
299                 }
300                 LoadingBlockModifierDef *lbm_def = def_it->second;
301                 if (lbm_def->run_at_every_load) {
302                         // This seems to be an LBM entry for
303                         // an LBM that runs at every load.
304                         // Don't add it just yet.
305                         continue;
306                 }
307
308                 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
309
310                 // Erase the entry so that we know later
311                 // what elements didn't get put into m_lbm_lookup
312                 m_lbm_defs.erase(name);
313         }
314
315         // Now also add the elements from m_lbm_defs to m_lbm_lookup
316         // that weren't added in the previous step.
317         // They are introduced first time to this world,
318         // or are run at every load (introducement time hardcoded to U32_MAX).
319
320         LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
321         LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
322
323         for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
324                         m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
325                 if (it->second->run_at_every_load) {
326                         lbms_running_always.addLBM(it->second, gamedef);
327                 } else {
328                         lbms_we_introduce_now.addLBM(it->second, gamedef);
329                 }
330         }
331
332         // Clear the list, so that we don't delete remaining elements
333         // twice in the destructor
334         m_lbm_defs.clear();
335 }
336
337 std::string LBMManager::createIntroductionTimesString()
338 {
339         // Precondition, we must be in query mode
340         FATAL_ERROR_IF(m_query_mode == false,
341                 "attempted to query on non fully set up LBMManager");
342
343         std::ostringstream oss;
344         for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
345                         it != m_lbm_lookup.end(); ++it) {
346                 u32 time = it->first;
347                 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
348                 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
349                                 iit != lbm_list.end(); ++iit) {
350                         // Don't add if the LBM runs at every load,
351                         // then introducement time is hardcoded
352                         // and doesn't need to be stored
353                         if ((*iit)->run_at_every_load)
354                                 continue;
355                         oss << (*iit)->name << "~" << time << ";";
356                 }
357         }
358         return oss.str();
359 }
360
361 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
362 {
363         // Precondition, we need m_lbm_lookup to be initialized
364         FATAL_ERROR_IF(m_query_mode == false,
365                 "attempted to query on non fully set up LBMManager");
366         v3s16 pos_of_block = block->getPosRelative();
367         v3s16 pos;
368         MapNode n;
369         content_t c;
370         lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
371         for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
372         for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
373         for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
374         {
375                 n = block->getNodeNoEx(pos);
376                 c = n.getContent();
377                 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
378                                 iit != m_lbm_lookup.end(); ++iit) {
379                         const std::vector<LoadingBlockModifierDef *> *lbm_list =
380                                 iit->second.lookup(c);
381                         if (!lbm_list)
382                                 continue;
383                         for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
384                                         lbm_list->begin(); iit != lbm_list->end(); ++iit) {
385                                 (*iit)->trigger(env, pos + pos_of_block, n);
386                         }
387                 }
388         }
389 }
390
391 /*
392         ActiveBlockList
393 */
394
395 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
396 {
397         v3s16 p;
398         for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
399         for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
400         for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
401         {
402                 // limit to a sphere
403                 if (p.getDistanceFrom(p0) <= r) {
404                         // Set in list
405                         list.insert(p);
406                 }
407         }
408 }
409
410 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
411                 s16 radius,
412                 std::set<v3s16> &blocks_removed,
413                 std::set<v3s16> &blocks_added)
414 {
415         /*
416                 Create the new list
417         */
418         std::set<v3s16> newlist = m_forceloaded_list;
419         for(std::vector<v3s16>::iterator i = active_positions.begin();
420                         i != active_positions.end(); ++i)
421         {
422                 fillRadiusBlock(*i, radius, newlist);
423         }
424
425         /*
426                 Find out which blocks on the old list are not on the new list
427         */
428         // Go through old list
429         for(std::set<v3s16>::iterator i = m_list.begin();
430                         i != m_list.end(); ++i)
431         {
432                 v3s16 p = *i;
433                 // If not on new list, it's been removed
434                 if(newlist.find(p) == newlist.end())
435                         blocks_removed.insert(p);
436         }
437
438         /*
439                 Find out which blocks on the new list are not on the old list
440         */
441         // Go through new list
442         for(std::set<v3s16>::iterator i = newlist.begin();
443                         i != newlist.end(); ++i)
444         {
445                 v3s16 p = *i;
446                 // If not on old list, it's been added
447                 if(m_list.find(p) == m_list.end())
448                         blocks_added.insert(p);
449         }
450
451         /*
452                 Update m_list
453         */
454         m_list.clear();
455         for(std::set<v3s16>::iterator i = newlist.begin();
456                         i != newlist.end(); ++i)
457         {
458                 v3s16 p = *i;
459                 m_list.insert(p);
460         }
461 }
462
463 /*
464         ServerEnvironment
465 */
466
467 ServerEnvironment::ServerEnvironment(ServerMap *map,
468                 GameScripting *scriptIface, IGameDef *gamedef,
469                 const std::string &path_world) :
470         m_map(map),
471         m_script(scriptIface),
472         m_gamedef(gamedef),
473         m_path_world(path_world),
474         m_send_recommended_timer(0),
475         m_active_block_interval_overload_skip(0),
476         m_game_time(0),
477         m_game_time_fraction_counter(0),
478         m_last_clear_objects_time(0),
479         m_recommended_send_interval(0.1),
480         m_max_lag_estimate(0.1)
481 {
482 }
483
484 ServerEnvironment::~ServerEnvironment()
485 {
486         // Clear active block list.
487         // This makes the next one delete all active objects.
488         m_active_blocks.clear();
489
490         // Convert all objects to static and delete the active objects
491         deactivateFarObjects(true);
492
493         // Drop/delete map
494         m_map->drop();
495
496         // Delete ActiveBlockModifiers
497         for (std::vector<ABMWithState>::iterator
498                         i = m_abms.begin(); i != m_abms.end(); ++i){
499                 delete i->abm;
500         }
501
502         // Deallocate players
503         for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
504                         i != m_players.end(); ++i) {
505                 delete (*i);
506         }
507 }
508
509 Map & ServerEnvironment::getMap()
510 {
511         return *m_map;
512 }
513
514 ServerMap & ServerEnvironment::getServerMap()
515 {
516         return *m_map;
517 }
518
519 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
520 {
521         for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
522                         i != m_players.end(); ++i) {
523                 RemotePlayer *player = *i;
524                 if (player->peer_id == peer_id)
525                         return player;
526         }
527         return NULL;
528 }
529
530 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
531 {
532         for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
533                         i != m_players.end(); ++i) {
534                 RemotePlayer *player = *i;
535                 if (strcmp(player->getName(), name) == 0)
536                         return player;
537         }
538         return NULL;
539 }
540
541 void ServerEnvironment::addPlayer(RemotePlayer *player)
542 {
543         DSTACK(FUNCTION_NAME);
544         /*
545                 Check that peer_ids are unique.
546                 Also check that names are unique.
547                 Exception: there can be multiple players with peer_id=0
548         */
549         // If peer id is non-zero, it has to be unique.
550         if (player->peer_id != 0)
551                 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
552         // Name has to be unique.
553         FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
554         // Add.
555         m_players.push_back(player);
556 }
557
558 void ServerEnvironment::removePlayer(RemotePlayer *player)
559 {
560         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
561                         it != m_players.end(); ++it) {
562                 if ((*it) == player) {
563                         delete *it;
564                         m_players.erase(it);
565                         return;
566                 }
567         }
568 }
569
570 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
571 {
572         float distance = pos1.getDistanceFrom(pos2);
573
574         //calculate normalized direction vector
575         v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
576                                 (pos2.Y - pos1.Y)/distance,
577                                 (pos2.Z - pos1.Z)/distance);
578
579         //find out if there's a node on path between pos1 and pos2
580         for (float i = 1; i < distance; i += stepsize) {
581                 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
582                                 normalized_vector.Y * i,
583                                 normalized_vector.Z * i) +pos1,BS);
584
585                 MapNode n = getMap().getNodeNoEx(pos);
586
587                 if(n.param0 != CONTENT_AIR) {
588                         if (p) {
589                                 *p = pos;
590                         }
591                         return false;
592                 }
593         }
594         return true;
595 }
596
597 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
598                 const std::string &str_reason, bool reconnect)
599 {
600         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
601                         it != m_players.end(); ++it) {
602                 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
603                 ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
604                                 player->protocol_version, reason, str_reason, reconnect);
605         }
606 }
607
608 void ServerEnvironment::saveLoadedPlayers()
609 {
610         std::string players_path = m_path_world + DIR_DELIM "players";
611         fs::CreateDir(players_path);
612
613         for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
614                         it != m_players.end();
615                         ++it) {
616                 if ((*it)->checkModified()) {
617                         (*it)->save(players_path, m_gamedef);
618                 }
619         }
620 }
621
622 void ServerEnvironment::savePlayer(RemotePlayer *player)
623 {
624         std::string players_path = m_path_world + DIR_DELIM "players";
625         fs::CreateDir(players_path);
626
627         player->save(players_path, m_gamedef);
628 }
629
630 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
631 {
632         bool newplayer = false;
633         bool found = false;
634         std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
635         std::string path = players_path + playername;
636
637         RemotePlayer *player = getPlayer(playername.c_str());
638         if (!player) {
639                 player = new RemotePlayer("", m_gamedef->idef());
640                 newplayer = true;
641         }
642
643         for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
644                 //// Open file and deserialize
645                 std::ifstream is(path.c_str(), std::ios_base::binary);
646                 if (!is.good())
647                         continue;
648
649                 player->deSerialize(is, path, sao);
650                 is.close();
651
652                 if (player->getName() == playername) {
653                         found = true;
654                         break;
655                 }
656
657                 path = players_path + playername + itos(i);
658         }
659
660         if (!found) {
661                 infostream << "Player file for player " << playername
662                                 << " not found" << std::endl;
663                 if (newplayer)
664                         delete player;
665
666                 return NULL;
667         }
668
669         if (newplayer) {
670                 addPlayer(player);
671         }
672         player->setModified(false);
673         return player;
674 }
675
676 void ServerEnvironment::saveMeta()
677 {
678         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
679
680         // Open file and serialize
681         std::ostringstream ss(std::ios_base::binary);
682
683         Settings args;
684         args.setU64("game_time", m_game_time);
685         args.setU64("time_of_day", getTimeOfDay());
686         args.setU64("last_clear_objects_time", m_last_clear_objects_time);
687         args.setU64("lbm_introduction_times_version", 1);
688         args.set("lbm_introduction_times",
689                 m_lbm_mgr.createIntroductionTimesString());
690         args.setU64("day_count", m_day_count);
691         args.writeLines(ss);
692         ss<<"EnvArgsEnd\n";
693
694         if(!fs::safeWriteToFile(path, ss.str()))
695         {
696                 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
697                                 <<path<<std::endl;
698                 throw SerializationError("Couldn't save env meta");
699         }
700 }
701
702 void ServerEnvironment::loadMeta()
703 {
704         std::string path = m_path_world + DIR_DELIM "env_meta.txt";
705
706         // Open file and deserialize
707         std::ifstream is(path.c_str(), std::ios_base::binary);
708         if (!is.good()) {
709                 infostream << "ServerEnvironment::loadMeta(): Failed to open "
710                                 << path << std::endl;
711                 throw SerializationError("Couldn't load env meta");
712         }
713
714         Settings args;
715
716         if (!args.parseConfigLines(is, "EnvArgsEnd")) {
717                 throw SerializationError("ServerEnvironment::loadMeta(): "
718                                 "EnvArgsEnd not found!");
719         }
720
721         try {
722                 m_game_time = args.getU64("game_time");
723         } catch (SettingNotFoundException &e) {
724                 // Getting this is crucial, otherwise timestamps are useless
725                 throw SerializationError("Couldn't load env meta game_time");
726         }
727
728         setTimeOfDay(args.exists("time_of_day") ?
729                 // set day to morning by default
730                 args.getU64("time_of_day") : 9000);
731
732         m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
733                 // If missing, do as if clearObjects was never called
734                 args.getU64("last_clear_objects_time") : 0;
735
736         std::string lbm_introduction_times = "";
737         try {
738                 u64 ver = args.getU64("lbm_introduction_times_version");
739                 if (ver == 1) {
740                         lbm_introduction_times = args.get("lbm_introduction_times");
741                 } else {
742                         infostream << "ServerEnvironment::loadMeta(): Non-supported"
743                                 << " introduction time version " << ver << std::endl;
744                 }
745         } catch (SettingNotFoundException &e) {
746                 // No problem, this is expected. Just continue with an empty string
747         }
748         m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
749
750         m_day_count = args.exists("day_count") ?
751                 args.getU64("day_count") : 0;
752 }
753
754 void ServerEnvironment::loadDefaultMeta()
755 {
756         m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
757 }
758
759 struct ActiveABM
760 {
761         ActiveBlockModifier *abm;
762         int chance;
763         std::set<content_t> required_neighbors;
764 };
765
766 class ABMHandler
767 {
768 private:
769         ServerEnvironment *m_env;
770         std::vector<std::vector<ActiveABM> *> m_aabms;
771 public:
772         ABMHandler(std::vector<ABMWithState> &abms,
773                         float dtime_s, ServerEnvironment *env,
774                         bool use_timers):
775                 m_env(env)
776         {
777                 if(dtime_s < 0.001)
778                         return;
779                 INodeDefManager *ndef = env->getGameDef()->ndef();
780                 for(std::vector<ABMWithState>::iterator
781                                 i = abms.begin(); i != abms.end(); ++i) {
782                         ActiveBlockModifier *abm = i->abm;
783                         float trigger_interval = abm->getTriggerInterval();
784                         if(trigger_interval < 0.001)
785                                 trigger_interval = 0.001;
786                         float actual_interval = dtime_s;
787                         if(use_timers){
788                                 i->timer += dtime_s;
789                                 if(i->timer < trigger_interval)
790                                         continue;
791                                 i->timer -= trigger_interval;
792                                 actual_interval = trigger_interval;
793                         }
794                         float chance = abm->getTriggerChance();
795                         if(chance == 0)
796                                 chance = 1;
797                         ActiveABM aabm;
798                         aabm.abm = abm;
799                         if(abm->getSimpleCatchUp()) {
800                                 float intervals = actual_interval / trigger_interval;
801                                 if(intervals == 0)
802                                         continue;
803                                 aabm.chance = chance / intervals;
804                                 if(aabm.chance == 0)
805                                         aabm.chance = 1;
806                         } else {
807                                 aabm.chance = chance;
808                         }
809                         // Trigger neighbors
810                         std::set<std::string> required_neighbors_s
811                                         = abm->getRequiredNeighbors();
812                         for(std::set<std::string>::iterator
813                                         i = required_neighbors_s.begin();
814                                         i != required_neighbors_s.end(); ++i)
815                         {
816                                 ndef->getIds(*i, aabm.required_neighbors);
817                         }
818                         // Trigger contents
819                         std::set<std::string> contents_s = abm->getTriggerContents();
820                         for(std::set<std::string>::iterator
821                                         i = contents_s.begin(); i != contents_s.end(); ++i)
822                         {
823                                 std::set<content_t> ids;
824                                 ndef->getIds(*i, ids);
825                                 for(std::set<content_t>::const_iterator k = ids.begin();
826                                                 k != ids.end(); ++k)
827                                 {
828                                         content_t c = *k;
829                                         if (c >= m_aabms.size())
830                                                 m_aabms.resize(c + 256, NULL);
831                                         if (!m_aabms[c])
832                                                 m_aabms[c] = new std::vector<ActiveABM>;
833                                         m_aabms[c]->push_back(aabm);
834                                 }
835                         }
836                 }
837         }
838
839         ~ABMHandler()
840         {
841                 for (size_t i = 0; i < m_aabms.size(); i++)
842                         delete m_aabms[i];
843         }
844
845         // Find out how many objects the given block and its neighbours contain.
846         // Returns the number of objects in the block, and also in 'wider' the
847         // number of objects in the block and all its neighbours. The latter
848         // may an estimate if any neighbours are unloaded.
849         u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
850         {
851                 wider = 0;
852                 u32 wider_unknown_count = 0;
853                 for(s16 x=-1; x<=1; x++)
854                 for(s16 y=-1; y<=1; y++)
855                 for(s16 z=-1; z<=1; z++)
856                 {
857                         MapBlock *block2 = map->getBlockNoCreateNoEx(
858                                         block->getPos() + v3s16(x,y,z));
859                         if(block2==NULL){
860                                 wider_unknown_count++;
861                                 continue;
862                         }
863                         wider += block2->m_static_objects.m_active.size()
864                                         + block2->m_static_objects.m_stored.size();
865                 }
866                 // Extrapolate
867                 u32 active_object_count = block->m_static_objects.m_active.size();
868                 u32 wider_known_count = 3*3*3 - wider_unknown_count;
869                 wider += wider_unknown_count * wider / wider_known_count;
870                 return active_object_count;
871
872         }
873         void apply(MapBlock *block)
874         {
875                 if(m_aabms.empty() || block->isDummy())
876                         return;
877
878                 ServerMap *map = &m_env->getServerMap();
879
880                 u32 active_object_count_wider;
881                 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
882                 m_env->m_added_objects = 0;
883
884                 v3s16 p0;
885                 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
886                 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
887                 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
888                 {
889                         const MapNode &n = block->getNodeUnsafe(p0);
890                         content_t c = n.getContent();
891
892                         if (c >= m_aabms.size() || !m_aabms[c])
893                                 continue;
894
895                         v3s16 p = p0 + block->getPosRelative();
896                         for(std::vector<ActiveABM>::iterator
897                                         i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
898                                 if(myrand() % i->chance != 0)
899                                         continue;
900
901                                 // Check neighbors
902                                 if(!i->required_neighbors.empty())
903                                 {
904                                         v3s16 p1;
905                                         for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
906                                         for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
907                                         for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
908                                         {
909                                                 if(p1 == p)
910                                                         continue;
911                                                 MapNode n = map->getNodeNoEx(p1);
912                                                 content_t c = n.getContent();
913                                                 std::set<content_t>::const_iterator k;
914                                                 k = i->required_neighbors.find(c);
915                                                 if(k != i->required_neighbors.end()){
916                                                         goto neighbor_found;
917                                                 }
918                                         }
919                                         // No required neighbor found
920                                         continue;
921                                 }
922 neighbor_found:
923
924                                 // Call all the trigger variations
925                                 i->abm->trigger(m_env, p, n);
926                                 i->abm->trigger(m_env, p, n,
927                                                 active_object_count, active_object_count_wider);
928
929                                 // Count surrounding objects again if the abms added any
930                                 if(m_env->m_added_objects > 0) {
931                                         active_object_count = countObjects(block, map, active_object_count_wider);
932                                         m_env->m_added_objects = 0;
933                                 }
934                         }
935                 }
936         }
937 };
938
939 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
940 {
941         // Reset usage timer immediately, otherwise a block that becomes active
942         // again at around the same time as it would normally be unloaded will
943         // get unloaded incorrectly. (I think this still leaves a small possibility
944         // of a race condition between this and server::AsyncRunStep, which only
945         // some kind of synchronisation will fix, but it at least reduces the window
946         // of opportunity for it to break from seconds to nanoseconds)
947         block->resetUsageTimer();
948
949         // Get time difference
950         u32 dtime_s = 0;
951         u32 stamp = block->getTimestamp();
952         if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
953                 dtime_s = m_game_time - stamp;
954         dtime_s += additional_dtime;
955
956         /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
957                         <<stamp<<", game time: "<<m_game_time<<std::endl;*/
958
959         // Remove stored static objects if clearObjects was called since block's timestamp
960         if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
961                 block->m_static_objects.m_stored.clear();
962                 // do not set changed flag to avoid unnecessary mapblock writes
963         }
964
965         // Set current time as timestamp
966         block->setTimestampNoChangedFlag(m_game_time);
967
968         /*infostream<<"ServerEnvironment::activateBlock(): block is "
969                         <<dtime_s<<" seconds old."<<std::endl;*/
970
971         // Activate stored objects
972         activateObjects(block, dtime_s);
973
974         /* Handle LoadingBlockModifiers */
975         m_lbm_mgr.applyLBMs(this, block, stamp);
976
977         // Run node timers
978         std::vector<NodeTimer> elapsed_timers =
979                 block->m_node_timers.step((float)dtime_s);
980         if (!elapsed_timers.empty()) {
981                 MapNode n;
982                 for (std::vector<NodeTimer>::iterator
983                                 i = elapsed_timers.begin();
984                                 i != elapsed_timers.end(); ++i){
985                         n = block->getNodeNoEx(i->position);
986                         v3s16 p = i->position + block->getPosRelative();
987                         if (m_script->node_on_timer(p, n, i->elapsed))
988                                 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
989                 }
990         }
991
992         /* Handle ActiveBlockModifiers */
993         ABMHandler abmhandler(m_abms, dtime_s, this, false);
994         abmhandler.apply(block);
995 }
996
997 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
998 {
999         m_abms.push_back(ABMWithState(abm));
1000 }
1001
1002 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1003 {
1004         m_lbm_mgr.addLBMDef(lbm);
1005 }
1006
1007 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1008 {
1009         INodeDefManager *ndef = m_gamedef->ndef();
1010         MapNode n_old = m_map->getNodeNoEx(p);
1011
1012         // Call destructor
1013         if (ndef->get(n_old).has_on_destruct)
1014                 m_script->node_on_destruct(p, n_old);
1015
1016         // Replace node
1017         if (!m_map->addNodeWithEvent(p, n))
1018                 return false;
1019
1020         // Update active VoxelManipulator if a mapgen thread
1021         m_map->updateVManip(p);
1022
1023         // Call post-destructor
1024         if (ndef->get(n_old).has_after_destruct)
1025                 m_script->node_after_destruct(p, n_old);
1026
1027         // Call constructor
1028         if (ndef->get(n).has_on_construct)
1029                 m_script->node_on_construct(p, n);
1030
1031         return true;
1032 }
1033
1034 bool ServerEnvironment::removeNode(v3s16 p)
1035 {
1036         INodeDefManager *ndef = m_gamedef->ndef();
1037         MapNode n_old = m_map->getNodeNoEx(p);
1038
1039         // Call destructor
1040         if (ndef->get(n_old).has_on_destruct)
1041                 m_script->node_on_destruct(p, n_old);
1042
1043         // Replace with air
1044         // This is slightly optimized compared to addNodeWithEvent(air)
1045         if (!m_map->removeNodeWithEvent(p))
1046                 return false;
1047
1048         // Update active VoxelManipulator if a mapgen thread
1049         m_map->updateVManip(p);
1050
1051         // Call post-destructor
1052         if (ndef->get(n_old).has_after_destruct)
1053                 m_script->node_after_destruct(p, n_old);
1054
1055         // Air doesn't require constructor
1056         return true;
1057 }
1058
1059 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1060 {
1061         if (!m_map->addNodeWithEvent(p, n, false))
1062                 return false;
1063
1064         // Update active VoxelManipulator if a mapgen thread
1065         m_map->updateVManip(p);
1066
1067         return true;
1068 }
1069
1070 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1071 {
1072         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1073                         i != m_active_objects.end(); ++i) {
1074                 ServerActiveObject* obj = i->second;
1075                 u16 id = i->first;
1076                 v3f objectpos = obj->getBasePosition();
1077                 if (objectpos.getDistanceFrom(pos) > radius)
1078                         continue;
1079                 objects.push_back(id);
1080         }
1081 }
1082
1083 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1084 {
1085         infostream << "ServerEnvironment::clearObjects(): "
1086                 << "Removing all active objects" << std::endl;
1087         std::vector<u16> objects_to_remove;
1088         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1089                         i != m_active_objects.end(); ++i) {
1090                 ServerActiveObject* obj = i->second;
1091                 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1092                         continue;
1093                 u16 id = i->first;
1094                 // Delete static object if block is loaded
1095                 if (obj->m_static_exists) {
1096                         MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1097                         if (block) {
1098                                 block->m_static_objects.remove(id);
1099                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1100                                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1101                                 obj->m_static_exists = false;
1102                         }
1103                 }
1104                 // If known by some client, don't delete immediately
1105                 if (obj->m_known_by_count > 0) {
1106                         obj->m_pending_deactivation = true;
1107                         obj->m_removed = true;
1108                         continue;
1109                 }
1110
1111                 // Tell the object about removal
1112                 obj->removingFromEnvironment();
1113                 // Deregister in scripting api
1114                 m_script->removeObjectReference(obj);
1115
1116                 // Delete active object
1117                 if (obj->environmentDeletes())
1118                         delete obj;
1119                 // Id to be removed from m_active_objects
1120                 objects_to_remove.push_back(id);
1121         }
1122
1123         // Remove references from m_active_objects
1124         for (std::vector<u16>::iterator i = objects_to_remove.begin();
1125                         i != objects_to_remove.end(); ++i) {
1126                 m_active_objects.erase(*i);
1127         }
1128
1129         // Get list of loaded blocks
1130         std::vector<v3s16> loaded_blocks;
1131         infostream << "ServerEnvironment::clearObjects(): "
1132                 << "Listing all loaded blocks" << std::endl;
1133         m_map->listAllLoadedBlocks(loaded_blocks);
1134         infostream << "ServerEnvironment::clearObjects(): "
1135                 << "Done listing all loaded blocks: "
1136                 << loaded_blocks.size()<<std::endl;
1137
1138         // Get list of loadable blocks
1139         std::vector<v3s16> loadable_blocks;
1140         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1141                 infostream << "ServerEnvironment::clearObjects(): "
1142                         << "Listing all loadable blocks" << std::endl;
1143                 m_map->listAllLoadableBlocks(loadable_blocks);
1144                 infostream << "ServerEnvironment::clearObjects(): "
1145                         << "Done listing all loadable blocks: "
1146                         << loadable_blocks.size() << std::endl;
1147         } else {
1148                 loadable_blocks = loaded_blocks;
1149         }
1150
1151         infostream << "ServerEnvironment::clearObjects(): "
1152                 << "Now clearing objects in " << loadable_blocks.size()
1153                 << " blocks" << std::endl;
1154
1155         // Grab a reference on each loaded block to avoid unloading it
1156         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1157                         i != loaded_blocks.end(); ++i) {
1158                 v3s16 p = *i;
1159                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1160                 assert(block != NULL);
1161                 block->refGrab();
1162         }
1163
1164         // Remove objects in all loadable blocks
1165         u32 unload_interval = U32_MAX;
1166         if (mode == CLEAR_OBJECTS_MODE_FULL) {
1167                 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1168                 unload_interval = MYMAX(unload_interval, 1);
1169         }
1170         u32 report_interval = loadable_blocks.size() / 10;
1171         u32 num_blocks_checked = 0;
1172         u32 num_blocks_cleared = 0;
1173         u32 num_objs_cleared = 0;
1174         for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1175                         i != loadable_blocks.end(); ++i) {
1176                 v3s16 p = *i;
1177                 MapBlock *block = m_map->emergeBlock(p, false);
1178                 if (!block) {
1179                         errorstream << "ServerEnvironment::clearObjects(): "
1180                                 << "Failed to emerge block " << PP(p) << std::endl;
1181                         continue;
1182                 }
1183                 u32 num_stored = block->m_static_objects.m_stored.size();
1184                 u32 num_active = block->m_static_objects.m_active.size();
1185                 if (num_stored != 0 || num_active != 0) {
1186                         block->m_static_objects.m_stored.clear();
1187                         block->m_static_objects.m_active.clear();
1188                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1189                                 MOD_REASON_CLEAR_ALL_OBJECTS);
1190                         num_objs_cleared += num_stored + num_active;
1191                         num_blocks_cleared++;
1192                 }
1193                 num_blocks_checked++;
1194
1195                 if (report_interval != 0 &&
1196                                 num_blocks_checked % report_interval == 0) {
1197                         float percent = 100.0 * (float)num_blocks_checked /
1198                                 loadable_blocks.size();
1199                         infostream << "ServerEnvironment::clearObjects(): "
1200                                 << "Cleared " << num_objs_cleared << " objects"
1201                                 << " in " << num_blocks_cleared << " blocks ("
1202                                 << percent << "%)" << std::endl;
1203                 }
1204                 if (num_blocks_checked % unload_interval == 0) {
1205                         m_map->unloadUnreferencedBlocks();
1206                 }
1207         }
1208         m_map->unloadUnreferencedBlocks();
1209
1210         // Drop references that were added above
1211         for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1212                         i != loaded_blocks.end(); ++i) {
1213                 v3s16 p = *i;
1214                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1215                 assert(block);
1216                 block->refDrop();
1217         }
1218
1219         m_last_clear_objects_time = m_game_time;
1220
1221         infostream << "ServerEnvironment::clearObjects(): "
1222                 << "Finished: Cleared " << num_objs_cleared << " objects"
1223                 << " in " << num_blocks_cleared << " blocks" << std::endl;
1224 }
1225
1226 void ServerEnvironment::step(float dtime)
1227 {
1228         DSTACK(FUNCTION_NAME);
1229
1230         //TimeTaker timer("ServerEnv step");
1231
1232         /* Step time of day */
1233         stepTimeOfDay(dtime);
1234
1235         // Update this one
1236         // NOTE: This is kind of funny on a singleplayer game, but doesn't
1237         // really matter that much.
1238         static const float server_step = g_settings->getFloat("dedicated_server_step");
1239         m_recommended_send_interval = server_step;
1240
1241         /*
1242                 Increment game time
1243         */
1244         {
1245                 m_game_time_fraction_counter += dtime;
1246                 u32 inc_i = (u32)m_game_time_fraction_counter;
1247                 m_game_time += inc_i;
1248                 m_game_time_fraction_counter -= (float)inc_i;
1249         }
1250
1251         /*
1252                 Handle players
1253         */
1254         {
1255                 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1256                 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1257                                 i != m_players.end(); ++i) {
1258                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1259                         assert(player);
1260
1261                         // Ignore disconnected players
1262                         if(player->peer_id == 0)
1263                                 continue;
1264
1265                         // Move
1266                         player->move(dtime, this, 100*BS);
1267                 }
1268         }
1269
1270         /*
1271                 Manage active block list
1272         */
1273         if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1274                 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1275                 /*
1276                         Get player block positions
1277                 */
1278                 std::vector<v3s16> players_blockpos;
1279                 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1280                                 i != m_players.end(); ++i) {
1281                         RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1282                         assert(player);
1283
1284                         // Ignore disconnected players
1285                         if (player->peer_id == 0)
1286                                 continue;
1287
1288                         PlayerSAO *playersao = player->getPlayerSAO();
1289                         assert(playersao);
1290
1291                         v3s16 blockpos = getNodeBlockPos(
1292                                         floatToInt(playersao->getBasePosition(), BS));
1293                         players_blockpos.push_back(blockpos);
1294                 }
1295
1296                 /*
1297                         Update list of active blocks, collecting changes
1298                 */
1299                 static const s16 active_block_range = g_settings->getS16("active_block_range");
1300                 std::set<v3s16> blocks_removed;
1301                 std::set<v3s16> blocks_added;
1302                 m_active_blocks.update(players_blockpos, active_block_range,
1303                                 blocks_removed, blocks_added);
1304
1305                 /*
1306                         Handle removed blocks
1307                 */
1308
1309                 // Convert active objects that are no more in active blocks to static
1310                 deactivateFarObjects(false);
1311
1312                 for(std::set<v3s16>::iterator
1313                                 i = blocks_removed.begin();
1314                                 i != blocks_removed.end(); ++i) {
1315                         v3s16 p = *i;
1316
1317                         /* infostream<<"Server: Block " << PP(p)
1318                                 << " became inactive"<<std::endl; */
1319
1320                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1321                         if(block==NULL)
1322                                 continue;
1323
1324                         // Set current time as timestamp (and let it set ChangedFlag)
1325                         block->setTimestamp(m_game_time);
1326                 }
1327
1328                 /*
1329                         Handle added blocks
1330                 */
1331
1332                 for(std::set<v3s16>::iterator
1333                                 i = blocks_added.begin();
1334                                 i != blocks_added.end(); ++i)
1335                 {
1336                         v3s16 p = *i;
1337
1338                         MapBlock *block = m_map->getBlockOrEmerge(p);
1339                         if(block==NULL){
1340                                 m_active_blocks.m_list.erase(p);
1341                                 continue;
1342                         }
1343
1344                         activateBlock(block);
1345                         /* infostream<<"Server: Block " << PP(p)
1346                                 << " became active"<<std::endl; */
1347                 }
1348         }
1349
1350         /*
1351                 Mess around in active blocks
1352         */
1353         if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1354                 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1355
1356                 float dtime = m_cache_nodetimer_interval;
1357
1358                 for(std::set<v3s16>::iterator
1359                                 i = m_active_blocks.m_list.begin();
1360                                 i != m_active_blocks.m_list.end(); ++i)
1361                 {
1362                         v3s16 p = *i;
1363
1364                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1365                                         <<") being handled"<<std::endl;*/
1366
1367                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1368                         if(block==NULL)
1369                                 continue;
1370
1371                         // Reset block usage timer
1372                         block->resetUsageTimer();
1373
1374                         // Set current time as timestamp
1375                         block->setTimestampNoChangedFlag(m_game_time);
1376                         // If time has changed much from the one on disk,
1377                         // set block to be saved when it is unloaded
1378                         if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1379                                 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1380                                         MOD_REASON_BLOCK_EXPIRED);
1381
1382                         // Run node timers
1383                         std::vector<NodeTimer> elapsed_timers =
1384                                 block->m_node_timers.step((float)dtime);
1385                         if (!elapsed_timers.empty()) {
1386                                 MapNode n;
1387                                 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1388                                                 i != elapsed_timers.end(); ++i) {
1389                                         n = block->getNodeNoEx(i->position);
1390                                         p = i->position + block->getPosRelative();
1391                                         if (m_script->node_on_timer(p, n, i->elapsed)) {
1392                                                 block->setNodeTimer(NodeTimer(
1393                                                         i->timeout, 0, i->position));
1394                                         }
1395                                 }
1396                         }
1397                 }
1398         }
1399
1400         if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1401         do{ // breakable
1402                 if(m_active_block_interval_overload_skip > 0){
1403                         ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1404                         m_active_block_interval_overload_skip--;
1405                         break;
1406                 }
1407                 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1408                 TimeTaker timer("modify in active blocks per interval");
1409
1410                 // Initialize handling of ActiveBlockModifiers
1411                 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1412
1413                 for(std::set<v3s16>::iterator
1414                                 i = m_active_blocks.m_list.begin();
1415                                 i != m_active_blocks.m_list.end(); ++i)
1416                 {
1417                         v3s16 p = *i;
1418
1419                         /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1420                                         <<") being handled"<<std::endl;*/
1421
1422                         MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1423                         if(block == NULL)
1424                                 continue;
1425
1426                         // Set current time as timestamp
1427                         block->setTimestampNoChangedFlag(m_game_time);
1428
1429                         /* Handle ActiveBlockModifiers */
1430                         abmhandler.apply(block);
1431                 }
1432
1433                 u32 time_ms = timer.stop(true);
1434                 u32 max_time_ms = 200;
1435                 if(time_ms > max_time_ms){
1436                         warningstream<<"active block modifiers took "
1437                                         <<time_ms<<"ms (longer than "
1438                                         <<max_time_ms<<"ms)"<<std::endl;
1439                         m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1440                 }
1441         }while(0);
1442
1443         /*
1444                 Step script environment (run global on_step())
1445         */
1446         m_script->environment_Step(dtime);
1447
1448         /*
1449                 Step active objects
1450         */
1451         {
1452                 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1453                 //TimeTaker timer("Step active objects");
1454
1455                 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1456
1457                 // This helps the objects to send data at the same time
1458                 bool send_recommended = false;
1459                 m_send_recommended_timer += dtime;
1460                 if(m_send_recommended_timer > getSendRecommendedInterval())
1461                 {
1462                         m_send_recommended_timer -= getSendRecommendedInterval();
1463                         send_recommended = true;
1464                 }
1465
1466                 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1467                                 i != m_active_objects.end(); ++i) {
1468                         ServerActiveObject* obj = i->second;
1469                         // Don't step if is to be removed or stored statically
1470                         if(obj->m_removed || obj->m_pending_deactivation)
1471                                 continue;
1472                         // Step object
1473                         obj->step(dtime, send_recommended);
1474                         // Read messages from object
1475                         while(!obj->m_messages_out.empty())
1476                         {
1477                                 m_active_object_messages.push(
1478                                                 obj->m_messages_out.front());
1479                                 obj->m_messages_out.pop();
1480                         }
1481                 }
1482         }
1483
1484         /*
1485                 Manage active objects
1486         */
1487         if(m_object_management_interval.step(dtime, 0.5))
1488         {
1489                 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1490                 /*
1491                         Remove objects that satisfy (m_removed && m_known_by_count==0)
1492                 */
1493                 removeRemovedObjects();
1494         }
1495
1496         /*
1497                 Manage particle spawner expiration
1498         */
1499         if (m_particle_management_interval.step(dtime, 1.0)) {
1500                 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1501                                 i != m_particle_spawners.end(); ) {
1502                         //non expiring spawners
1503                         if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1504                                 ++i;
1505                                 continue;
1506                         }
1507
1508                         i->second -= 1.0f;
1509                         if (i->second <= 0.f)
1510                                 m_particle_spawners.erase(i++);
1511                         else
1512                                 ++i;
1513                 }
1514         }
1515 }
1516
1517 u32 ServerEnvironment::addParticleSpawner(float exptime)
1518 {
1519         // Timers with lifetime 0 do not expire
1520         float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1521
1522         u32 id = 0;
1523         for (;;) { // look for unused particlespawner id
1524                 id++;
1525                 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1526                 if (f == m_particle_spawners.end()) {
1527                         m_particle_spawners[id] = time;
1528                         break;
1529                 }
1530         }
1531         return id;
1532 }
1533
1534 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1535 {
1536         u32 id = addParticleSpawner(exptime);
1537         m_particle_spawner_attachments[id] = attached_id;
1538         if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1539                 obj->attachParticleSpawner(id);
1540         }
1541         return id;
1542 }
1543
1544 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1545 {
1546         m_particle_spawners.erase(id);
1547         UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1548         if (it != m_particle_spawner_attachments.end()) {
1549                 u16 obj_id = (*it).second;
1550                 ServerActiveObject *sao = getActiveObject(obj_id);
1551                 if (sao != NULL && remove_from_object) {
1552                         sao->detachParticleSpawner(id);
1553                 }
1554                 m_particle_spawner_attachments.erase(id);
1555         }
1556 }
1557
1558 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1559 {
1560         ActiveObjectMap::iterator n = m_active_objects.find(id);
1561         return (n != m_active_objects.end() ? n->second : NULL);
1562 }
1563
1564 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1565 {
1566         if (id == 0)
1567                 return false;
1568
1569         return objects.find(id) == objects.end();
1570 }
1571
1572 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1573 {
1574         //try to reuse id's as late as possible
1575         static u16 last_used_id = 0;
1576         u16 startid = last_used_id;
1577         for(;;)
1578         {
1579                 last_used_id ++;
1580                 if(isFreeServerActiveObjectId(last_used_id, objects))
1581                         return last_used_id;
1582
1583                 if(last_used_id == startid)
1584                         return 0;
1585         }
1586 }
1587
1588 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1589 {
1590         assert(object); // Pre-condition
1591         m_added_objects++;
1592         u16 id = addActiveObjectRaw(object, true, 0);
1593         return id;
1594 }
1595
1596 /*
1597         Finds out what new objects have been added to
1598         inside a radius around a position
1599 */
1600 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1601                 s16 player_radius,
1602                 std::set<u16> &current_objects,
1603                 std::queue<u16> &added_objects)
1604 {
1605         f32 radius_f = radius * BS;
1606         f32 player_radius_f = player_radius * BS;
1607
1608         if (player_radius_f < 0)
1609                 player_radius_f = 0;
1610         /*
1611                 Go through the object list,
1612                 - discard m_removed objects,
1613                 - discard objects that are too far away,
1614                 - discard objects that are found in current_objects.
1615                 - add remaining objects to added_objects
1616         */
1617         for (ActiveObjectMap::iterator i = m_active_objects.begin();
1618                         i != m_active_objects.end(); ++i) {
1619                 u16 id = i->first;
1620
1621                 // Get object
1622                 ServerActiveObject *object = i->second;
1623                 if (object == NULL)
1624                         continue;
1625
1626                 // Discard if removed or deactivating
1627                 if(object->m_removed || object->m_pending_deactivation)
1628                         continue;
1629
1630                 f32 distance_f = object->getBasePosition().
1631                                 getDistanceFrom(playersao->getBasePosition());
1632                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1633                         // Discard if too far
1634                         if (distance_f > player_radius_f && player_radius_f != 0)
1635                                 continue;
1636                 } else if (distance_f > radius_f)
1637                         continue;
1638
1639                 // Discard if already on current_objects
1640                 std::set<u16>::iterator n;
1641                 n = current_objects.find(id);
1642                 if(n != current_objects.end())
1643                         continue;
1644                 // Add to added_objects
1645                 added_objects.push(id);
1646         }
1647 }
1648
1649 /*
1650         Finds out what objects have been removed from
1651         inside a radius around a position
1652 */
1653 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1654                 s16 player_radius,
1655                 std::set<u16> &current_objects,
1656                 std::queue<u16> &removed_objects)
1657 {
1658         f32 radius_f = radius * BS;
1659         f32 player_radius_f = player_radius * BS;
1660
1661         if (player_radius_f < 0)
1662                 player_radius_f = 0;
1663         /*
1664                 Go through current_objects; object is removed if:
1665                 - object is not found in m_active_objects (this is actually an
1666                   error condition; objects should be set m_removed=true and removed
1667                   only after all clients have been informed about removal), or
1668                 - object has m_removed=true, or
1669                 - object is too far away
1670         */
1671         for(std::set<u16>::iterator
1672                         i = current_objects.begin();
1673                         i != current_objects.end(); ++i)
1674         {
1675                 u16 id = *i;
1676                 ServerActiveObject *object = getActiveObject(id);
1677
1678                 if (object == NULL) {
1679                         infostream << "ServerEnvironment::getRemovedActiveObjects():"
1680                                 << " object in current_objects is NULL" << std::endl;
1681                         removed_objects.push(id);
1682                         continue;
1683                 }
1684
1685                 if (object->m_removed || object->m_pending_deactivation) {
1686                         removed_objects.push(id);
1687                         continue;
1688                 }
1689
1690                 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1691                 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1692                         if (distance_f <= player_radius_f || player_radius_f == 0)
1693                                 continue;
1694                 } else if (distance_f <= radius_f)
1695                         continue;
1696
1697                 // Object is no longer visible
1698                 removed_objects.push(id);
1699         }
1700 }
1701
1702 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1703         v3s16 blockpos, bool static_exists, v3s16 static_block)
1704 {
1705         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1706         if (!block)
1707                 return;
1708
1709         for (std::map<u16, StaticObject>::iterator
1710                         so_it = block->m_static_objects.m_active.begin();
1711                         so_it != block->m_static_objects.m_active.end(); ++so_it) {
1712                 // Get the ServerActiveObject counterpart to this StaticObject
1713                 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1714                 if (ao_it == m_active_objects.end()) {
1715                         // If this ever happens, there must be some kind of nasty bug.
1716                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1717                                 "Object from MapBlock::m_static_objects::m_active not found "
1718                                 "in m_active_objects";
1719                         continue;
1720                 }
1721
1722                 ServerActiveObject *sao = ao_it->second;
1723                 sao->m_static_exists = static_exists;
1724                 sao->m_static_block  = static_block;
1725         }
1726 }
1727
1728 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1729 {
1730         if(m_active_object_messages.empty())
1731                 return ActiveObjectMessage(0);
1732
1733         ActiveObjectMessage message = m_active_object_messages.front();
1734         m_active_object_messages.pop();
1735         return message;
1736 }
1737
1738 /*
1739         ************ Private methods *************
1740 */
1741
1742 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1743                 bool set_changed, u32 dtime_s)
1744 {
1745         assert(object); // Pre-condition
1746         if(object->getId() == 0){
1747                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1748                 if(new_id == 0)
1749                 {
1750                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1751                                         <<"no free ids available"<<std::endl;
1752                         if(object->environmentDeletes())
1753                                 delete object;
1754                         return 0;
1755                 }
1756                 object->setId(new_id);
1757         }
1758         else{
1759                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1760                                 <<"supplied with id "<<object->getId()<<std::endl;
1761         }
1762
1763         if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1764                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1765                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1766                 if(object->environmentDeletes())
1767                         delete object;
1768                 return 0;
1769         }
1770
1771         if (objectpos_over_limit(object->getBasePosition())) {
1772                 v3f p = object->getBasePosition();
1773                 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1774                         << "object position (" << p.X << "," << p.Y << "," << p.Z
1775                         << ") outside maximum range" << std::endl;
1776                 if (object->environmentDeletes())
1777                         delete object;
1778                 return 0;
1779         }
1780
1781         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1782                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1783
1784         m_active_objects[object->getId()] = object;
1785
1786         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1787                         <<"Added id="<<object->getId()<<"; there are now "
1788                         <<m_active_objects.size()<<" active objects."
1789                         <<std::endl;
1790
1791         // Register reference in scripting api (must be done before post-init)
1792         m_script->addObjectReference(object);
1793         // Post-initialize object
1794         object->addedToEnvironment(dtime_s);
1795
1796         // Add static data to block
1797         if(object->isStaticAllowed())
1798         {
1799                 // Add static object to active static list of the block
1800                 v3f objectpos = object->getBasePosition();
1801                 std::string staticdata = object->getStaticData();
1802                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1803                 // Add to the block where the object is located in
1804                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1805                 MapBlock *block = m_map->emergeBlock(blockpos);
1806                 if(block){
1807                         block->m_static_objects.m_active[object->getId()] = s_obj;
1808                         object->m_static_exists = true;
1809                         object->m_static_block = blockpos;
1810
1811                         if(set_changed)
1812                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1813                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1814                 } else {
1815                         v3s16 p = floatToInt(objectpos, BS);
1816                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1817                                         <<"could not emerge block for storing id="<<object->getId()
1818                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1819                 }
1820         }
1821
1822         return object->getId();
1823 }
1824
1825 /*
1826         Remove objects that satisfy (m_removed && m_known_by_count==0)
1827 */
1828 void ServerEnvironment::removeRemovedObjects()
1829 {
1830         std::vector<u16> objects_to_remove;
1831         for(ActiveObjectMap::iterator i = m_active_objects.begin();
1832                         i != m_active_objects.end(); ++i) {
1833                 u16 id = i->first;
1834                 ServerActiveObject* obj = i->second;
1835                 // This shouldn't happen but check it
1836                 if(obj == NULL)
1837                 {
1838                         infostream<<"NULL object found in ServerEnvironment"
1839                                         <<" while finding removed objects. id="<<id<<std::endl;
1840                         // Id to be removed from m_active_objects
1841                         objects_to_remove.push_back(id);
1842                         continue;
1843                 }
1844
1845                 /*
1846                         We will delete objects that are marked as removed or thatare
1847                         waiting for deletion after deactivation
1848                 */
1849                 if (!obj->m_removed && !obj->m_pending_deactivation)
1850                         continue;
1851
1852                 /*
1853                         Delete static data from block if is marked as removed
1854                 */
1855                 if(obj->m_static_exists && obj->m_removed)
1856                 {
1857                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1858                         if (block) {
1859                                 block->m_static_objects.remove(id);
1860                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1862                                 obj->m_static_exists = false;
1863                         } else {
1864                                 infostream<<"Failed to emerge block from which an object to "
1865                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1866                         }
1867                 }
1868
1869                 // If m_known_by_count > 0, don't actually remove. On some future
1870                 // invocation this will be 0, which is when removal will continue.
1871                 if(obj->m_known_by_count > 0)
1872                         continue;
1873
1874                 /*
1875                         Move static data from active to stored if not marked as removed
1876                 */
1877                 if(obj->m_static_exists && !obj->m_removed){
1878                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1879                         if (block) {
1880                                 std::map<u16, StaticObject>::iterator i =
1881                                                 block->m_static_objects.m_active.find(id);
1882                                 if(i != block->m_static_objects.m_active.end()){
1883                                         block->m_static_objects.m_stored.push_back(i->second);
1884                                         block->m_static_objects.m_active.erase(id);
1885                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1886                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1887                                 }
1888                         } else {
1889                                 infostream<<"Failed to emerge block from which an object to "
1890                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1891                         }
1892                 }
1893
1894                 // Tell the object about removal
1895                 obj->removingFromEnvironment();
1896                 // Deregister in scripting api
1897                 m_script->removeObjectReference(obj);
1898
1899                 // Delete
1900                 if(obj->environmentDeletes())
1901                         delete obj;
1902
1903                 // Id to be removed from m_active_objects
1904                 objects_to_remove.push_back(id);
1905         }
1906         // Remove references from m_active_objects
1907         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1908                         i != objects_to_remove.end(); ++i) {
1909                 m_active_objects.erase(*i);
1910         }
1911 }
1912
1913 static void print_hexdump(std::ostream &o, const std::string &data)
1914 {
1915         const int linelength = 16;
1916         for(int l=0; ; l++){
1917                 int i0 = linelength * l;
1918                 bool at_end = false;
1919                 int thislinelength = linelength;
1920                 if(i0 + thislinelength > (int)data.size()){
1921                         thislinelength = data.size() - i0;
1922                         at_end = true;
1923                 }
1924                 for(int di=0; di<linelength; di++){
1925                         int i = i0 + di;
1926                         char buf[4];
1927                         if(di<thislinelength)
1928                                 snprintf(buf, 4, "%.2x ", data[i]);
1929                         else
1930                                 snprintf(buf, 4, "   ");
1931                         o<<buf;
1932                 }
1933                 o<<" ";
1934                 for(int di=0; di<thislinelength; di++){
1935                         int i = i0 + di;
1936                         if(data[i] >= 32)
1937                                 o<<data[i];
1938                         else
1939                                 o<<".";
1940                 }
1941                 o<<std::endl;
1942                 if(at_end)
1943                         break;
1944         }
1945 }
1946
1947 /*
1948         Convert stored objects from blocks near the players to active.
1949 */
1950 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1951 {
1952         if(block == NULL)
1953                 return;
1954
1955         // Ignore if no stored objects (to not set changed flag)
1956         if(block->m_static_objects.m_stored.empty())
1957                 return;
1958
1959         verbosestream<<"ServerEnvironment::activateObjects(): "
1960                         <<"activating objects of block "<<PP(block->getPos())
1961                         <<" ("<<block->m_static_objects.m_stored.size()
1962                         <<" objects)"<<std::endl;
1963         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1964         if (large_amount) {
1965                 errorstream<<"suspiciously large amount of objects detected: "
1966                                 <<block->m_static_objects.m_stored.size()<<" in "
1967                                 <<PP(block->getPos())
1968                                 <<"; removing all of them."<<std::endl;
1969                 // Clear stored list
1970                 block->m_static_objects.m_stored.clear();
1971                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1972                         MOD_REASON_TOO_MANY_OBJECTS);
1973                 return;
1974         }
1975
1976         // Activate stored objects
1977         std::vector<StaticObject> new_stored;
1978         for (std::vector<StaticObject>::iterator
1979                         i = block->m_static_objects.m_stored.begin();
1980                         i != block->m_static_objects.m_stored.end(); ++i) {
1981                 StaticObject &s_obj = *i;
1982
1983                 // Create an active object from the data
1984                 ServerActiveObject *obj = ServerActiveObject::create
1985                                 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1986                 // If couldn't create object, store static data back.
1987                 if(obj == NULL) {
1988                         errorstream<<"ServerEnvironment::activateObjects(): "
1989                                         <<"failed to create active object from static object "
1990                                         <<"in block "<<PP(s_obj.pos/BS)
1991                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1992                         print_hexdump(verbosestream, s_obj.data);
1993
1994                         new_stored.push_back(s_obj);
1995                         continue;
1996                 }
1997                 verbosestream<<"ServerEnvironment::activateObjects(): "
1998                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1999                                 <<" type="<<(int)s_obj.type<<std::endl;
2000                 // This will also add the object to the active static list
2001                 addActiveObjectRaw(obj, false, dtime_s);
2002         }
2003         // Clear stored list
2004         block->m_static_objects.m_stored.clear();
2005         // Add leftover failed stuff to stored list
2006         for(std::vector<StaticObject>::iterator
2007                         i = new_stored.begin();
2008                         i != new_stored.end(); ++i) {
2009                 StaticObject &s_obj = *i;
2010                 block->m_static_objects.m_stored.push_back(s_obj);
2011         }
2012
2013         // Turn the active counterparts of activated objects not pending for
2014         // deactivation
2015         for(std::map<u16, StaticObject>::iterator
2016                         i = block->m_static_objects.m_active.begin();
2017                         i != block->m_static_objects.m_active.end(); ++i)
2018         {
2019                 u16 id = i->first;
2020                 ServerActiveObject *object = getActiveObject(id);
2021                 assert(object);
2022                 object->m_pending_deactivation = false;
2023         }
2024
2025         /*
2026                 Note: Block hasn't really been modified here.
2027                 The objects have just been activated and moved from the stored
2028                 static list to the active static list.
2029                 As such, the block is essentially the same.
2030                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2031                 Otherwise there would be a huge amount of unnecessary I/O.
2032         */
2033 }
2034
2035 /*
2036         Convert objects that are not standing inside active blocks to static.
2037
2038         If m_known_by_count != 0, active object is not deleted, but static
2039         data is still updated.
2040
2041         If force_delete is set, active object is deleted nevertheless. It
2042         shall only be set so in the destructor of the environment.
2043
2044         If block wasn't generated (not in memory or on disk),
2045 */
2046 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2047 {
2048         std::vector<u16> objects_to_remove;
2049         for(ActiveObjectMap::iterator i = m_active_objects.begin();
2050                         i != m_active_objects.end(); ++i) {
2051                 ServerActiveObject* obj = i->second;
2052                 assert(obj);
2053
2054                 // Do not deactivate if static data creation not allowed
2055                 if(!force_delete && !obj->isStaticAllowed())
2056                         continue;
2057
2058                 // If pending deactivation, let removeRemovedObjects() do it
2059                 if(!force_delete && obj->m_pending_deactivation)
2060                         continue;
2061
2062                 u16 id = i->first;
2063                 v3f objectpos = obj->getBasePosition();
2064
2065                 // The block in which the object resides in
2066                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2067
2068                 // If object's static data is stored in a deactivated block and object
2069                 // is actually located in an active block, re-save to the block in
2070                 // which the object is actually located in.
2071                 if(!force_delete &&
2072                                 obj->m_static_exists &&
2073                                 !m_active_blocks.contains(obj->m_static_block) &&
2074                                  m_active_blocks.contains(blockpos_o))
2075                 {
2076                         v3s16 old_static_block = obj->m_static_block;
2077
2078                         // Save to block where object is located
2079                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2080                         if(!block){
2081                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2082                                                 <<"Could not save object id="<<id
2083                                                 <<" to it's current block "<<PP(blockpos_o)
2084                                                 <<std::endl;
2085                                 continue;
2086                         }
2087                         std::string staticdata_new = obj->getStaticData();
2088                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2089                         block->m_static_objects.insert(id, s_obj);
2090                         obj->m_static_block = blockpos_o;
2091                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2092                                 MOD_REASON_STATIC_DATA_ADDED);
2093
2094                         // Delete from block where object was located
2095                         block = m_map->emergeBlock(old_static_block, false);
2096                         if(!block){
2097                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2098                                                 <<"Could not delete object id="<<id
2099                                                 <<" from it's previous block "<<PP(old_static_block)
2100                                                 <<std::endl;
2101                                 continue;
2102                         }
2103                         block->m_static_objects.remove(id);
2104                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
2105                                 MOD_REASON_STATIC_DATA_REMOVED);
2106                         continue;
2107                 }
2108
2109                 // If block is active, don't remove
2110                 if(!force_delete && m_active_blocks.contains(blockpos_o))
2111                         continue;
2112
2113                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2114                                 <<"deactivating object id="<<id<<" on inactive block "
2115                                 <<PP(blockpos_o)<<std::endl;
2116
2117                 // If known by some client, don't immediately delete.
2118                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2119
2120                 /*
2121                         Update the static data
2122                 */
2123
2124                 if(obj->isStaticAllowed())
2125                 {
2126                         // Create new static object
2127                         std::string staticdata_new = obj->getStaticData();
2128                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2129
2130                         bool stays_in_same_block = false;
2131                         bool data_changed = true;
2132
2133                         if (obj->m_static_exists) {
2134                                 if (obj->m_static_block == blockpos_o)
2135                                         stays_in_same_block = true;
2136
2137                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2138
2139                                 if (block) {
2140                                         std::map<u16, StaticObject>::iterator n =
2141                                                 block->m_static_objects.m_active.find(id);
2142                                         if (n != block->m_static_objects.m_active.end()) {
2143                                                 StaticObject static_old = n->second;
2144
2145                                                 float save_movem = obj->getMinimumSavedMovement();
2146
2147                                                 if (static_old.data == staticdata_new &&
2148                                                                 (static_old.pos - objectpos).getLength() < save_movem)
2149                                                         data_changed = false;
2150                                         } else {
2151                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2152                                                         <<"id="<<id<<" m_static_exists=true but "
2153                                                         <<"static data doesn't actually exist in "
2154                                                         <<PP(obj->m_static_block)<<std::endl;
2155                                         }
2156                                 }
2157                         }
2158
2159                         bool shall_be_written = (!stays_in_same_block || data_changed);
2160
2161                         // Delete old static object
2162                         if(obj->m_static_exists)
2163                         {
2164                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2165                                 if(block)
2166                                 {
2167                                         block->m_static_objects.remove(id);
2168                                         obj->m_static_exists = false;
2169                                         // Only mark block as modified if data changed considerably
2170                                         if(shall_be_written)
2171                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2172                                                         MOD_REASON_STATIC_DATA_CHANGED);
2173                                 }
2174                         }
2175
2176                         // Add to the block where the object is located in
2177                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2178                         // Get or generate the block
2179                         MapBlock *block = NULL;
2180                         try{
2181                                 block = m_map->emergeBlock(blockpos);
2182                         } catch(InvalidPositionException &e){
2183                                 // Handled via NULL pointer
2184                                 // NOTE: emergeBlock's failure is usually determined by it
2185                                 //       actually returning NULL
2186                         }
2187
2188                         if(block)
2189                         {
2190                                 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2191                                         warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2192                                                         << " statically but block " << PP(blockpos)
2193                                                         << " already contains "
2194                                                         << block->m_static_objects.m_stored.size()
2195                                                         << " objects."
2196                                                         << " Forcing delete." << std::endl;
2197                                         force_delete = true;
2198                                 } else {
2199                                         // If static counterpart already exists in target block,
2200                                         // remove it first.
2201                                         // This shouldn't happen because the object is removed from
2202                                         // the previous block before this according to
2203                                         // obj->m_static_block, but happens rarely for some unknown
2204                                         // reason. Unsuccessful attempts have been made to find
2205                                         // said reason.
2206                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2207                                                 warningstream<<"ServerEnv: Performing hack #83274"
2208                                                                 <<std::endl;
2209                                                 block->m_static_objects.remove(id);
2210                                         }
2211                                         // Store static data
2212                                         u16 store_id = pending_delete ? id : 0;
2213                                         block->m_static_objects.insert(store_id, s_obj);
2214
2215                                         // Only mark block as modified if data changed considerably
2216                                         if(shall_be_written)
2217                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2218                                                         MOD_REASON_STATIC_DATA_CHANGED);
2219
2220                                         obj->m_static_exists = true;
2221                                         obj->m_static_block = block->getPos();
2222                                 }
2223                         }
2224                         else{
2225                                 if(!force_delete){
2226                                         v3s16 p = floatToInt(objectpos, BS);
2227                                         errorstream<<"ServerEnv: Could not find or generate "
2228                                                         <<"a block for storing id="<<obj->getId()
2229                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
2230                                         continue;
2231                                 }
2232                         }
2233                 }
2234
2235                 /*
2236                         If known by some client, set pending deactivation.
2237                         Otherwise delete it immediately.
2238                 */
2239
2240                 if(pending_delete && !force_delete)
2241                 {
2242                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2243                                         <<"object id="<<id<<" is known by clients"
2244                                         <<"; not deleting yet"<<std::endl;
2245
2246                         obj->m_pending_deactivation = true;
2247                         continue;
2248                 }
2249
2250                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2251                                 <<"object id="<<id<<" is not known by clients"
2252                                 <<"; deleting"<<std::endl;
2253
2254                 // Tell the object about removal
2255                 obj->removingFromEnvironment();
2256                 // Deregister in scripting api
2257                 m_script->removeObjectReference(obj);
2258
2259                 // Delete active object
2260                 if(obj->environmentDeletes())
2261                         delete obj;
2262                 // Id to be removed from m_active_objects
2263                 objects_to_remove.push_back(id);
2264         }
2265
2266         // Remove references from m_active_objects
2267         for(std::vector<u16>::iterator i = objects_to_remove.begin();
2268                         i != objects_to_remove.end(); ++i) {
2269                 m_active_objects.erase(*i);
2270         }
2271 }
2272
2273 #ifndef SERVER
2274
2275 #include "clientsimpleobject.h"
2276
2277 /*
2278         ClientEnvironment
2279 */
2280
2281 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2282                 ITextureSource *texturesource, IGameDef *gamedef,
2283                 IrrlichtDevice *irr):
2284         m_map(map),
2285         m_local_player(NULL),
2286         m_smgr(smgr),
2287         m_texturesource(texturesource),
2288         m_gamedef(gamedef),
2289         m_irr(irr)
2290 {
2291         char zero = 0;
2292         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2293 }
2294
2295 ClientEnvironment::~ClientEnvironment()
2296 {
2297         // delete active objects
2298         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2299                         i != m_active_objects.end(); ++i) {
2300                 delete i->second;
2301         }
2302
2303         for(std::vector<ClientSimpleObject*>::iterator
2304                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2305                 delete *i;
2306         }
2307
2308         // Drop/delete map
2309         m_map->drop();
2310 }
2311
2312 Map & ClientEnvironment::getMap()
2313 {
2314         return *m_map;
2315 }
2316
2317 ClientMap & ClientEnvironment::getClientMap()
2318 {
2319         return *m_map;
2320 }
2321
2322 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
2323 {
2324         DSTACK(FUNCTION_NAME);
2325         /*
2326                 It is a failure if already is a local player
2327         */
2328         FATAL_ERROR_IF(m_local_player != NULL,
2329                         "Local player already allocated");
2330
2331         m_local_player = player;
2332 }
2333
2334 void ClientEnvironment::step(float dtime)
2335 {
2336         DSTACK(FUNCTION_NAME);
2337
2338         /* Step time of day */
2339         stepTimeOfDay(dtime);
2340
2341         // Get some settings
2342         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2343         bool free_move = fly_allowed && g_settings->getBool("free_move");
2344
2345         // Get local player
2346         LocalPlayer *lplayer = getLocalPlayer();
2347         assert(lplayer);
2348         // collision info queue
2349         std::vector<CollisionInfo> player_collisions;
2350
2351         /*
2352                 Get the speed the player is going
2353         */
2354         bool is_climbing = lplayer->is_climbing;
2355
2356         f32 player_speed = lplayer->getSpeed().getLength();
2357
2358         /*
2359                 Maximum position increment
2360         */
2361         //f32 position_max_increment = 0.05*BS;
2362         f32 position_max_increment = 0.1*BS;
2363
2364         // Maximum time increment (for collision detection etc)
2365         // time = distance / speed
2366         f32 dtime_max_increment = 1;
2367         if(player_speed > 0.001)
2368                 dtime_max_increment = position_max_increment / player_speed;
2369
2370         // Maximum time increment is 10ms or lower
2371         if(dtime_max_increment > 0.01)
2372                 dtime_max_increment = 0.01;
2373
2374         // Don't allow overly huge dtime
2375         if(dtime > 0.5)
2376                 dtime = 0.5;
2377
2378         f32 dtime_downcount = dtime;
2379
2380         /*
2381                 Stuff that has a maximum time increment
2382         */
2383
2384         u32 loopcount = 0;
2385         do
2386         {
2387                 loopcount++;
2388
2389                 f32 dtime_part;
2390                 if(dtime_downcount > dtime_max_increment)
2391                 {
2392                         dtime_part = dtime_max_increment;
2393                         dtime_downcount -= dtime_part;
2394                 }
2395                 else
2396                 {
2397                         dtime_part = dtime_downcount;
2398                         /*
2399                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2400                                 when dtime_part is so small that dtime_downcount -= dtime_part
2401                                 does nothing
2402                         */
2403                         dtime_downcount = 0;
2404                 }
2405
2406                 /*
2407                         Handle local player
2408                 */
2409
2410                 {
2411                         // Apply physics
2412                         if(!free_move && !is_climbing)
2413                         {
2414                                 // Gravity
2415                                 v3f speed = lplayer->getSpeed();
2416                                 if(!lplayer->in_liquid)
2417                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2418
2419                                 // Liquid floating / sinking
2420                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2421                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2422
2423                                 // Liquid resistance
2424                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2425                                 {
2426                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2427                                         // Should match the scale at which viscosity increase affects other liquid attributes
2428                                         const f32 viscosity_factor = 0.3;
2429
2430                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2431                                         f32 dl = d_wanted.getLength();
2432                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2433                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2434                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2435
2436                                         v3f d = d_wanted.normalize() * dl;
2437                                         speed += d;
2438                                 }
2439
2440                                 lplayer->setSpeed(speed);
2441                         }
2442
2443                         /*
2444                                 Move the lplayer.
2445                                 This also does collision detection.
2446                         */
2447                         lplayer->move(dtime_part, this, position_max_increment,
2448                                         &player_collisions);
2449                 }
2450         }
2451         while(dtime_downcount > 0.001);
2452
2453         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2454
2455         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2456                         i != player_collisions.end(); ++i) {
2457                 CollisionInfo &info = *i;
2458                 v3f speed_diff = info.new_speed - info.old_speed;;
2459                 // Handle only fall damage
2460                 // (because otherwise walking against something in fast_move kills you)
2461                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2462                         continue;
2463                 // Get rid of other components
2464                 speed_diff.X = 0;
2465                 speed_diff.Z = 0;
2466                 f32 pre_factor = 1; // 1 hp per node/s
2467                 f32 tolerance = BS*14; // 5 without damage
2468                 f32 post_factor = 1; // 1 hp per node/s
2469                 if(info.type == COLLISION_NODE)
2470                 {
2471                         const ContentFeatures &f = m_gamedef->ndef()->
2472                                         get(m_map->getNodeNoEx(info.node_p));
2473                         // Determine fall damage multiplier
2474                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2475                         pre_factor = 1.0 + (float)addp/100.0;
2476                 }
2477                 float speed = pre_factor * speed_diff.getLength();
2478                 if(speed > tolerance)
2479                 {
2480                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2481                         u16 damage = (u16)(damage_f+0.5);
2482                         if(damage != 0){
2483                                 damageLocalPlayer(damage, true);
2484                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2485                                 m_gamedef->event()->put(e);
2486                         }
2487                 }
2488         }
2489
2490         /*
2491                 A quick draft of lava damage
2492         */
2493         if(m_lava_hurt_interval.step(dtime, 1.0))
2494         {
2495                 v3f pf = lplayer->getPosition();
2496
2497                 // Feet, middle and head
2498                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2499                 MapNode n1 = m_map->getNodeNoEx(p1);
2500                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2501                 MapNode n2 = m_map->getNodeNoEx(p2);
2502                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2503                 MapNode n3 = m_map->getNodeNoEx(p3);
2504
2505                 u32 damage_per_second = 0;
2506                 damage_per_second = MYMAX(damage_per_second,
2507                                 m_gamedef->ndef()->get(n1).damage_per_second);
2508                 damage_per_second = MYMAX(damage_per_second,
2509                                 m_gamedef->ndef()->get(n2).damage_per_second);
2510                 damage_per_second = MYMAX(damage_per_second,
2511                                 m_gamedef->ndef()->get(n3).damage_per_second);
2512
2513                 if(damage_per_second != 0)
2514                 {
2515                         damageLocalPlayer(damage_per_second, true);
2516                 }
2517         }
2518
2519         // Protocol v29 make this behaviour obsolete
2520         if (((Client*) getGameDef())->getProtoVersion() < 29) {
2521                 /*
2522                         Drowning
2523                 */
2524                 if (m_drowning_interval.step(dtime, 2.0)) {
2525                         v3f pf = lplayer->getPosition();
2526
2527                         // head
2528                         v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
2529                         MapNode n = m_map->getNodeNoEx(p);
2530                         ContentFeatures c = m_gamedef->ndef()->get(n);
2531                         u8 drowning_damage = c.drowning;
2532                         if (drowning_damage > 0 && lplayer->hp > 0) {
2533                                 u16 breath = lplayer->getBreath();
2534                                 if (breath > 10) {
2535                                         breath = 11;
2536                                 }
2537                                 if (breath > 0) {
2538                                         breath -= 1;
2539                                 }
2540                                 lplayer->setBreath(breath);
2541                                 updateLocalPlayerBreath(breath);
2542                         }
2543
2544                         if (lplayer->getBreath() == 0 && drowning_damage > 0) {
2545                                 damageLocalPlayer(drowning_damage, true);
2546                         }
2547                 }
2548                 if (m_breathing_interval.step(dtime, 0.5)) {
2549                         v3f pf = lplayer->getPosition();
2550
2551                         // head
2552                         v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
2553                         MapNode n = m_map->getNodeNoEx(p);
2554                         ContentFeatures c = m_gamedef->ndef()->get(n);
2555                         if (!lplayer->hp) {
2556                                 lplayer->setBreath(11);
2557                         } else if (c.drowning == 0) {
2558                                 u16 breath = lplayer->getBreath();
2559                                 if (breath <= 10) {
2560                                         breath += 1;
2561                                         lplayer->setBreath(breath);
2562                                         updateLocalPlayerBreath(breath);
2563                                 }
2564                         }
2565                 }
2566         }
2567
2568         // Update lighting on local player (used for wield item)
2569         u32 day_night_ratio = getDayNightRatio();
2570         {
2571                 // Get node at head
2572
2573                 // On InvalidPositionException, use this as default
2574                 // (day: LIGHT_SUN, night: 0)
2575                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2576
2577                 v3s16 p = lplayer->getLightPosition();
2578                 node_at_lplayer = m_map->getNodeNoEx(p);
2579
2580                 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2581                 u8 day = light & 0xff;
2582                 u8 night = (light >> 8) & 0xff;
2583                 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2584         }
2585
2586         /*
2587                 Step active objects and update lighting of them
2588         */
2589
2590         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2591         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2592         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2593                         i != m_active_objects.end(); ++i) {
2594                 ClientActiveObject* obj = i->second;
2595                 // Step object
2596                 obj->step(dtime, this);
2597
2598                 if(update_lighting)
2599                 {
2600                         // Update lighting
2601                         u8 light = 0;
2602                         bool pos_ok;
2603
2604                         // Get node at head
2605                         v3s16 p = obj->getLightPosition();
2606                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2607                         if (pos_ok)
2608                                 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2609                         else
2610                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2611
2612                         obj->updateLight(light);
2613                 }
2614         }
2615
2616         /*
2617                 Step and handle simple objects
2618         */
2619         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2620         for(std::vector<ClientSimpleObject*>::iterator
2621                         i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2622                 std::vector<ClientSimpleObject*>::iterator cur = i;
2623                 ClientSimpleObject *simple = *cur;
2624
2625                 simple->step(dtime);
2626                 if(simple->m_to_be_removed) {
2627                         delete simple;
2628                         i = m_simple_objects.erase(cur);
2629                 }
2630                 else {
2631                         ++i;
2632                 }
2633         }
2634 }
2635
2636 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2637 {
2638         m_simple_objects.push_back(simple);
2639 }
2640
2641 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2642 {
2643         ClientActiveObject *obj = getActiveObject(id);
2644         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2645                 return (GenericCAO*) obj;
2646         else
2647                 return NULL;
2648 }
2649
2650 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2651 {
2652         UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2653         if (n == m_active_objects.end())
2654                 return NULL;
2655         return n->second;
2656 }
2657
2658 bool isFreeClientActiveObjectId(const u16 id,
2659         UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2660 {
2661         if(id == 0)
2662                 return false;
2663
2664         return objects.find(id) == objects.end();
2665 }
2666
2667 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2668 {
2669         //try to reuse id's as late as possible
2670         static u16 last_used_id = 0;
2671         u16 startid = last_used_id;
2672         for(;;) {
2673                 last_used_id ++;
2674                 if (isFreeClientActiveObjectId(last_used_id, objects))
2675                         return last_used_id;
2676
2677                 if (last_used_id == startid)
2678                         return 0;
2679         }
2680 }
2681
2682 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2683 {
2684         assert(object); // Pre-condition
2685         if(object->getId() == 0)
2686         {
2687                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2688                 if(new_id == 0)
2689                 {
2690                         infostream<<"ClientEnvironment::addActiveObject(): "
2691                                         <<"no free ids available"<<std::endl;
2692                         delete object;
2693                         return 0;
2694                 }
2695                 object->setId(new_id);
2696         }
2697         if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2698                 infostream<<"ClientEnvironment::addActiveObject(): "
2699                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2700                 delete object;
2701                 return 0;
2702         }
2703         infostream<<"ClientEnvironment::addActiveObject(): "
2704                         <<"added (id="<<object->getId()<<")"<<std::endl;
2705         m_active_objects[object->getId()] = object;
2706         object->addToScene(m_smgr, m_texturesource, m_irr);
2707         { // Update lighting immediately
2708                 u8 light = 0;
2709                 bool pos_ok;
2710
2711                 // Get node at head
2712                 v3s16 p = object->getLightPosition();
2713                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2714                 if (pos_ok)
2715                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2716                 else
2717                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2718
2719                 object->updateLight(light);
2720         }
2721         return object->getId();
2722 }
2723
2724 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2725                 const std::string &init_data)
2726 {
2727         ClientActiveObject* obj =
2728                         ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2729         if(obj == NULL)
2730         {
2731                 infostream<<"ClientEnvironment::addActiveObject(): "
2732                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2733                                 <<std::endl;
2734                 return;
2735         }
2736
2737         obj->setId(id);
2738
2739         try
2740         {
2741                 obj->initialize(init_data);
2742         }
2743         catch(SerializationError &e)
2744         {
2745                 errorstream<<"ClientEnvironment::addActiveObject():"
2746                                 <<" id="<<id<<" type="<<type
2747                                 <<": SerializationError in initialize(): "
2748                                 <<e.what()
2749                                 <<": init_data="<<serializeJsonString(init_data)
2750                                 <<std::endl;
2751         }
2752
2753         addActiveObject(obj);
2754 }
2755
2756 void ClientEnvironment::removeActiveObject(u16 id)
2757 {
2758         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2759                         <<"id="<<id<<std::endl;
2760         ClientActiveObject* obj = getActiveObject(id);
2761         if (obj == NULL) {
2762                 infostream<<"ClientEnvironment::removeActiveObject(): "
2763                                 <<"id="<<id<<" not found"<<std::endl;
2764                 return;
2765         }
2766         obj->removeFromScene(true);
2767         delete obj;
2768         m_active_objects.erase(id);
2769 }
2770
2771 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2772 {
2773         ClientActiveObject *obj = getActiveObject(id);
2774         if (obj == NULL) {
2775                 infostream << "ClientEnvironment::processActiveObjectMessage():"
2776                         << " got message for id=" << id << ", which doesn't exist."
2777                         << std::endl;
2778                 return;
2779         }
2780
2781         try {
2782                 obj->processMessage(data);
2783         } catch (SerializationError &e) {
2784                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2785                         << " id=" << id << " type=" << obj->getType()
2786                         << " SerializationError in processMessage(): " << e.what()
2787                         << std::endl;
2788         }
2789 }
2790
2791 /*
2792         Callbacks for activeobjects
2793 */
2794
2795 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2796 {
2797         LocalPlayer *lplayer = getLocalPlayer();
2798         assert(lplayer);
2799
2800         if (handle_hp) {
2801                 if (lplayer->hp > damage)
2802                         lplayer->hp -= damage;
2803                 else
2804                         lplayer->hp = 0;
2805         }
2806
2807         ClientEnvEvent event;
2808         event.type = CEE_PLAYER_DAMAGE;
2809         event.player_damage.amount = damage;
2810         event.player_damage.send_to_server = handle_hp;
2811         m_client_event_queue.push(event);
2812 }
2813
2814 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2815 {
2816         ClientEnvEvent event;
2817         event.type = CEE_PLAYER_BREATH;
2818         event.player_breath.amount = breath;
2819         m_client_event_queue.push(event);
2820 }
2821
2822 /*
2823         Client likes to call these
2824 */
2825
2826 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2827                 std::vector<DistanceSortedActiveObject> &dest)
2828 {
2829         for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2830                         i != m_active_objects.end(); ++i) {
2831                 ClientActiveObject* obj = i->second;
2832
2833                 f32 d = (obj->getPosition() - origin).getLength();
2834
2835                 if(d > max_d)
2836                         continue;
2837
2838                 DistanceSortedActiveObject dso(obj, d);
2839
2840                 dest.push_back(dso);
2841         }
2842 }
2843
2844 ClientEnvEvent ClientEnvironment::getClientEvent()
2845 {
2846         ClientEnvEvent event;
2847         if(m_client_event_queue.empty())
2848                 event.type = CEE_NONE;
2849         else {
2850                 event = m_client_event_queue.front();
2851                 m_client_event_queue.pop();
2852         }
2853         return event;
2854 }
2855
2856 ClientActiveObject * ClientEnvironment::getSelectedActiveObject(
2857         const core::line3d<f32> &shootline_on_map, v3f *intersection_point,
2858         v3s16 *intersection_normal)
2859 {
2860         std::vector<DistanceSortedActiveObject> objects;
2861         getActiveObjects(shootline_on_map.start,
2862                 shootline_on_map.getLength() + 3, objects);
2863         const v3f line_vector = shootline_on_map.getVector();
2864
2865         // Sort them.
2866         // After this, the closest object is the first in the array.
2867         std::sort(objects.begin(), objects.end());
2868
2869         /* Because objects can have different nodebox sizes,
2870          * the object whose center is the nearest isn't necessarily
2871          * the closest one. If an object is found, don't stop
2872          * immediately. */
2873
2874         f32 d_min = shootline_on_map.getLength();
2875         ClientActiveObject *nearest_obj = NULL;
2876         for (u32 i = 0; i < objects.size(); i++) {
2877                 ClientActiveObject *obj = objects[i].obj;
2878
2879                 aabb3f *selection_box = obj->getSelectionBox();
2880                 if (selection_box == NULL)
2881                         continue;
2882
2883                 v3f pos = obj->getPosition();
2884
2885                 aabb3f offsetted_box(selection_box->MinEdge + pos,
2886                         selection_box->MaxEdge + pos);
2887
2888                 if (offsetted_box.getCenter().getDistanceFrom(
2889                                 shootline_on_map.start) > d_min + 9.6f*BS) {
2890                         // Probably there is no active object that has bigger nodebox than
2891                         // (-5.5,-5.5,-5.5,5.5,5.5,5.5)
2892                         // 9.6 > 5.5*sqrt(3)
2893                         break;
2894                 }
2895
2896                 v3f current_intersection;
2897                 v3s16 current_normal;
2898                 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
2899                                 &current_intersection, &current_normal)) {
2900                         f32 d_current = current_intersection.getDistanceFrom(
2901                                 shootline_on_map.start);
2902                         if (d_current <= d_min) {
2903                                 d_min = d_current;
2904                                 nearest_obj = obj;
2905                                 *intersection_point = current_intersection;
2906                                 *intersection_normal = current_normal;
2907                         }
2908                 }
2909         }
2910
2911         return nearest_obj;
2912 }
2913
2914 /*
2915         Check if a node is pointable
2916 */
2917 static inline bool isPointableNode(const MapNode &n,
2918         INodeDefManager *ndef, bool liquids_pointable)
2919 {
2920         const ContentFeatures &features = ndef->get(n);
2921         return features.pointable ||
2922                (liquids_pointable && features.isLiquid());
2923 }
2924
2925 PointedThing ClientEnvironment::getPointedThing(
2926         core::line3d<f32> shootline,
2927         bool liquids_pointable,
2928         bool look_for_object)
2929 {
2930         PointedThing result;
2931
2932         INodeDefManager *nodedef = m_map->getNodeDefManager();
2933
2934         core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
2935         // The code needs to search these nodes
2936         core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge,
2937                 -maximal_exceed.MinEdge);
2938         // If a node is found, there might be a larger node behind.
2939         // To find it, we have to go further.
2940         s16 maximal_overcheck =
2941                   std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X))
2942                 + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y))
2943                 + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z));
2944
2945         const v3f original_vector = shootline.getVector();
2946         const f32 original_length = original_vector.getLength();
2947
2948         f32 min_distance = original_length;
2949
2950         // First try to find an active object
2951         if (look_for_object) {
2952                 ClientActiveObject *selected_object = getSelectedActiveObject(
2953                         shootline, &result.intersection_point,
2954                         &result.intersection_normal);
2955
2956                 if (selected_object != NULL) {
2957                         min_distance =
2958                                 (result.intersection_point - shootline.start).getLength();
2959
2960                         result.type = POINTEDTHING_OBJECT;
2961                         result.object_id = selected_object->getId();
2962                 }
2963         }
2964
2965         // Reduce shootline
2966         if (original_length > 0) {
2967                 shootline.end = shootline.start
2968                         + shootline.getVector() / original_length * min_distance;
2969         }
2970
2971         // Try to find a node that is closer than the selected active
2972         // object (if it exists).
2973
2974         voxalgo::VoxelLineIterator iterator(shootline.start / BS,
2975                 shootline.getVector() / BS);
2976         v3s16 oldnode = iterator.m_current_node_pos;
2977         // Indicates that a node was found.
2978         bool is_node_found = false;
2979         // If a node is found, it is possible that there's a node
2980         // behind it with a large nodebox, so continue the search.
2981         u16 node_foundcounter = 0;
2982         // If a node is found, this is the center of the
2983         // first nodebox the shootline meets.
2984         v3f found_boxcenter(0, 0, 0);
2985         // The untested nodes are in this range.
2986         core::aabbox3d<s16> new_nodes;
2987         while (true) {
2988                 // Test the nodes around the current node in search_range.
2989                 new_nodes = search_range;
2990                 new_nodes.MinEdge += iterator.m_current_node_pos;
2991                 new_nodes.MaxEdge += iterator.m_current_node_pos;
2992
2993                 // Only check new nodes
2994                 v3s16 delta = iterator.m_current_node_pos - oldnode;
2995                 if (delta.X > 0)
2996                         new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
2997                 else if (delta.X < 0)
2998                         new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
2999                 else if (delta.Y > 0)
3000                         new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
3001                 else if (delta.Y < 0)
3002                         new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
3003                 else if (delta.Z > 0)
3004                         new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
3005                 else if (delta.Z < 0)
3006                         new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;
3007
3008                 // For each untested node
3009                 for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
3010                 for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) {
3011                 for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
3012                 MapNode n;
3013                 v3s16 np(x, y, z);
3014                 bool is_valid_position;
3015
3016                 n = m_map->getNodeNoEx(np, &is_valid_position);
3017                 if (!(is_valid_position &&
3018                                 isPointableNode(n, nodedef, liquids_pointable))) {
3019                         continue;
3020                 }
3021                 std::vector<aabb3f> boxes;
3022                 n.getSelectionBoxes(nodedef, &boxes,
3023                         n.getNeighbors(np, m_map));
3024
3025                 v3f npf = intToFloat(np, BS);
3026                 for (std::vector<aabb3f>::const_iterator i = boxes.begin();
3027                         i != boxes.end(); ++i) {
3028                         aabb3f box = *i;
3029                         box.MinEdge += npf;
3030                         box.MaxEdge += npf;
3031                         v3f intersection_point;
3032                         v3s16 intersection_normal;
3033                         if (!boxLineCollision(box, shootline.start, shootline.getVector(),
3034                                         &intersection_point, &intersection_normal)) {
3035                                 continue;
3036                         }
3037                         f32 distance = (intersection_point - shootline.start).getLength();
3038                         if (distance >= min_distance) {
3039                                 continue;
3040                         }
3041                         result.type = POINTEDTHING_NODE;
3042                         result.node_undersurface = np;
3043                         result.intersection_point = intersection_point;
3044                         result.intersection_normal = intersection_normal;
3045                         found_boxcenter = box.getCenter();
3046                         min_distance = distance;
3047                         is_node_found = true;
3048                 }
3049                 }
3050                 }
3051                 }
3052                 if (is_node_found) {
3053                         node_foundcounter++;
3054                         if (node_foundcounter > maximal_overcheck) {
3055                                 break;
3056                         }
3057                 }
3058                 // Next node
3059                 if (iterator.hasNext()) {
3060                         oldnode = iterator.m_current_node_pos;
3061                         iterator.next();
3062                 } else {
3063                         break;
3064                 }
3065         }
3066
3067         if (is_node_found) {
3068                 // Set undersurface and abovesurface nodes
3069                 f32 d = 0.002 * BS;
3070                 v3f fake_intersection = result.intersection_point;
3071                 // Move intersection towards its source block.
3072                 if (fake_intersection.X < found_boxcenter.X)
3073                         fake_intersection.X += d;
3074                 else
3075                         fake_intersection.X -= d;
3076
3077                 if (fake_intersection.Y < found_boxcenter.Y)
3078                         fake_intersection.Y += d;
3079                 else
3080                         fake_intersection.Y -= d;
3081
3082                 if (fake_intersection.Z < found_boxcenter.Z)
3083                         fake_intersection.Z += d;
3084                 else
3085                         fake_intersection.Z -= d;
3086
3087                 result.node_real_undersurface = floatToInt(fake_intersection, BS);
3088                 result.node_abovesurface = result.node_real_undersurface
3089                         + result.intersection_normal;
3090         }
3091         return result;
3092 }
3093
3094 #endif // #ifndef SERVER