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