Replace old active block random node modifying things with actual ActiveBlockModifiers

This commit is contained in:
Perttu Ahola 2011-11-28 00:45:34 +02:00
parent 05df2ee8a4
commit 842eb5da28
11 changed files with 528 additions and 422 deletions

@ -94,6 +94,7 @@ configure_file(
) )
set(common_SRCS set(common_SRCS
content_abm.cpp
craftdef.cpp craftdef.cpp
nameidmapping.cpp nameidmapping.cpp
tooldef.cpp tooldef.cpp

269
src/content_abm.cpp Normal file

@ -0,0 +1,269 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "content_abm.h"
#include "environment.h"
#include "gamedef.h"
#include "nodedef.h"
#include "content_sao.h"
#include "settings.h"
#include "mapblock.h" // For getNodeBlockPos
#include "mapgen.h" // For mapgen::make_tree
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
class GrowGrassABM : public ActiveBlockModifier
{
private:
public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("dirt");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 20; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
{
INodeDefManager *ndef = env->getGameDef()->ndef();
ServerMap *map = &env->getServerMap();
MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
if(ndef->get(n_top).light_propagates &&
!ndef->get(n_top).isLiquid() &&
n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13)
{
n.setContent(ndef->getId("dirt_with_grass"));
map->addNodeWithEvent(p, n);
}
}
};
class RemoveGrassABM : public ActiveBlockModifier
{
private:
public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("dirt_with_grass");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 1; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n)
{
INodeDefManager *ndef = env->getGameDef()->ndef();
ServerMap *map = &env->getServerMap();
MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0));
if(!ndef->get(n_top).light_propagates ||
ndef->get(n_top).isLiquid())
{
n.setContent(ndef->getId("dirt"));
map->addNodeWithEvent(p, n);
}
}
};
class SpawnRatsAroundTreesABM : public ActiveBlockModifier
{
private:
public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("tree");
s.insert("jungletree");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 200; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider)
{
if(active_object_count_wider != 0)
return;
INodeDefManager *ndef = env->getGameDef()->ndef();
ServerMap *map = &env->getServerMap();
v3s16 p1 = p + v3s16(myrand_range(-2, 2),
0, myrand_range(-2, 2));
MapNode n1 = map->getNodeNoEx(p1);
MapNode n1b = map->getNodeNoEx(p1+v3s16(0,-1,0));
if(n1b.getContent() == ndef->getId("dirt_with_grass") &&
n1.getContent() == CONTENT_AIR)
{
v3f pos = intToFloat(p1, BS);
ServerActiveObject *obj = new RatSAO(env, pos);
env->addActiveObject(obj);
}
}
};
static void getMob_dungeon_master(Settings &properties)
{
properties.set("looks", "dungeon_master");
properties.setFloat("yaw", 1.57);
properties.setFloat("hp", 30);
properties.setBool("bright_shooting", true);
properties.set("shoot_type", "fireball");
properties.set("shoot_y", "0.7");
properties.set("player_hit_damage", "1");
properties.set("player_hit_distance", "1.0");
properties.set("player_hit_interval", "0.5");
properties.setBool("mindless_rage", myrand_range(0,100)==0);
}
class SpawnInCavesABM : public ActiveBlockModifier
{
private:
public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("stone");
s.insert("mossycobble");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 200; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider)
{
if(active_object_count_wider != 0)
return;
INodeDefManager *ndef = env->getGameDef()->ndef();
ServerMap *map = &env->getServerMap();
v3s16 p1 = p + v3s16(0,1,0);
MapNode n1a = map->getNodeNoEx(p1+v3s16(0,0,0));
if(n1a.getLightBlend(env->getDayNightRatio(), ndef) <= 3){
MapNode n1b = map->getNodeNoEx(p1+v3s16(0,1,0));
if(n1a.getContent() == CONTENT_AIR &&
n1b.getContent() == CONTENT_AIR)
{
v3f pos = intToFloat(p1, BS);
int i = myrand()%5;
if(i == 0 || i == 1){
actionstream<<"A dungeon master spawns at "
<<PP(p1)<<std::endl;
Settings properties;
getMob_dungeon_master(properties);
ServerActiveObject *obj = new MobV2SAO(
env, pos, &properties);
env->addActiveObject(obj);
} else if(i == 2 || i == 3){
actionstream<<"Rats spawn at "
<<PP(p1)<<std::endl;
for(int j=0; j<3; j++){
ServerActiveObject *obj = new RatSAO(
env, pos);
env->addActiveObject(obj);
}
} else {
actionstream<<"An oerkki spawns at "
<<PP(p1)<<std::endl;
ServerActiveObject *obj = new Oerkki1SAO(
env, pos);
env->addActiveObject(obj);
}
}
}
}
};
class MakeTreesFromSaplingsABM : public ActiveBlockModifier
{
private:
public:
virtual std::set<std::string> getTriggerContents()
{
std::set<std::string> s;
s.insert("sapling");
return s;
}
virtual float getTriggerInterval()
{ return 10.0; }
virtual u32 getTriggerChance()
{ return 50; }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider)
{
INodeDefManager *ndef = env->getGameDef()->ndef();
ServerMap *map = &env->getServerMap();
actionstream<<"A sapling grows into a tree at "
<<PP(p)<<std::endl;
core::map<v3s16, MapBlock*> modified_blocks;
v3s16 tree_p = p;
ManualMapVoxelManipulator vmanip(map);
v3s16 tree_blockp = getNodeBlockPos(tree_p);
vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
bool is_apple_tree = myrand()%4 == 0;
mapgen::make_tree(vmanip, tree_p, is_apple_tree, ndef);
vmanip.blitBackAll(&modified_blocks);
// update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
map->updateLighting(lighting_modified_blocks, modified_blocks);
// Send a MEET_OTHER event
MapEditEvent event;
event.type = MEET_OTHER;
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
event.modified_blocks.insert(p, true);
}
map->dispatchEvent(&event);
}
};
void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef)
{
env->addActiveBlockModifier(new GrowGrassABM());
env->addActiveBlockModifier(new RemoveGrassABM());
env->addActiveBlockModifier(new SpawnRatsAroundTreesABM());
env->addActiveBlockModifier(new SpawnInCavesABM());
env->addActiveBlockModifier(new MakeTreesFromSaplingsABM());
}

33
src/content_abm.h Normal file

@ -0,0 +1,33 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CONTENT_ABM_HEADER
#define CONTENT_ABM_HEADER
class ServerEnvironment;
class INodeDefManager;
/*
Legacy ActiveBlockModifiers
*/
void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef);
#endif

@ -17,6 +17,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include <set>
#include <list>
#include <map>
#include "environment.h" #include "environment.h"
#include "filesys.h" #include "filesys.h"
#include "porting.h" #include "porting.h"
@ -296,6 +299,12 @@ ServerEnvironment::~ServerEnvironment()
// Drop/delete map // Drop/delete map
m_map->drop(); m_map->drop();
// Delete ActiveBlockModifiers
for(core::list<ActiveBlockModifier*>::Iterator
i = m_abms.begin(); i != m_abms.end(); i++){
delete (*i);
}
} }
void ServerEnvironment::serializePlayers(const std::string &savedir) void ServerEnvironment::serializePlayers(const std::string &savedir)
@ -545,56 +554,101 @@ void ServerEnvironment::loadMeta(const std::string &savedir)
} }
} }
#if 0 struct ActiveABM
// This is probably very useless
void spawnRandomObjects(MapBlock *block)
{ {
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) ActiveBlockModifier *abm;
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) int chance;
};
class ABMHandler
{
private:
ServerEnvironment *m_env;
std::map<content_t, std::list<ActiveABM> > m_aabms;
public:
ABMHandler(core::list<ActiveBlockModifier*> &abms,
float dtime_s, ServerEnvironment *env):
m_env(env)
{ {
bool last_node_walkable = false; infostream<<"ABMHandler: dtime_s="<<dtime_s<<std::endl;
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) if(dtime_s < 0.001)
{ return;
v3s16 p(x0,y0,z0); INodeDefManager *ndef = env->getGameDef()->ndef();
MapNode n = block->getNodeNoEx(p); for(core::list<ActiveBlockModifier*>::Iterator
if(n.getContent() == CONTENT_IGNORE) i = abms.begin(); i != abms.end(); i++){
continue; ActiveBlockModifier *abm = *i;
if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE) ActiveABM aabm;
continue; aabm.abm = abm;
if(m_gamedef->ndef()->get(n).walkable) float intervals = dtime_s / abm->getTriggerInterval();
{ float chance = abm->getTriggerChance();
last_node_walkable = true; if(chance == 0)
continue; chance = 1;
} aabm.chance = 1.0 / pow(1.0 / chance, intervals);
if(last_node_walkable) if(aabm.chance == 0)
{ aabm.chance = 1;
// If block contains light information std::set<std::string> contents_s = abm->getTriggerContents();
if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT) for(std::set<std::string>::iterator
{ i = contents_s.begin(); i != contents_s.end(); i++){
if(n.getLight(LIGHTBANK_DAY) <= 5) content_t c = ndef->getId(*i);
{ if(c == CONTENT_IGNORE)
if(myrand() % 1000 == 0) continue;
{ std::map<content_t, std::list<ActiveABM> >::iterator j;
v3f pos_f = intToFloat(p+block->getPosRelative(), BS); j = m_aabms.find(c);
pos_f.Y -= BS*0.4; if(j == m_aabms.end()){
ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f); std::list<ActiveABM> aabmlist;
std::string data = obj->getStaticData(); m_aabms[c] = aabmlist;
StaticObject s_obj(obj->getType(), j = m_aabms.find(c);
obj->getBasePosition(), data);
// Add one
block->m_static_objects.insert(0, s_obj);
delete obj;
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"spawnRandomObjects");
}
}
} }
j->second.push_back(aabm);
} }
last_node_walkable = false;
} }
} }
} void apply(MapBlock *block)
#endif {
ServerMap *map = &m_env->getServerMap();
// Find out how many objects the block contains
u32 active_object_count = block->m_static_objects.m_active.size();
// Find out how many objects this and all the neighbors contain
u32 active_object_count_wider = 0;
for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)
for(s16 z=-1; z<=1; z++)
{
MapBlock *block2 = map->getBlockNoCreateNoEx(
block->getPos() + v3s16(x,y,z));
if(block2==NULL)
continue;
active_object_count_wider +=
block2->m_static_objects.m_active.size()
+ block2->m_static_objects.m_stored.size();
}
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
{
MapNode n = block->getNodeNoEx(p0);
content_t c = n.getContent();
v3s16 p = p0 + block->getPosRelative();
std::map<content_t, std::list<ActiveABM> >::iterator j;
j = m_aabms.find(c);
if(j == m_aabms.end())
continue;
for(std::list<ActiveABM>::iterator
i = j->second.begin(); i != j->second.end(); i++){
if(myrand() % i->chance != 0)
continue;
// Call all the trigger variations
i->abm->trigger(m_env, p, n);
i->abm->trigger(m_env, p, n,
active_object_count, active_object_count_wider);
}
}
}
};
void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
{ {
@ -605,10 +659,14 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
dtime_s = m_game_time - block->getTimestamp(); dtime_s = m_game_time - block->getTimestamp();
dtime_s += additional_dtime; dtime_s += additional_dtime;
// Set current time as timestamp (and let it set ChangedFlag) infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
block->setTimestamp(m_game_time); <<stamp<<", game time: "<<m_game_time<<std::endl;
//infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl; // Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time);
infostream<<"ServerEnvironment::activateBlock(): block is "
<<dtime_s<<" seconds old."<<std::endl;
// Activate stored objects // Activate stored objects
activateObjects(block); activateObjects(block);
@ -626,36 +684,14 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
"node metadata modified in activateBlock"); "node metadata modified in activateBlock");
} }
// TODO: Do something /* Handle ActiveBlockModifiers */
// TODO: Implement usage of ActiveBlockModifier ABMHandler abmhandler(m_abms, dtime_s, this);
abmhandler.apply(block);
// Here's a quick demonstration }
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++) void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++) {
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++) m_abms.push_back(abm);
{
v3s16 p = p0 + block->getPosRelative();
MapNode n = block->getNodeNoEx(p0);
#if 1
// Test something:
// Convert all mud under proper day lighting to grass
if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_MUD"))
{
if(dtime_s > 300)
{
MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
if(m_gamedef->ndef()->get(n_top).light_propagates &&
!m_gamedef->ndef()->get(n_top).isLiquid() &&
n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
{
n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS"));
m_map->addNodeWithEvent(p, n);
}
}
}
#endif
}
} }
void ServerEnvironment::clearAllObjects() void ServerEnvironment::clearAllObjects()
@ -748,20 +784,6 @@ void ServerEnvironment::clearAllObjects()
<<" in "<<num_blocks_cleared<<" blocks"<<std::endl; <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
} }
static void getMob_dungeon_master(Settings &properties)
{
properties.set("looks", "dungeon_master");
properties.setFloat("yaw", 1.57);
properties.setFloat("hp", 30);
properties.setBool("bright_shooting", true);
properties.set("shoot_type", "fireball");
properties.set("shoot_y", "0.7");
properties.set("player_hit_damage", "1");
properties.set("player_hit_distance", "1.0");
properties.set("player_hit_interval", "0.5");
properties.setBool("mindless_rage", myrand_range(0,100)==0);
}
void ServerEnvironment::step(float dtime) void ServerEnvironment::step(float dtime)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
@ -930,6 +952,11 @@ void ServerEnvironment::step(float dtime)
// Set current time as timestamp // Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time); block->setTimestampNoChangedFlag(m_game_time);
// If time has changed much from the one on disk,
// set block to be saved when it is unloaded
if(block->getTimestamp() > block->getDiskTimestamp() + 60)
block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
"Timestamp older than 60s (step)");
// Run node metadata // Run node metadata
bool changed = block->m_node_metadata->step(dtime); bool changed = block->m_node_metadata->step(dtime);
@ -946,23 +973,15 @@ void ServerEnvironment::step(float dtime)
} }
} }
if(m_active_blocks_test_interval.step(dtime, 10.0)) const float abm_interval = 10.0;
if(m_active_block_modifier_interval.step(dtime, abm_interval))
{ {
ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG); ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
//float dtime = 10.0;
TimeTaker timer("modify in active blocks"); TimeTaker timer("modify in active blocks");
// Initialize handling of ActiveBlockModifiers
ABMHandler abmhandler(m_abms, abm_interval, this);
INodeDefManager *ndef = m_gamedef->ndef();
// Pre-fetch content ids for the luge loop
content_t c_dirt = ndef->getId("dirt");
content_t c_grass = ndef->getId("dirt_with_grass");
content_t c_tree = ndef->getId("tree");
content_t c_jungletree = ndef->getId("jungletree");
content_t c_stone = ndef->getId("stone");
content_t c_mossycobble = ndef->getId("mossycobble");
content_t c_sapling = ndef->getId("sapling");
for(core::map<v3s16, bool>::Iterator for(core::map<v3s16, bool>::Iterator
i = m_active_blocks.m_list.getIterator(); i = m_active_blocks.m_list.getIterator();
i.atEnd()==false; i++) i.atEnd()==false; i++)
@ -979,199 +998,8 @@ void ServerEnvironment::step(float dtime)
// Set current time as timestamp // Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time); block->setTimestampNoChangedFlag(m_game_time);
/* /* Handle ActiveBlockModifiers */
Do stuff! abmhandler.apply(block);
Note that map modifications should be done using the event-
making map methods so that the server gets information
about them.
Reading can be done quickly directly from the block.
Everything should bind to inside this single content
searching loop to keep things fast.
*/
// TODO: Implement usage of ActiveBlockModifier
// Find out how many objects the block contains
//u32 active_object_count = block->m_static_objects.m_active.size();
// Find out how many objects this and all the neighbors contain
u32 active_object_count_wider = 0;
for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)
for(s16 z=-1; z<=1; z++)
{
MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
if(block==NULL)
continue;
active_object_count_wider +=
block->m_static_objects.m_active.size()
+ block->m_static_objects.m_stored.size();
/*if(block->m_static_objects.m_stored.size() != 0){
errorstream<<"ServerEnvironment::step(): "
<<PP(block->getPos())<<" contains "
<<block->m_static_objects.m_stored.size()
<<" stored objects; "
<<"when spawning objects, when counting active "
<<"objects in wide area. relative position: "
<<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
}*/
}
v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
{
v3s16 p = p0 + block->getPosRelative();
MapNode n = block->getNodeNoEx(p0);
/*
Test something:
Convert dirt under proper lighting to grass
*/
if(n.getContent() == c_dirt)
{
if(myrand()%20 == 0)
{
MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
if(m_gamedef->ndef()->get(n_top).light_propagates &&
!m_gamedef->ndef()->get(n_top).isLiquid() &&
n_top.getLightBlend(getDayNightRatio(),
m_gamedef->ndef()) >= 13)
{
n.setContent(c_grass);
m_map->addNodeWithEvent(p, n);
}
}
}
/*
Convert grass into dirt if under something else than air
*/
if(n.getContent() == c_grass)
{
//if(myrand()%20 == 0)
{
MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
if(!m_gamedef->ndef()->get(n_top).light_propagates ||
m_gamedef->ndef()->get(n_top).isLiquid())
{
n.setContent(c_dirt);
m_map->addNodeWithEvent(p, n);
}
}
}
/*
Rats spawn around regular trees
*/
if(n.getContent() == c_tree ||
n.getContent() == c_jungletree)
{
if(myrand()%200 == 0 && active_object_count_wider == 0)
{
v3s16 p1 = p + v3s16(myrand_range(-2, 2),
0, myrand_range(-2, 2));
MapNode n1 = m_map->getNodeNoEx(p1);
MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
if(n1b.getContent() == c_grass &&
n1.getContent() == CONTENT_AIR)
{
v3f pos = intToFloat(p1, BS);
ServerActiveObject *obj = new RatSAO(this, pos);
addActiveObject(obj);
}
}
}
/*
Fun things spawn in caves and dungeons
*/
if(n.getContent() == c_stone ||
n.getContent() == c_mossycobble)
{
if(myrand()%200 == 0 && active_object_count_wider == 0)
{
v3s16 p1 = p + v3s16(0,1,0);
MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
if(n1a.getLightBlend(getDayNightRatio(),
m_gamedef->ndef()) <= 3){
MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
if(n1a.getContent() == CONTENT_AIR &&
n1b.getContent() == CONTENT_AIR)
{
v3f pos = intToFloat(p1, BS);
int i = myrand()%5;
if(i == 0 || i == 1){
actionstream<<"A dungeon master spawns at "
<<PP(p1)<<std::endl;
Settings properties;
getMob_dungeon_master(properties);
ServerActiveObject *obj = new MobV2SAO(
this, pos, &properties);
addActiveObject(obj);
} else if(i == 2 || i == 3){
actionstream<<"Rats spawn at "
<<PP(p1)<<std::endl;
for(int j=0; j<3; j++){
ServerActiveObject *obj = new RatSAO(
this, pos);
addActiveObject(obj);
}
} else {
actionstream<<"An oerkki spawns at "
<<PP(p1)<<std::endl;
ServerActiveObject *obj = new Oerkki1SAO(
this, pos);
addActiveObject(obj);
}
}
}
}
}
/*
Make trees from saplings!
*/
if(n.getContent() == c_sapling)
{
if(myrand()%50 == 0)
{
actionstream<<"A sapling grows into a tree at "
<<PP(p)<<std::endl;
core::map<v3s16, MapBlock*> modified_blocks;
v3s16 tree_p = p;
ManualMapVoxelManipulator vmanip(m_map);
v3s16 tree_blockp = getNodeBlockPos(tree_p);
vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
bool is_apple_tree = myrand()%4 == 0;
mapgen::make_tree(vmanip, tree_p, is_apple_tree,
m_gamedef->ndef());
vmanip.blitBackAll(&modified_blocks);
// update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
m_map->updateLighting(lighting_modified_blocks, modified_blocks);
// Send a MEET_OTHER event
MapEditEvent event;
event.type = MEET_OTHER;
for(core::map<v3s16, MapBlock*>::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
event.modified_blocks.insert(p, true);
}
m_map->dispatchEvent(&event);
}
}
}
} }
u32 time_ms = timer.stop(true); u32 time_ms = timer.stop(true);
@ -1241,60 +1069,6 @@ void ServerEnvironment::step(float dtime)
*/ */
removeRemovedObjects(); removeRemovedObjects();
} }
if(g_settings->getBool("enable_experimental"))
{
/*
TEST CODE
*/
#if 0
m_random_spawn_timer -= dtime;
if(m_random_spawn_timer < 0)
{
//m_random_spawn_timer += myrand_range(2.0, 20.0);
//m_random_spawn_timer += 2.0;
m_random_spawn_timer += 200.0;
/*
Find some position
*/
/*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
s16 y = 1 + getServerMap().findGroundLevel(p2d);
v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
Player *player = getRandomConnectedPlayer();
v3f pos(0,0,0);
if(player)
pos = player->getPosition();
pos += v3f(
myrand_range(-3,3)*BS,
5,
myrand_range(-3,3)*BS
);
/*
Create a ServerActiveObject
*/
//TestSAO *obj = new TestSAO(this, pos);
//ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
//ServerActiveObject *obj = new RatSAO(this, pos);
//ServerActiveObject *obj = new Oerkki1SAO(this, pos);
//ServerActiveObject *obj = new FireflySAO(this, pos);
infostream<<"Server: Spawning MobV2SAO at "
<<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
Settings properties;
getMob_dungeon_master(properties);
ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
addActiveObject(obj);
}
#endif
} // enable_experimental
} }
ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)

@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- etc. - etc.
*/ */
#include <list> #include <set>
#include "common_irrlicht.h" #include "common_irrlicht.h"
#include "player.h" #include "player.h"
#include "map.h" #include "map.h"
@ -95,6 +95,29 @@ protected:
u32 m_time_of_day; u32 m_time_of_day;
}; };
/*
Active block modifier interface.
These are fed into ServerEnvironment at initialization time;
ServerEnvironment handles deleting them.
*/
class ActiveBlockModifier
{
public:
ActiveBlockModifier(){};
virtual ~ActiveBlockModifier(){};
virtual std::set<std::string> getTriggerContents()=0;
virtual float getTriggerInterval() = 0;
// chance of (1 / return value), 0 is disallowed
virtual u32 getTriggerChance() = 0;
// This is called usually at interval for 1/chance of the nodes
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
u32 active_object_count, u32 active_object_count_wider){};
};
/* /*
List of active blocks, used by ServerEnvironment List of active blocks, used by ServerEnvironment
*/ */
@ -299,38 +322,14 @@ private:
// List of active blocks // List of active blocks
ActiveBlockList m_active_blocks; ActiveBlockList m_active_blocks;
IntervalLimiter m_active_blocks_management_interval; IntervalLimiter m_active_blocks_management_interval;
IntervalLimiter m_active_blocks_test_interval; IntervalLimiter m_active_block_modifier_interval;
IntervalLimiter m_active_blocks_nodemetadata_interval; IntervalLimiter m_active_blocks_nodemetadata_interval;
// Time from the beginning of the game in seconds. // Time from the beginning of the game in seconds.
// Incremented in step(). // Incremented in step().
u32 m_game_time; u32 m_game_time;
// A helper variable for incrementing the latter // A helper variable for incrementing the latter
float m_game_time_fraction_counter; float m_game_time_fraction_counter;
}; core::list<ActiveBlockModifier*> m_abms;
/*
Active block modifier interface.
These are fed into ServerEnvironment at initialization time;
ServerEnvironment handles deleting them.
NOTE: Not used currently (TODO: Use or remove)
*/
class ActiveBlockModifier
{
public:
ActiveBlockModifier(){};
virtual ~ActiveBlockModifier(){};
//virtual core::list<u8> update(ServerEnvironment *env) = 0;
virtual u32 getTriggerContentCount(){ return 1;}
virtual u8 getTriggerContent(u32 i) = 0;
virtual float getActiveInterval() = 0;
// chance of (1 / return value), 0 is disallowed
virtual u32 getActiveChance() = 0;
// This is called usually at interval for 1/chance of the nodes
virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
}; };
#ifndef SERVER #ifndef SERVER

@ -2011,7 +2011,7 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
emergeSector(v2s16(0,0)); emergeSector(v2s16(0,0));
// Initially write whole map // Initially write whole map
save(false); save(MOD_STATE_CLEAN);
} }
ServerMap::~ServerMap() ServerMap::~ServerMap()
@ -2023,7 +2023,7 @@ ServerMap::~ServerMap()
if(m_map_saving_enabled) if(m_map_saving_enabled)
{ {
// Save only changed parts // Save only changed parts
save(true); save(MOD_STATE_WRITE_AT_UNLOAD);
infostream<<"Server: saved map to "<<m_savedir<<std::endl; infostream<<"Server: saved map to "<<m_savedir<<std::endl;
} }
else else
@ -2324,7 +2324,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
Save changed parts of map Save changed parts of map
NOTE: Will be saved later. NOTE: Will be saved later.
*/ */
//save(true); //save(MOD_STATE_WRITE_AT_UNLOAD);
/*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<"," /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
<<blockpos.Z<<")"<<std::endl;*/ <<blockpos.Z<<")"<<std::endl;*/
@ -2832,7 +2832,7 @@ std::string ServerMap::getBlockFilename(v3s16 p)
return cc; return cc;
} }
void ServerMap::save(bool only_changed) void ServerMap::save(ModifiedState save_level)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
if(m_map_saving_enabled == false) if(m_map_saving_enabled == false)
@ -2841,11 +2841,11 @@ void ServerMap::save(bool only_changed)
return; return;
} }
if(only_changed == false) if(save_level == MOD_STATE_CLEAN)
infostream<<"ServerMap: Saving whole map, this can take time." infostream<<"ServerMap: Saving whole map, this can take time."
<<std::endl; <<std::endl;
if(only_changed == false || m_map_metadata_changed) if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
{ {
saveMapMeta(); saveMapMeta();
} }
@ -2866,7 +2866,7 @@ void ServerMap::save(bool only_changed)
ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue(); ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
assert(sector->getId() == MAPSECTOR_SERVER); assert(sector->getId() == MAPSECTOR_SERVER);
if(sector->differs_from_disk || only_changed == false) if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
{ {
saveSectorMeta(sector); saveSectorMeta(sector);
sector_meta_count++; sector_meta_count++;
@ -2881,8 +2881,7 @@ void ServerMap::save(bool only_changed)
block_count_all++; block_count_all++;
if(block->getModified() >= MOD_STATE_WRITE_NEEDED if(block->getModified() >= save_level)
|| only_changed == false)
{ {
// Lazy beginSave() // Lazy beginSave()
if(!save_started){ if(!save_started){
@ -2909,7 +2908,7 @@ void ServerMap::save(bool only_changed)
/* /*
Only print if something happened or saved whole map Only print if something happened or saved whole map
*/ */
if(only_changed == false || sector_meta_count != 0 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
|| block_count != 0) || block_count != 0)
{ {
infostream<<"ServerMap: Written: " infostream<<"ServerMap: Written: "

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h" #include "constants.h"
#include "voxel.h" #include "voxel.h"
#include "utility.h" // Needed for UniqueQueue, a member of Map #include "utility.h" // Needed for UniqueQueue, a member of Map
#include "modifiedstate.h"
extern "C" { extern "C" {
#include "sqlite3.h" #include "sqlite3.h"
@ -236,7 +237,7 @@ public:
virtual void beginSave() {return;}; virtual void beginSave() {return;};
virtual void endSave() {return;}; virtual void endSave() {return;};
virtual void save(bool only_changed){assert(0);}; virtual void save(ModifiedState save_level){assert(0);};
// Server implements this. // Server implements this.
// Client leaves it as no-op. // Client leaves it as no-op.
@ -396,7 +397,7 @@ public:
void beginSave(); void beginSave();
void endSave(); void endSave();
void save(bool only_changed); void save(ModifiedState save_level);
//void loadAll(); //void loadAll();
void listAllLoadableBlocks(core::list<v3s16> &dst); void listAllLoadableBlocks(core::list<v3s16> &dst);

@ -48,6 +48,7 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_day_night_differs(false), m_day_night_differs(false),
m_generated(false), m_generated(false),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED), m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0) m_usage_timer(0)
{ {
data = NULL; data = NULL;
@ -928,10 +929,12 @@ void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
m_static_objects.deSerialize(is); m_static_objects.deSerialize(is);
// Timestamp // Timestamp
if(version >= 17) if(version >= 17){
setTimestamp(readU32(is)); setTimestamp(readU32(is));
else m_disk_timestamp = m_timestamp;
} else {
setTimestamp(BLOCK_TIMESTAMP_UNDEFINED); setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
}
// Dynamically re-set ids based on node names // Dynamically re-set ids based on node names
NameIdMapping nimap; NameIdMapping nimap;

@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER #ifndef SERVER
#include "mapblock_mesh.h" #include "mapblock_mesh.h"
#endif #endif
#include "modifiedstate.h"
class Map; class Map;
class NodeMetadataList; class NodeMetadataList;
@ -53,19 +54,6 @@ enum{
FACE_LEFT FACE_LEFT
};*/ };*/
enum ModifiedState
{
// Has not been modified.
MOD_STATE_CLEAN = 0,
MOD_RESERVED1 = 1,
// Has been modified, and will be saved when being unloaded.
MOD_STATE_WRITE_AT_UNLOAD = 2,
MOD_RESERVED3 = 3,
// Has been modified, and will be saved as soon as possible.
MOD_STATE_WRITE_NEEDED = 4,
MOD_RESERVED5 = 5,
};
// NOTE: If this is enabled, set MapBlock to be initialized with // NOTE: If this is enabled, set MapBlock to be initialized with
// CONTENT_IGNORE. // CONTENT_IGNORE.
/*enum BlockGenerationStatus /*enum BlockGenerationStatus
@ -167,6 +155,10 @@ public:
m_modified = mod; m_modified = mod;
m_modified_reason = reason; m_modified_reason = reason;
m_modified_reason_too_long = false; m_modified_reason_too_long = false;
if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){
m_disk_timestamp = m_timestamp;
}
} else if(mod == m_modified){ } else if(mod == m_modified){
if(!m_modified_reason_too_long){ if(!m_modified_reason_too_long){
if(m_modified_reason.size() < 40) if(m_modified_reason.size() < 40)
@ -509,6 +501,10 @@ public:
{ {
return m_timestamp; return m_timestamp;
} }
u32 getDiskTimestamp()
{
return m_disk_timestamp;
}
/* /*
See m_usage_timer See m_usage_timer
@ -646,6 +642,8 @@ private:
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp. Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
*/ */
u32 m_timestamp; u32 m_timestamp;
// The on-disk (or to-be on-disk) timestamp value
u32 m_disk_timestamp;
/* /*
When the block is accessed, this is set to 0. When the block is accessed, this is set to 0.

37
src/modifiedstate.h Normal file

@ -0,0 +1,37 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MODIFIEDSTATE_HEADER
#define MODIFIEDSTATE_HEADER
enum ModifiedState
{
// Has not been modified.
MOD_STATE_CLEAN = 0,
MOD_RESERVED1 = 1,
// Has been modified, and will be saved when being unloaded.
MOD_STATE_WRITE_AT_UNLOAD = 2,
MOD_RESERVED3 = 3,
// Has been modified, and will be saved as soon as possible.
MOD_STATE_WRITE_NEEDED = 4,
MOD_RESERVED5 = 5,
};
#endif

@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tooldef.h" #include "tooldef.h"
#include "craftdef.h" #include "craftdef.h"
#include "mapgen.h" #include "mapgen.h"
#include "content_abm.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -282,7 +283,7 @@ void * EmergeThread::Thread()
MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events); MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
// Activate objects and stuff // Activate objects and stuff
m_server->m_env->activateBlock(block, 3600); m_server->m_env->activateBlock(block, 0);
} }
} }
@ -1116,6 +1117,11 @@ Server::Server(
// Load players // Load players
infostream<<"Server: Loading players"<<std::endl; infostream<<"Server: Loading players"<<std::endl;
m_env->deSerializePlayers(m_mapsavedir); m_env->deSerializePlayers(m_mapsavedir);
/*
Add some test ActiveBlockModifiers to environment
*/
add_legacy_abms(m_env, m_nodedef);
} }
Server::~Server() Server::~Server()
@ -1937,22 +1943,8 @@ void Server::AsyncRunStep()
// Map // Map
JMutexAutoLock lock(m_env_mutex); JMutexAutoLock lock(m_env_mutex);
/*// Unload unused data (delete from memory) // Save changed parts of map
m_env->getMap().unloadUnusedData( m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
g_settings->getFloat("server_unload_unused_sectors_timeout"));
*/
/*u32 deleted_count = m_env->getMap().unloadUnusedData(
g_settings->getFloat("server_unload_unused_sectors_timeout"));
*/
// Save only changed parts
m_env->getMap().save(true);
/*if(deleted_count > 0)
{
infostream<<"Server: Unloaded "<<deleted_count
<<" blocks from memory"<<std::endl;
}*/
// Save players // Save players
m_env->serializePlayers(m_mapsavedir); m_env->serializePlayers(m_mapsavedir);