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