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