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