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