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