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