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
content_abm.cpp
craftdef.cpp
nameidmapping.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.
*/
#include <set>
#include <list>
#include <map>
#include "environment.h"
#include "filesys.h"
#include "porting.h"
@ -296,6 +299,12 @@ ServerEnvironment::~ServerEnvironment()
// Drop/delete map
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)
@ -545,56 +554,101 @@ void ServerEnvironment::loadMeta(const std::string &savedir)
}
}
#if 0
// This is probably very useless
void spawnRandomObjects(MapBlock *block)
struct ActiveABM
{
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
ActiveBlockModifier *abm;
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;
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
{
v3s16 p(x0,y0,z0);
MapNode n = block->getNodeNoEx(p);
if(n.getContent() == CONTENT_IGNORE)
continue;
if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE)
continue;
if(m_gamedef->ndef()->get(n).walkable)
{
last_node_walkable = true;
continue;
}
if(last_node_walkable)
{
// If block contains light information
if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT)
{
if(n.getLight(LIGHTBANK_DAY) <= 5)
{
if(myrand() % 1000 == 0)
{
v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
pos_f.Y -= BS*0.4;
ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
std::string data = obj->getStaticData();
StaticObject s_obj(obj->getType(),
obj->getBasePosition(), data);
// Add one
block->m_static_objects.insert(0, s_obj);
delete obj;
block->raiseModified(MOD_STATE_WRITE_NEEDED,
"spawnRandomObjects");
}
}
infostream<<"ABMHandler: dtime_s="<<dtime_s<<std::endl;
if(dtime_s < 0.001)
return;
INodeDefManager *ndef = env->getGameDef()->ndef();
for(core::list<ActiveBlockModifier*>::Iterator
i = abms.begin(); i != abms.end(); i++){
ActiveBlockModifier *abm = *i;
ActiveABM aabm;
aabm.abm = abm;
float intervals = dtime_s / abm->getTriggerInterval();
float chance = abm->getTriggerChance();
if(chance == 0)
chance = 1;
aabm.chance = 1.0 / pow(1.0 / chance, intervals);
if(aabm.chance == 0)
aabm.chance = 1;
std::set<std::string> contents_s = abm->getTriggerContents();
for(std::set<std::string>::iterator
i = contents_s.begin(); i != contents_s.end(); i++){
content_t c = ndef->getId(*i);
if(c == CONTENT_IGNORE)
continue;
std::map<content_t, std::list<ActiveABM> >::iterator j;
j = m_aabms.find(c);
if(j == m_aabms.end()){
std::list<ActiveABM> aabmlist;
m_aabms[c] = aabmlist;
j = m_aabms.find(c);
}
j->second.push_back(aabm);
}
last_node_walkable = false;
}
}
}
#endif
void apply(MapBlock *block)
{
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)
{
@ -605,10 +659,14 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
dtime_s = m_game_time - block->getTimestamp();
dtime_s += additional_dtime;
// Set current time as timestamp (and let it set ChangedFlag)
block->setTimestamp(m_game_time);
infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
<<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
activateObjects(block);
@ -626,36 +684,14 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
"node metadata modified in activateBlock");
}
// TODO: Do something
// TODO: Implement usage of ActiveBlockModifier
// Here's a quick demonstration
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);
#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
}
/* Handle ActiveBlockModifiers */
ABMHandler abmhandler(m_abms, dtime_s, this);
abmhandler.apply(block);
}
void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
{
m_abms.push_back(abm);
}
void ServerEnvironment::clearAllObjects()
@ -748,20 +784,6 @@ void ServerEnvironment::clearAllObjects()
<<" 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)
{
DSTACK(__FUNCTION_NAME);
@ -930,6 +952,11 @@ void ServerEnvironment::step(float dtime)
// Set current time as timestamp
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
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);
//float dtime = 10.0;
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
i = m_active_blocks.m_list.getIterator();
i.atEnd()==false; i++)
@ -979,199 +998,8 @@ void ServerEnvironment::step(float dtime)
// Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time);
/*
Do stuff!
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);
}
}
}
/* Handle ActiveBlockModifiers */
abmhandler.apply(block);
}
u32 time_ms = timer.stop(true);
@ -1241,60 +1069,6 @@ void ServerEnvironment::step(float dtime)
*/
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)

@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- etc.
*/
#include <list>
#include <set>
#include "common_irrlicht.h"
#include "player.h"
#include "map.h"
@ -95,6 +95,29 @@ protected:
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
*/
@ -299,38 +322,14 @@ private:
// List of active blocks
ActiveBlockList m_active_blocks;
IntervalLimiter m_active_blocks_management_interval;
IntervalLimiter m_active_blocks_test_interval;
IntervalLimiter m_active_block_modifier_interval;
IntervalLimiter m_active_blocks_nodemetadata_interval;
// Time from the beginning of the game in seconds.
// Incremented in step().
u32 m_game_time;
// A helper variable for incrementing the latter
float m_game_time_fraction_counter;
};
/*
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;
core::list<ActiveBlockModifier*> m_abms;
};
#ifndef SERVER

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

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "voxel.h"
#include "utility.h" // Needed for UniqueQueue, a member of Map
#include "modifiedstate.h"
extern "C" {
#include "sqlite3.h"
@ -236,7 +237,7 @@ public:
virtual void beginSave() {return;};
virtual void endSave() {return;};
virtual void save(bool only_changed){assert(0);};
virtual void save(ModifiedState save_level){assert(0);};
// Server implements this.
// Client leaves it as no-op.
@ -396,7 +397,7 @@ public:
void beginSave();
void endSave();
void save(bool only_changed);
void save(ModifiedState save_level);
//void loadAll();
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_generated(false),
m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
m_usage_timer(0)
{
data = NULL;
@ -928,10 +929,12 @@ void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
m_static_objects.deSerialize(is);
// Timestamp
if(version >= 17)
if(version >= 17){
setTimestamp(readU32(is));
else
m_disk_timestamp = m_timestamp;
} else {
setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
}
// Dynamically re-set ids based on node names
NameIdMapping nimap;

@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER
#include "mapblock_mesh.h"
#endif
#include "modifiedstate.h"
class Map;
class NodeMetadataList;
@ -53,19 +54,6 @@ enum{
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
// CONTENT_IGNORE.
/*enum BlockGenerationStatus
@ -167,6 +155,10 @@ public:
m_modified = mod;
m_modified_reason = reason;
m_modified_reason_too_long = false;
if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){
m_disk_timestamp = m_timestamp;
}
} else if(mod == m_modified){
if(!m_modified_reason_too_long){
if(m_modified_reason.size() < 40)
@ -509,6 +501,10 @@ public:
{
return m_timestamp;
}
u32 getDiskTimestamp()
{
return m_disk_timestamp;
}
/*
See m_usage_timer
@ -646,6 +642,8 @@ private:
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no 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.

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 "craftdef.h"
#include "mapgen.h"
#include "content_abm.h"
#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);
// 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
infostream<<"Server: Loading players"<<std::endl;
m_env->deSerializePlayers(m_mapsavedir);
/*
Add some test ActiveBlockModifiers to environment
*/
add_legacy_abms(m_env, m_nodedef);
}
Server::~Server()
@ -1937,22 +1943,8 @@ void Server::AsyncRunStep()
// Map
JMutexAutoLock lock(m_env_mutex);
/*// Unload unused data (delete from memory)
m_env->getMap().unloadUnusedData(
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 changed parts of map
m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
// Save players
m_env->serializePlayers(m_mapsavedir);