]> git.lizzy.rs Git - dragonfireclient.git/blob - src/environment.cpp
Change i++ to ++i
[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 "threading/mutex_auto_lock.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         MutexAutoLock(this->m_timeofday_lock);
208         m_time_of_day_speed = speed;
209 }
210
211 float Environment::getTimeOfDaySpeed()
212 {
213         MutexAutoLock(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         MutexAutoLock(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         MutexAutoLock(this->m_time_lock);
228         u32 retval = m_time_of_day;
229         return retval;
230 }
231
232 float Environment::getTimeOfDayF()
233 {
234         MutexAutoLock(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 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1428         v3s16 blockpos, bool static_exists, v3s16 static_block)
1429 {
1430         MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1431         if (!block)
1432                 return;
1433
1434         for (std::map<u16, StaticObject>::iterator
1435                         so_it = block->m_static_objects.m_active.begin();
1436                         so_it != block->m_static_objects.m_active.end(); ++so_it) {
1437                 // Get the ServerActiveObject counterpart to this StaticObject
1438                 std::map<u16, ServerActiveObject *>::iterator ao_it;
1439                 ao_it = m_active_objects.find(so_it->first);
1440                 if (ao_it == m_active_objects.end()) {
1441                         // If this ever happens, there must be some kind of nasty bug.
1442                         errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1443                                 "Object from MapBlock::m_static_objects::m_active not found "
1444                                 "in m_active_objects";
1445                         continue;
1446                 }
1447
1448                 ServerActiveObject *sao = ao_it->second;
1449                 sao->m_static_exists = static_exists;
1450                 sao->m_static_block  = static_block;
1451         }
1452 }
1453
1454 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1455 {
1456         if(m_active_object_messages.empty())
1457                 return ActiveObjectMessage(0);
1458
1459         ActiveObjectMessage message = m_active_object_messages.front();
1460         m_active_object_messages.pop();
1461         return message;
1462 }
1463
1464 /*
1465         ************ Private methods *************
1466 */
1467
1468 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1469                 bool set_changed, u32 dtime_s)
1470 {
1471         assert(object); // Pre-condition
1472         if(object->getId() == 0){
1473                 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1474                 if(new_id == 0)
1475                 {
1476                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1477                                         <<"no free ids available"<<std::endl;
1478                         if(object->environmentDeletes())
1479                                 delete object;
1480                         return 0;
1481                 }
1482                 object->setId(new_id);
1483         }
1484         else{
1485                 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1486                                 <<"supplied with id "<<object->getId()<<std::endl;
1487         }
1488         if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1489         {
1490                 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1491                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1492                 if(object->environmentDeletes())
1493                         delete object;
1494                 return 0;
1495         }
1496         /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1497                         <<"added (id="<<object->getId()<<")"<<std::endl;*/
1498
1499         m_active_objects[object->getId()] = object;
1500
1501         verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1502                         <<"Added id="<<object->getId()<<"; there are now "
1503                         <<m_active_objects.size()<<" active objects."
1504                         <<std::endl;
1505
1506         // Register reference in scripting api (must be done before post-init)
1507         m_script->addObjectReference(object);
1508         // Post-initialize object
1509         object->addedToEnvironment(dtime_s);
1510
1511         // Add static data to block
1512         if(object->isStaticAllowed())
1513         {
1514                 // Add static object to active static list of the block
1515                 v3f objectpos = object->getBasePosition();
1516                 std::string staticdata = object->getStaticData();
1517                 StaticObject s_obj(object->getType(), objectpos, staticdata);
1518                 // Add to the block where the object is located in
1519                 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1520                 MapBlock *block = m_map->emergeBlock(blockpos);
1521                 if(block){
1522                         block->m_static_objects.m_active[object->getId()] = s_obj;
1523                         object->m_static_exists = true;
1524                         object->m_static_block = blockpos;
1525
1526                         if(set_changed)
1527                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1528                                         MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1529                 } else {
1530                         v3s16 p = floatToInt(objectpos, BS);
1531                         errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1532                                         <<"could not emerge block for storing id="<<object->getId()
1533                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1534                 }
1535         }
1536
1537         return object->getId();
1538 }
1539
1540 /*
1541         Remove objects that satisfy (m_removed && m_known_by_count==0)
1542 */
1543 void ServerEnvironment::removeRemovedObjects()
1544 {
1545         std::vector<u16> objects_to_remove;
1546         for(std::map<u16, ServerActiveObject*>::iterator
1547                         i = m_active_objects.begin();
1548                         i != m_active_objects.end(); ++i) {
1549                 u16 id = i->first;
1550                 ServerActiveObject* obj = i->second;
1551                 // This shouldn't happen but check it
1552                 if(obj == NULL)
1553                 {
1554                         infostream<<"NULL object found in ServerEnvironment"
1555                                         <<" while finding removed objects. id="<<id<<std::endl;
1556                         // Id to be removed from m_active_objects
1557                         objects_to_remove.push_back(id);
1558                         continue;
1559                 }
1560
1561                 /*
1562                         We will delete objects that are marked as removed or thatare
1563                         waiting for deletion after deactivation
1564                 */
1565                 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1566                         continue;
1567
1568                 /*
1569                         Delete static data from block if is marked as removed
1570                 */
1571                 if(obj->m_static_exists && obj->m_removed)
1572                 {
1573                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1574                         if (block) {
1575                                 block->m_static_objects.remove(id);
1576                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1577                                         MOD_REASON_REMOVE_OBJECTS_REMOVE);
1578                                 obj->m_static_exists = false;
1579                         } else {
1580                                 infostream<<"Failed to emerge block from which an object to "
1581                                                 <<"be removed was loaded from. id="<<id<<std::endl;
1582                         }
1583                 }
1584
1585                 // If m_known_by_count > 0, don't actually remove. On some future
1586                 // invocation this will be 0, which is when removal will continue.
1587                 if(obj->m_known_by_count > 0)
1588                         continue;
1589
1590                 /*
1591                         Move static data from active to stored if not marked as removed
1592                 */
1593                 if(obj->m_static_exists && !obj->m_removed){
1594                         MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1595                         if (block) {
1596                                 std::map<u16, StaticObject>::iterator i =
1597                                                 block->m_static_objects.m_active.find(id);
1598                                 if(i != block->m_static_objects.m_active.end()){
1599                                         block->m_static_objects.m_stored.push_back(i->second);
1600                                         block->m_static_objects.m_active.erase(id);
1601                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1602                                                 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1603                                 }
1604                         } else {
1605                                 infostream<<"Failed to emerge block from which an object to "
1606                                                 <<"be deactivated was loaded from. id="<<id<<std::endl;
1607                         }
1608                 }
1609
1610                 // Tell the object about removal
1611                 obj->removingFromEnvironment();
1612                 // Deregister in scripting api
1613                 m_script->removeObjectReference(obj);
1614
1615                 // Delete
1616                 if(obj->environmentDeletes())
1617                         delete obj;
1618
1619                 // Id to be removed from m_active_objects
1620                 objects_to_remove.push_back(id);
1621         }
1622         // Remove references from m_active_objects
1623         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1624                         i != objects_to_remove.end(); ++i) {
1625                 m_active_objects.erase(*i);
1626         }
1627 }
1628
1629 static void print_hexdump(std::ostream &o, const std::string &data)
1630 {
1631         const int linelength = 16;
1632         for(int l=0; ; l++){
1633                 int i0 = linelength * l;
1634                 bool at_end = false;
1635                 int thislinelength = linelength;
1636                 if(i0 + thislinelength > (int)data.size()){
1637                         thislinelength = data.size() - i0;
1638                         at_end = true;
1639                 }
1640                 for(int di=0; di<linelength; di++){
1641                         int i = i0 + di;
1642                         char buf[4];
1643                         if(di<thislinelength)
1644                                 snprintf(buf, 4, "%.2x ", data[i]);
1645                         else
1646                                 snprintf(buf, 4, "   ");
1647                         o<<buf;
1648                 }
1649                 o<<" ";
1650                 for(int di=0; di<thislinelength; di++){
1651                         int i = i0 + di;
1652                         if(data[i] >= 32)
1653                                 o<<data[i];
1654                         else
1655                                 o<<".";
1656                 }
1657                 o<<std::endl;
1658                 if(at_end)
1659                         break;
1660         }
1661 }
1662
1663 /*
1664         Convert stored objects from blocks near the players to active.
1665 */
1666 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1667 {
1668         if(block == NULL)
1669                 return;
1670
1671         // Ignore if no stored objects (to not set changed flag)
1672         if(block->m_static_objects.m_stored.empty())
1673                 return;
1674
1675         verbosestream<<"ServerEnvironment::activateObjects(): "
1676                         <<"activating objects of block "<<PP(block->getPos())
1677                         <<" ("<<block->m_static_objects.m_stored.size()
1678                         <<" objects)"<<std::endl;
1679         bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1680         if (large_amount) {
1681                 errorstream<<"suspiciously large amount of objects detected: "
1682                                 <<block->m_static_objects.m_stored.size()<<" in "
1683                                 <<PP(block->getPos())
1684                                 <<"; removing all of them."<<std::endl;
1685                 // Clear stored list
1686                 block->m_static_objects.m_stored.clear();
1687                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1688                         MOD_REASON_TOO_MANY_OBJECTS);
1689                 return;
1690         }
1691
1692         // Activate stored objects
1693         std::vector<StaticObject> new_stored;
1694         for (std::vector<StaticObject>::iterator
1695                         i = block->m_static_objects.m_stored.begin();
1696                         i != block->m_static_objects.m_stored.end(); ++i) {
1697                 StaticObject &s_obj = *i;
1698
1699                 // Create an active object from the data
1700                 ServerActiveObject *obj = ServerActiveObject::create
1701                                 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1702                 // If couldn't create object, store static data back.
1703                 if(obj == NULL) {
1704                         errorstream<<"ServerEnvironment::activateObjects(): "
1705                                         <<"failed to create active object from static object "
1706                                         <<"in block "<<PP(s_obj.pos/BS)
1707                                         <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1708                         print_hexdump(verbosestream, s_obj.data);
1709
1710                         new_stored.push_back(s_obj);
1711                         continue;
1712                 }
1713                 verbosestream<<"ServerEnvironment::activateObjects(): "
1714                                 <<"activated static object pos="<<PP(s_obj.pos/BS)
1715                                 <<" type="<<(int)s_obj.type<<std::endl;
1716                 // This will also add the object to the active static list
1717                 addActiveObjectRaw(obj, false, dtime_s);
1718         }
1719         // Clear stored list
1720         block->m_static_objects.m_stored.clear();
1721         // Add leftover failed stuff to stored list
1722         for(std::vector<StaticObject>::iterator
1723                         i = new_stored.begin();
1724                         i != new_stored.end(); ++i) {
1725                 StaticObject &s_obj = *i;
1726                 block->m_static_objects.m_stored.push_back(s_obj);
1727         }
1728
1729         // Turn the active counterparts of activated objects not pending for
1730         // deactivation
1731         for(std::map<u16, StaticObject>::iterator
1732                         i = block->m_static_objects.m_active.begin();
1733                         i != block->m_static_objects.m_active.end(); ++i)
1734         {
1735                 u16 id = i->first;
1736                 ServerActiveObject *object = getActiveObject(id);
1737                 assert(object);
1738                 object->m_pending_deactivation = false;
1739         }
1740
1741         /*
1742                 Note: Block hasn't really been modified here.
1743                 The objects have just been activated and moved from the stored
1744                 static list to the active static list.
1745                 As such, the block is essentially the same.
1746                 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1747                 Otherwise there would be a huge amount of unnecessary I/O.
1748         */
1749 }
1750
1751 /*
1752         Convert objects that are not standing inside active blocks to static.
1753
1754         If m_known_by_count != 0, active object is not deleted, but static
1755         data is still updated.
1756
1757         If force_delete is set, active object is deleted nevertheless. It
1758         shall only be set so in the destructor of the environment.
1759
1760         If block wasn't generated (not in memory or on disk),
1761 */
1762 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1763 {
1764         std::vector<u16> objects_to_remove;
1765         for(std::map<u16, ServerActiveObject*>::iterator
1766                         i = m_active_objects.begin();
1767                         i != m_active_objects.end(); ++i) {
1768                 ServerActiveObject* obj = i->second;
1769                 assert(obj);
1770
1771                 // Do not deactivate if static data creation not allowed
1772                 if(!force_delete && !obj->isStaticAllowed())
1773                         continue;
1774
1775                 // If pending deactivation, let removeRemovedObjects() do it
1776                 if(!force_delete && obj->m_pending_deactivation)
1777                         continue;
1778
1779                 u16 id = i->first;
1780                 v3f objectpos = obj->getBasePosition();
1781
1782                 // The block in which the object resides in
1783                 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1784
1785                 // If object's static data is stored in a deactivated block and object
1786                 // is actually located in an active block, re-save to the block in
1787                 // which the object is actually located in.
1788                 if(!force_delete &&
1789                                 obj->m_static_exists &&
1790                                 !m_active_blocks.contains(obj->m_static_block) &&
1791                                  m_active_blocks.contains(blockpos_o))
1792                 {
1793                         v3s16 old_static_block = obj->m_static_block;
1794
1795                         // Save to block where object is located
1796                         MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1797                         if(!block){
1798                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1799                                                 <<"Could not save object id="<<id
1800                                                 <<" to it's current block "<<PP(blockpos_o)
1801                                                 <<std::endl;
1802                                 continue;
1803                         }
1804                         std::string staticdata_new = obj->getStaticData();
1805                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1806                         block->m_static_objects.insert(id, s_obj);
1807                         obj->m_static_block = blockpos_o;
1808                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1809                                 MOD_REASON_STATIC_DATA_ADDED);
1810
1811                         // Delete from block where object was located
1812                         block = m_map->emergeBlock(old_static_block, false);
1813                         if(!block){
1814                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1815                                                 <<"Could not delete object id="<<id
1816                                                 <<" from it's previous block "<<PP(old_static_block)
1817                                                 <<std::endl;
1818                                 continue;
1819                         }
1820                         block->m_static_objects.remove(id);
1821                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
1822                                 MOD_REASON_STATIC_DATA_REMOVED);
1823                         continue;
1824                 }
1825
1826                 // If block is active, don't remove
1827                 if(!force_delete && m_active_blocks.contains(blockpos_o))
1828                         continue;
1829
1830                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1831                                 <<"deactivating object id="<<id<<" on inactive block "
1832                                 <<PP(blockpos_o)<<std::endl;
1833
1834                 // If known by some client, don't immediately delete.
1835                 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1836
1837                 /*
1838                         Update the static data
1839                 */
1840
1841                 if(obj->isStaticAllowed())
1842                 {
1843                         // Create new static object
1844                         std::string staticdata_new = obj->getStaticData();
1845                         StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1846
1847                         bool stays_in_same_block = false;
1848                         bool data_changed = true;
1849
1850                         if (obj->m_static_exists) {
1851                                 if (obj->m_static_block == blockpos_o)
1852                                         stays_in_same_block = true;
1853
1854                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1855
1856                                 if (block) {
1857                                         std::map<u16, StaticObject>::iterator n =
1858                                                 block->m_static_objects.m_active.find(id);
1859                                         if (n != block->m_static_objects.m_active.end()) {
1860                                                 StaticObject static_old = n->second;
1861
1862                                                 float save_movem = obj->getMinimumSavedMovement();
1863
1864                                                 if (static_old.data == staticdata_new &&
1865                                                                 (static_old.pos - objectpos).getLength() < save_movem)
1866                                                         data_changed = false;
1867                                         } else {
1868                                                 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1869                                                         <<"id="<<id<<" m_static_exists=true but "
1870                                                         <<"static data doesn't actually exist in "
1871                                                         <<PP(obj->m_static_block)<<std::endl;
1872                                         }
1873                                 }
1874                         }
1875
1876                         bool shall_be_written = (!stays_in_same_block || data_changed);
1877
1878                         // Delete old static object
1879                         if(obj->m_static_exists)
1880                         {
1881                                 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1882                                 if(block)
1883                                 {
1884                                         block->m_static_objects.remove(id);
1885                                         obj->m_static_exists = false;
1886                                         // Only mark block as modified if data changed considerably
1887                                         if(shall_be_written)
1888                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1889                                                         MOD_REASON_STATIC_DATA_CHANGED);
1890                                 }
1891                         }
1892
1893                         // Add to the block where the object is located in
1894                         v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1895                         // Get or generate the block
1896                         MapBlock *block = NULL;
1897                         try{
1898                                 block = m_map->emergeBlock(blockpos);
1899                         } catch(InvalidPositionException &e){
1900                                 // Handled via NULL pointer
1901                                 // NOTE: emergeBlock's failure is usually determined by it
1902                                 //       actually returning NULL
1903                         }
1904
1905                         if(block)
1906                         {
1907                                 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1908                                         errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1909                                                         <<" statically but block "<<PP(blockpos)
1910                                                         <<" already contains "
1911                                                         <<block->m_static_objects.m_stored.size()
1912                                                         <<" objects."
1913                                                         <<" Forcing delete."<<std::endl;
1914                                         force_delete = true;
1915                                 } else {
1916                                         // If static counterpart already exists in target block,
1917                                         // remove it first.
1918                                         // This shouldn't happen because the object is removed from
1919                                         // the previous block before this according to
1920                                         // obj->m_static_block, but happens rarely for some unknown
1921                                         // reason. Unsuccessful attempts have been made to find
1922                                         // said reason.
1923                                         if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1924                                                 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1925                                                                 <<std::endl;
1926                                                 block->m_static_objects.remove(id);
1927                                         }
1928                                         // Store static data
1929                                         u16 store_id = pending_delete ? id : 0;
1930                                         block->m_static_objects.insert(store_id, s_obj);
1931
1932                                         // Only mark block as modified if data changed considerably
1933                                         if(shall_be_written)
1934                                                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1935                                                         MOD_REASON_STATIC_DATA_CHANGED);
1936
1937                                         obj->m_static_exists = true;
1938                                         obj->m_static_block = block->getPos();
1939                                 }
1940                         }
1941                         else{
1942                                 if(!force_delete){
1943                                         v3s16 p = floatToInt(objectpos, BS);
1944                                         errorstream<<"ServerEnv: Could not find or generate "
1945                                                         <<"a block for storing id="<<obj->getId()
1946                                                         <<" statically (pos="<<PP(p)<<")"<<std::endl;
1947                                         continue;
1948                                 }
1949                         }
1950                 }
1951
1952                 /*
1953                         If known by some client, set pending deactivation.
1954                         Otherwise delete it immediately.
1955                 */
1956
1957                 if(pending_delete && !force_delete)
1958                 {
1959                         verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1960                                         <<"object id="<<id<<" is known by clients"
1961                                         <<"; not deleting yet"<<std::endl;
1962
1963                         obj->m_pending_deactivation = true;
1964                         continue;
1965                 }
1966
1967                 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1968                                 <<"object id="<<id<<" is not known by clients"
1969                                 <<"; deleting"<<std::endl;
1970
1971                 // Tell the object about removal
1972                 obj->removingFromEnvironment();
1973                 // Deregister in scripting api
1974                 m_script->removeObjectReference(obj);
1975
1976                 // Delete active object
1977                 if(obj->environmentDeletes())
1978                         delete obj;
1979                 // Id to be removed from m_active_objects
1980                 objects_to_remove.push_back(id);
1981         }
1982
1983         // Remove references from m_active_objects
1984         for(std::vector<u16>::iterator i = objects_to_remove.begin();
1985                         i != objects_to_remove.end(); ++i) {
1986                 m_active_objects.erase(*i);
1987         }
1988 }
1989
1990 #ifndef SERVER
1991
1992 #include "clientsimpleobject.h"
1993
1994 /*
1995         ClientEnvironment
1996 */
1997
1998 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1999                 ITextureSource *texturesource, IGameDef *gamedef,
2000                 IrrlichtDevice *irr):
2001         m_map(map),
2002         m_smgr(smgr),
2003         m_texturesource(texturesource),
2004         m_gamedef(gamedef),
2005         m_irr(irr)
2006 {
2007         char zero = 0;
2008         memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2009 }
2010
2011 ClientEnvironment::~ClientEnvironment()
2012 {
2013         // delete active objects
2014         for(std::map<u16, ClientActiveObject*>::iterator
2015                         i = m_active_objects.begin();
2016                         i != m_active_objects.end(); ++i)
2017         {
2018                 delete i->second;
2019         }
2020
2021         for(std::vector<ClientSimpleObject*>::iterator
2022                         i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2023                 delete *i;
2024         }
2025
2026         // Drop/delete map
2027         m_map->drop();
2028 }
2029
2030 Map & ClientEnvironment::getMap()
2031 {
2032         return *m_map;
2033 }
2034
2035 ClientMap & ClientEnvironment::getClientMap()
2036 {
2037         return *m_map;
2038 }
2039
2040 void ClientEnvironment::addPlayer(Player *player)
2041 {
2042         DSTACK(__FUNCTION_NAME);
2043         /*
2044                 It is a failure if player is local and there already is a local
2045                 player
2046         */
2047         FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2048                 "Player is local but there is already a local player");
2049
2050         Environment::addPlayer(player);
2051 }
2052
2053 LocalPlayer * ClientEnvironment::getLocalPlayer()
2054 {
2055         for(std::vector<Player*>::iterator i = m_players.begin();
2056                         i != m_players.end(); ++i) {
2057                 Player *player = *i;
2058                 if(player->isLocal())
2059                         return (LocalPlayer*)player;
2060         }
2061         return NULL;
2062 }
2063
2064 void ClientEnvironment::step(float dtime)
2065 {
2066         DSTACK(__FUNCTION_NAME);
2067
2068         /* Step time of day */
2069         stepTimeOfDay(dtime);
2070
2071         // Get some settings
2072         bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2073         bool free_move = fly_allowed && g_settings->getBool("free_move");
2074
2075         // Get local player
2076         LocalPlayer *lplayer = getLocalPlayer();
2077         assert(lplayer);
2078         // collision info queue
2079         std::vector<CollisionInfo> player_collisions;
2080
2081         /*
2082                 Get the speed the player is going
2083         */
2084         bool is_climbing = lplayer->is_climbing;
2085
2086         f32 player_speed = lplayer->getSpeed().getLength();
2087
2088         /*
2089                 Maximum position increment
2090         */
2091         //f32 position_max_increment = 0.05*BS;
2092         f32 position_max_increment = 0.1*BS;
2093
2094         // Maximum time increment (for collision detection etc)
2095         // time = distance / speed
2096         f32 dtime_max_increment = 1;
2097         if(player_speed > 0.001)
2098                 dtime_max_increment = position_max_increment / player_speed;
2099
2100         // Maximum time increment is 10ms or lower
2101         if(dtime_max_increment > 0.01)
2102                 dtime_max_increment = 0.01;
2103
2104         // Don't allow overly huge dtime
2105         if(dtime > 0.5)
2106                 dtime = 0.5;
2107
2108         f32 dtime_downcount = dtime;
2109
2110         /*
2111                 Stuff that has a maximum time increment
2112         */
2113
2114         u32 loopcount = 0;
2115         do
2116         {
2117                 loopcount++;
2118
2119                 f32 dtime_part;
2120                 if(dtime_downcount > dtime_max_increment)
2121                 {
2122                         dtime_part = dtime_max_increment;
2123                         dtime_downcount -= dtime_part;
2124                 }
2125                 else
2126                 {
2127                         dtime_part = dtime_downcount;
2128                         /*
2129                                 Setting this to 0 (no -=dtime_part) disables an infinite loop
2130                                 when dtime_part is so small that dtime_downcount -= dtime_part
2131                                 does nothing
2132                         */
2133                         dtime_downcount = 0;
2134                 }
2135
2136                 /*
2137                         Handle local player
2138                 */
2139
2140                 {
2141                         // Apply physics
2142                         if(free_move == false && is_climbing == false)
2143                         {
2144                                 // Gravity
2145                                 v3f speed = lplayer->getSpeed();
2146                                 if(lplayer->in_liquid == false)
2147                                         speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2148
2149                                 // Liquid floating / sinking
2150                                 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2151                                         speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2152
2153                                 // Liquid resistance
2154                                 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2155                                 {
2156                                         // How much the node's viscosity blocks movement, ranges between 0 and 1
2157                                         // Should match the scale at which viscosity increase affects other liquid attributes
2158                                         const f32 viscosity_factor = 0.3;
2159
2160                                         v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2161                                         f32 dl = d_wanted.getLength();
2162                                         if(dl > lplayer->movement_liquid_fluidity_smooth)
2163                                                 dl = lplayer->movement_liquid_fluidity_smooth;
2164                                         dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2165
2166                                         v3f d = d_wanted.normalize() * dl;
2167                                         speed += d;
2168                                 }
2169
2170                                 lplayer->setSpeed(speed);
2171                         }
2172
2173                         /*
2174                                 Move the lplayer.
2175                                 This also does collision detection.
2176                         */
2177                         lplayer->move(dtime_part, this, position_max_increment,
2178                                         &player_collisions);
2179                 }
2180         }
2181         while(dtime_downcount > 0.001);
2182
2183         //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2184
2185         for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2186                         i != player_collisions.end(); ++i) {
2187                 CollisionInfo &info = *i;
2188                 v3f speed_diff = info.new_speed - info.old_speed;;
2189                 // Handle only fall damage
2190                 // (because otherwise walking against something in fast_move kills you)
2191                 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2192                         continue;
2193                 // Get rid of other components
2194                 speed_diff.X = 0;
2195                 speed_diff.Z = 0;
2196                 f32 pre_factor = 1; // 1 hp per node/s
2197                 f32 tolerance = BS*14; // 5 without damage
2198                 f32 post_factor = 1; // 1 hp per node/s
2199                 if(info.type == COLLISION_NODE)
2200                 {
2201                         const ContentFeatures &f = m_gamedef->ndef()->
2202                                         get(m_map->getNodeNoEx(info.node_p));
2203                         // Determine fall damage multiplier
2204                         int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2205                         pre_factor = 1.0 + (float)addp/100.0;
2206                 }
2207                 float speed = pre_factor * speed_diff.getLength();
2208                 if(speed > tolerance)
2209                 {
2210                         f32 damage_f = (speed - tolerance)/BS * post_factor;
2211                         u16 damage = (u16)(damage_f+0.5);
2212                         if(damage != 0){
2213                                 damageLocalPlayer(damage, true);
2214                                 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2215                                 m_gamedef->event()->put(e);
2216                         }
2217                 }
2218         }
2219
2220         /*
2221                 A quick draft of lava damage
2222         */
2223         if(m_lava_hurt_interval.step(dtime, 1.0))
2224         {
2225                 v3f pf = lplayer->getPosition();
2226
2227                 // Feet, middle and head
2228                 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2229                 MapNode n1 = m_map->getNodeNoEx(p1);
2230                 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2231                 MapNode n2 = m_map->getNodeNoEx(p2);
2232                 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2233                 MapNode n3 = m_map->getNodeNoEx(p3);
2234
2235                 u32 damage_per_second = 0;
2236                 damage_per_second = MYMAX(damage_per_second,
2237                                 m_gamedef->ndef()->get(n1).damage_per_second);
2238                 damage_per_second = MYMAX(damage_per_second,
2239                                 m_gamedef->ndef()->get(n2).damage_per_second);
2240                 damage_per_second = MYMAX(damage_per_second,
2241                                 m_gamedef->ndef()->get(n3).damage_per_second);
2242
2243                 if(damage_per_second != 0)
2244                 {
2245                         damageLocalPlayer(damage_per_second, true);
2246                 }
2247         }
2248
2249         /*
2250                 Drowning
2251         */
2252         if(m_drowning_interval.step(dtime, 2.0))
2253         {
2254                 v3f pf = lplayer->getPosition();
2255
2256                 // head
2257                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2258                 MapNode n = m_map->getNodeNoEx(p);
2259                 ContentFeatures c = m_gamedef->ndef()->get(n);
2260                 u8 drowning_damage = c.drowning;
2261                 if(drowning_damage > 0 && lplayer->hp > 0){
2262                         u16 breath = lplayer->getBreath();
2263                         if(breath > 10){
2264                                 breath = 11;
2265                         }
2266                         if(breath > 0){
2267                                 breath -= 1;
2268                         }
2269                         lplayer->setBreath(breath);
2270                         updateLocalPlayerBreath(breath);
2271                 }
2272
2273                 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2274                         damageLocalPlayer(drowning_damage, true);
2275                 }
2276         }
2277         if(m_breathing_interval.step(dtime, 0.5))
2278         {
2279                 v3f pf = lplayer->getPosition();
2280
2281                 // head
2282                 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2283                 MapNode n = m_map->getNodeNoEx(p);
2284                 ContentFeatures c = m_gamedef->ndef()->get(n);
2285                 if (!lplayer->hp){
2286                         lplayer->setBreath(11);
2287                 }
2288                 else if(c.drowning == 0){
2289                         u16 breath = lplayer->getBreath();
2290                         if(breath <= 10){
2291                                 breath += 1;
2292                                 lplayer->setBreath(breath);
2293                                 updateLocalPlayerBreath(breath);
2294                         }
2295                 }
2296         }
2297
2298         /*
2299                 Stuff that can be done in an arbitarily large dtime
2300         */
2301         for(std::vector<Player*>::iterator i = m_players.begin();
2302                         i != m_players.end(); ++i) {
2303                 Player *player = *i;
2304
2305                 /*
2306                         Handle non-local players
2307                 */
2308                 if(player->isLocal() == false) {
2309                         // Move
2310                         player->move(dtime, this, 100*BS);
2311
2312                 }
2313         }
2314
2315         // Update lighting on local player (used for wield item)
2316         u32 day_night_ratio = getDayNightRatio();
2317         {
2318                 // Get node at head
2319
2320                 // On InvalidPositionException, use this as default
2321                 // (day: LIGHT_SUN, night: 0)
2322                 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2323
2324                 v3s16 p = lplayer->getLightPosition();
2325                 node_at_lplayer = m_map->getNodeNoEx(p);
2326
2327                 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2328                 u8 day = light & 0xff;
2329                 u8 night = (light >> 8) & 0xff;
2330                 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2331         }
2332
2333         /*
2334                 Step active objects and update lighting of them
2335         */
2336
2337         g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2338         bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2339         for(std::map<u16, ClientActiveObject*>::iterator
2340                         i = m_active_objects.begin();
2341                         i != m_active_objects.end(); ++i)
2342         {
2343                 ClientActiveObject* obj = i->second;
2344                 // Step object
2345                 obj->step(dtime, this);
2346
2347                 if(update_lighting)
2348                 {
2349                         // Update lighting
2350                         u8 light = 0;
2351                         bool pos_ok;
2352
2353                         // Get node at head
2354                         v3s16 p = obj->getLightPosition();
2355                         MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2356                         if (pos_ok)
2357                                 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2358                         else
2359                                 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2360
2361                         obj->updateLight(light);
2362                 }
2363         }
2364
2365         /*
2366                 Step and handle simple objects
2367         */
2368         g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2369         for(std::vector<ClientSimpleObject*>::iterator
2370                         i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2371                 std::vector<ClientSimpleObject*>::iterator cur = i;
2372                 ClientSimpleObject *simple = *cur;
2373
2374                 simple->step(dtime);
2375                 if(simple->m_to_be_removed) {
2376                         delete simple;
2377                         i = m_simple_objects.erase(cur);
2378                 }
2379                 else {
2380                         ++i;
2381                 }
2382         }
2383 }
2384
2385 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2386 {
2387         m_simple_objects.push_back(simple);
2388 }
2389
2390 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2391 {
2392         ClientActiveObject *obj = getActiveObject(id);
2393         if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2394                 return (GenericCAO*) obj;
2395         else
2396                 return NULL;
2397 }
2398
2399 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2400 {
2401         std::map<u16, ClientActiveObject*>::iterator n;
2402         n = m_active_objects.find(id);
2403         if(n == m_active_objects.end())
2404                 return NULL;
2405         return n->second;
2406 }
2407
2408 bool isFreeClientActiveObjectId(u16 id,
2409                 std::map<u16, ClientActiveObject*> &objects)
2410 {
2411         if(id == 0)
2412                 return false;
2413
2414         return objects.find(id) == objects.end();
2415 }
2416
2417 u16 getFreeClientActiveObjectId(
2418                 std::map<u16, ClientActiveObject*> &objects)
2419 {
2420         //try to reuse id's as late as possible
2421         static u16 last_used_id = 0;
2422         u16 startid = last_used_id;
2423         for(;;)
2424         {
2425                 last_used_id ++;
2426                 if(isFreeClientActiveObjectId(last_used_id, objects))
2427                         return last_used_id;
2428
2429                 if(last_used_id == startid)
2430                         return 0;
2431         }
2432 }
2433
2434 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2435 {
2436         assert(object); // Pre-condition
2437         if(object->getId() == 0)
2438         {
2439                 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2440                 if(new_id == 0)
2441                 {
2442                         infostream<<"ClientEnvironment::addActiveObject(): "
2443                                         <<"no free ids available"<<std::endl;
2444                         delete object;
2445                         return 0;
2446                 }
2447                 object->setId(new_id);
2448         }
2449         if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2450         {
2451                 infostream<<"ClientEnvironment::addActiveObject(): "
2452                                 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2453                 delete object;
2454                 return 0;
2455         }
2456         infostream<<"ClientEnvironment::addActiveObject(): "
2457                         <<"added (id="<<object->getId()<<")"<<std::endl;
2458         m_active_objects[object->getId()] = object;
2459         object->addToScene(m_smgr, m_texturesource, m_irr);
2460         { // Update lighting immediately
2461                 u8 light = 0;
2462                 bool pos_ok;
2463
2464                 // Get node at head
2465                 v3s16 p = object->getLightPosition();
2466                 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2467                 if (pos_ok)
2468                         light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2469                 else
2470                         light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2471
2472                 object->updateLight(light);
2473         }
2474         return object->getId();
2475 }
2476
2477 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2478                 const std::string &init_data)
2479 {
2480         ClientActiveObject* obj =
2481                         ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2482         if(obj == NULL)
2483         {
2484                 infostream<<"ClientEnvironment::addActiveObject(): "
2485                                 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2486                                 <<std::endl;
2487                 return;
2488         }
2489
2490         obj->setId(id);
2491
2492         try
2493         {
2494                 obj->initialize(init_data);
2495         }
2496         catch(SerializationError &e)
2497         {
2498                 errorstream<<"ClientEnvironment::addActiveObject():"
2499                                 <<" id="<<id<<" type="<<type
2500                                 <<": SerializationError in initialize(): "
2501                                 <<e.what()
2502                                 <<": init_data="<<serializeJsonString(init_data)
2503                                 <<std::endl;
2504         }
2505
2506         addActiveObject(obj);
2507 }
2508
2509 void ClientEnvironment::removeActiveObject(u16 id)
2510 {
2511         verbosestream<<"ClientEnvironment::removeActiveObject(): "
2512                         <<"id="<<id<<std::endl;
2513         ClientActiveObject* obj = getActiveObject(id);
2514         if(obj == NULL)
2515         {
2516                 infostream<<"ClientEnvironment::removeActiveObject(): "
2517                                 <<"id="<<id<<" not found"<<std::endl;
2518                 return;
2519         }
2520         obj->removeFromScene(true);
2521         delete obj;
2522         m_active_objects.erase(id);
2523 }
2524
2525 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2526 {
2527         ClientActiveObject *obj = getActiveObject(id);
2528         if (obj == NULL) {
2529                 infostream << "ClientEnvironment::processActiveObjectMessage():"
2530                         << " got message for id=" << id << ", which doesn't exist."
2531                         << std::endl;
2532                 return;
2533         }
2534
2535         try {
2536                 obj->processMessage(data);
2537         } catch (SerializationError &e) {
2538                 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2539                         << " id=" << id << " type=" << obj->getType()
2540                         << " SerializationError in processMessage(): " << e.what()
2541                         << std::endl;
2542         }
2543 }
2544
2545 /*
2546         Callbacks for activeobjects
2547 */
2548
2549 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2550 {
2551         LocalPlayer *lplayer = getLocalPlayer();
2552         assert(lplayer);
2553
2554         if (handle_hp) {
2555                 if (lplayer->hp > damage)
2556                         lplayer->hp -= damage;
2557                 else
2558                         lplayer->hp = 0;
2559         }
2560
2561         ClientEnvEvent event;
2562         event.type = CEE_PLAYER_DAMAGE;
2563         event.player_damage.amount = damage;
2564         event.player_damage.send_to_server = handle_hp;
2565         m_client_event_queue.push_back(event);
2566 }
2567
2568 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2569 {
2570         ClientEnvEvent event;
2571         event.type = CEE_PLAYER_BREATH;
2572         event.player_breath.amount = breath;
2573         m_client_event_queue.push_back(event);
2574 }
2575
2576 /*
2577         Client likes to call these
2578 */
2579
2580 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2581                 std::vector<DistanceSortedActiveObject> &dest)
2582 {
2583         for(std::map<u16, ClientActiveObject*>::iterator
2584                         i = m_active_objects.begin();
2585                         i != m_active_objects.end(); ++i)
2586         {
2587                 ClientActiveObject* obj = i->second;
2588
2589                 f32 d = (obj->getPosition() - origin).getLength();
2590
2591                 if(d > max_d)
2592                         continue;
2593
2594                 DistanceSortedActiveObject dso(obj, d);
2595
2596                 dest.push_back(dso);
2597         }
2598 }
2599
2600 ClientEnvEvent ClientEnvironment::getClientEvent()
2601 {
2602         ClientEnvEvent event;
2603         if(m_client_event_queue.empty())
2604                 event.type = CEE_NONE;
2605         else {
2606                 event = m_client_event_queue.front();
2607                 m_client_event_queue.pop_front();
2608         }
2609         return event;
2610 }
2611
2612 #endif // #ifndef SERVER
2613
2614