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