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"
#include "settings.h"
#include "log.h"
#include "profiler.h"
+#include "scriptapi.h"
+#include "nodedef.h"
+#include "nodemetadata.h"
+#include "main.h" // For g_settings, g_profiler
+#include "gamedef.h"
+#include "serverremoteplayer.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
return time_to_daynight_ratio(m_time_of_day);
}
+/*
+ ABMWithState
+*/
+
+ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
+ abm(abm_),
+ timer(0)
+{
+ // Initialize timer to random value to spread processing
+ float itv = abm->getTriggerInterval();
+ itv = MYMAX(0.001, itv); // No less than 1ms
+ int minval = MYMAX(-0.51*itv, -60); // Clamp to
+ int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
+ timer = myrand_range(minval, maxval);
+}
+
/*
ActiveBlockList
*/
ServerEnvironment
*/
-ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
+ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
+ IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
m_map(map),
- m_server(server),
+ m_lua(L),
+ m_gamedef(gamedef),
+ m_emerger(emerger),
m_random_spawn_timer(3),
m_send_recommended_timer(0),
m_game_time(0),
// Drop/delete map
m_map->drop();
+
+ // Delete ActiveBlockModifiers
+ for(core::list<ABMWithState>::Iterator
+ i = m_abms.begin(); i != m_abms.end(); i++){
+ delete i->abm;
+ }
}
void ServerEnvironment::serializePlayers(const std::string &savedir)
//infostream<<"Checking player file "<<path<<std::endl;
// Load player to see what is its name
- ServerRemotePlayer testplayer;
+ ServerRemotePlayer testplayer(this);
{
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
// Full path to this file
std::string path = players_path + "/" + player_files[i].name;
- infostream<<"Checking player file "<<path<<std::endl;
+ //infostream<<"Checking player file "<<path<<std::endl;
// Load player to see what is its name
- ServerRemotePlayer testplayer;
+ ServerRemotePlayer testplayer(this);
{
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
<<testplayer.getName()<<std::endl;
}
- infostream<<"Loaded test player with name "<<testplayer.getName()
- <<std::endl;
+ /*infostream<<"Loaded test player with name "<<testplayer.getName()
+ <<std::endl;*/
// Search for the player
std::string playername = testplayer.getName();
bool newplayer = false;
if(player == NULL)
{
- infostream<<"Is a new player"<<std::endl;
- player = new ServerRemotePlayer();
+ //infostream<<"Is a new player"<<std::endl;
+ player = new ServerRemotePlayer(this);
newplayer = true;
}
+ ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
+
// Load player
{
- infostream<<"Reading player "<<testplayer.getName()<<" from "
+ verbosestream<<"Reading player "<<testplayer.getName()<<" from "
<<path<<std::endl;
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
infostream<<"Failed to read "<<path<<std::endl;
continue;
}
- player->deSerialize(is);
+ srp->deSerialize(is);
+ srp->m_last_good_position = srp->getBasePosition();
+ srp->m_last_good_position_age = 0;
}
if(newplayer)
+ {
addPlayer(player);
+ }
}
}
}
}
-#if 0
-// This is probably very useless
-void spawnRandomObjects(MapBlock *block)
+struct ActiveABM
+{
+ ActiveBlockModifier *abm;
+ int chance;
+ std::set<content_t> required_neighbors;
+};
+
+class ABMHandler
{
- for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
- for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+private:
+ ServerEnvironment *m_env;
+ std::map<content_t, std::list<ActiveABM> > m_aabms;
+public:
+ ABMHandler(core::list<ABMWithState> &abms,
+ float dtime_s, ServerEnvironment *env,
+ bool use_timers):
+ m_env(env)
+ {
+ if(dtime_s < 0.001)
+ return;
+ INodeDefManager *ndef = env->getGameDef()->ndef();
+ for(core::list<ABMWithState>::Iterator
+ i = abms.begin(); i != abms.end(); i++){
+ ActiveBlockModifier *abm = i->abm;
+ float trigger_interval = abm->getTriggerInterval();
+ if(trigger_interval < 0.001)
+ trigger_interval = 0.001;
+ float actual_interval = dtime_s;
+ if(use_timers){
+ i->timer += dtime_s;
+ if(i->timer < trigger_interval)
+ continue;
+ i->timer -= trigger_interval;
+ actual_interval = trigger_interval;
+ }
+ ActiveABM aabm;
+ aabm.abm = abm;
+ float intervals = actual_interval / trigger_interval;
+ float chance = abm->getTriggerChance();
+ if(chance == 0)
+ chance = 1;
+ aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
+ if(aabm.chance == 0)
+ aabm.chance = 1;
+ // Trigger neighbors
+ std::set<std::string> required_neighbors_s
+ = abm->getRequiredNeighbors();
+ for(std::set<std::string>::iterator
+ i = required_neighbors_s.begin();
+ i != required_neighbors_s.end(); i++){
+ content_t c = ndef->getId(*i);
+ if(c == CONTENT_IGNORE)
+ continue;
+ aabm.required_neighbors.insert(c);
+ }
+ // Trigger contents
+ 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);
+ }
+ }
+ }
+ void apply(MapBlock *block)
{
- bool last_node_walkable = false;
- for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+ if(m_aabms.empty())
+ return;
+
+ ServerMap *map = &m_env->getServerMap();
+
+ 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(x0,y0,z0);
- MapNode n = block->getNodeNoEx(p);
- if(n.getContent() == CONTENT_IGNORE)
- continue;
- if(content_features(n).liquid_type != LIQUID_NONE)
- continue;
- if(content_features(n).walkable)
- {
- last_node_walkable = true;
+ 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;
- }
- if(last_node_walkable)
+
+ for(std::list<ActiveABM>::iterator
+ i = j->second.begin(); i != j->second.end(); i++)
{
- // If block contains light information
- if(content_features(n).param_type == CPT_LIGHT)
+ if(myrand() % i->chance != 0)
+ continue;
+
+ // Check neighbors
+ if(!i->required_neighbors.empty())
{
- if(n.getLight(LIGHTBANK_DAY) <= 5)
+ v3s16 p1;
+ for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
+ for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
+ for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
{
- 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->setChangedFlag();
+ if(p1 == p)
+ continue;
+ MapNode n = map->getNodeNoEx(p1);
+ content_t c = n.getContent();
+ std::set<content_t>::const_iterator k;
+ k = i->required_neighbors.find(c);
+ if(k != i->required_neighbors.end()){
+ goto neighbor_found;
}
}
+ // No required neighbor found
+ continue;
+ }
+neighbor_found:
+
+ // 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();
}
+
+ // 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);
}
- last_node_walkable = false;
}
}
-}
-#endif
+};
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;*/
+
+ // Set current time as timestamp
+ block->setTimestampNoChangedFlag(m_game_time);
- //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
+ /*infostream<<"ServerEnvironment::activateBlock(): block is "
+ <<dtime_s<<" seconds old."<<std::endl;*/
// Activate stored objects
activateObjects(block);
// Run node metadata
- bool changed = block->m_node_metadata.step((float)dtime_s);
+ bool changed = block->m_node_metadata->step((float)dtime_s);
if(changed)
{
MapEditEvent event;
event.p = block->getPos();
m_map->dispatchEvent(&event);
- block->setChangedFlag();
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "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() == CONTENT_MUD)
- {
- if(dtime_s > 300)
- {
- MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
- if(content_features(n_top).air_equivalent &&
- n_top.getLight(LIGHTBANK_DAY) >= 13)
- {
- n.setContent(CONTENT_GRASS);
- m_map->addNodeWithEvent(p, n);
- }
- }
- }
-#endif
+ /* Handle ActiveBlockModifiers */
+ ABMHandler abmhandler(m_abms, dtime_s, this, false);
+ abmhandler.apply(block);
+}
+
+void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
+{
+ m_abms.push_back(ABMWithState(abm));
+}
+
+std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
+{
+ std::set<u16> objects;
+ for(core::map<u16, ServerActiveObject*>::Iterator
+ i = m_active_objects.getIterator();
+ i.atEnd()==false; i++)
+ {
+ ServerActiveObject* obj = i.getNode()->getValue();
+ u16 id = i.getNode()->getKey();
+ v3f objectpos = obj->getBasePosition();
+ if(objectpos.getDistanceFrom(pos) > radius)
+ continue;
+ objects.insert(id);
}
+ return objects;
}
-static void getMob_dungeon_master(Settings &properties)
+void ServerEnvironment::clearAllObjects()
{
- 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);
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Removing all active objects"<<std::endl;
+ core::list<u16> objects_to_remove;
+ for(core::map<u16, ServerActiveObject*>::Iterator
+ i = m_active_objects.getIterator();
+ i.atEnd()==false; i++)
+ {
+ ServerActiveObject* obj = i.getNode()->getValue();
+ if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ continue;
+ u16 id = i.getNode()->getKey();
+ v3f objectpos = obj->getBasePosition();
+ // Delete static object if block is loaded
+ if(obj->m_static_exists){
+ MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
+ if(block){
+ block->m_static_objects.remove(id);
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "clearAllObjects");
+ obj->m_static_exists = false;
+ }
+ }
+ // If known by some client, don't delete immediately
+ if(obj->m_known_by_count > 0){
+ obj->m_pending_deactivation = true;
+ obj->m_removed = true;
+ continue;
+ }
+
+ // Tell the object about removal
+ obj->removingFromEnvironment();
+ // Deregister in scripting api
+ scriptapi_rm_object_reference(m_lua, obj);
+
+ // Delete active object
+ if(obj->environmentDeletes())
+ delete obj;
+ // Id to be removed from m_active_objects
+ objects_to_remove.push_back(id);
+ }
+ // Remove references from m_active_objects
+ for(core::list<u16>::Iterator i = objects_to_remove.begin();
+ i != objects_to_remove.end(); i++)
+ {
+ m_active_objects.remove(*i);
+ }
+
+ core::list<v3s16> loadable_blocks;
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Listing all loadable blocks"<<std::endl;
+ m_map->listAllLoadableBlocks(loadable_blocks);
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Done listing all loadable blocks: "
+ <<loadable_blocks.size()
+ <<", now clearing"<<std::endl;
+ u32 report_interval = loadable_blocks.size() / 10;
+ u32 num_blocks_checked = 0;
+ u32 num_blocks_cleared = 0;
+ u32 num_objs_cleared = 0;
+ for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
+ i != loadable_blocks.end(); i++)
+ {
+ v3s16 p = *i;
+ MapBlock *block = m_map->emergeBlock(p, false);
+ if(!block){
+ errorstream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Failed to emerge block "<<PP(p)<<std::endl;
+ continue;
+ }
+ u32 num_stored = block->m_static_objects.m_stored.size();
+ u32 num_active = block->m_static_objects.m_active.size();
+ if(num_stored != 0 || num_active != 0){
+ block->m_static_objects.m_stored.clear();
+ block->m_static_objects.m_active.clear();
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "clearAllObjects");
+ num_objs_cleared += num_stored + num_active;
+ num_blocks_cleared++;
+ }
+ num_blocks_checked++;
+
+ if(num_blocks_checked % report_interval == 0){
+ float percent = 100.0 * (float)num_blocks_checked /
+ loadable_blocks.size();
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Cleared "<<num_objs_cleared<<" objects"
+ <<" in "<<num_blocks_cleared<<" blocks ("
+ <<percent<<"%)"<<std::endl;
+ }
+ }
+ infostream<<"ServerEnvironment::clearAllObjects(): "
+ <<"Finished: Cleared "<<num_objs_cleared<<" objects"
+ <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
}
void ServerEnvironment::step(float dtime)
//TimeTaker timer("ServerEnv step");
- // Get some settings
- bool footprints = g_settings->getBool("footprints");
-
/*
Increment game time
*/
// Move
player->move(dtime, *m_map, 100*BS);
-
- /*
- Add footsteps to grass
- */
- if(footprints)
- {
- // Get node that is at BS/4 under player
- v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
- try{
- MapNode n = m_map->getNode(bottompos);
- if(n.getContent() == CONTENT_GRASS)
- {
- n.setContent(CONTENT_GRASS_FOOTSTEPS);
- m_map->setNode(bottompos, n);
- }
- }
- catch(InvalidPositionException &e)
- {
- }
- }
}
}
<<") became active"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
- if(block==NULL)
+ if(block==NULL){
+ // Block needs to be fetched first
+ m_emerger->queueBlockEmerge(p, false);
+ m_active_blocks.m_list.remove(p);
continue;
+ }
activateBlock(block);
}
// 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);
+ bool changed = block->m_node_metadata->step(dtime);
if(changed)
{
MapEditEvent event;
event.p = p;
m_map->dispatchEvent(&event);
- block->setChangedFlag();
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "node metadata modified in step");
}
}
}
- if(m_active_blocks_test_interval.step(dtime, 10.0))
+ const float abm_interval = 1.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;
+ ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
+ TimeTaker timer("modify in active blocks");
+ // Initialize handling of ActiveBlockModifiers
+ ABMHandler abmhandler(m_abms, abm_interval, this, true);
+
for(core::map<v3s16, bool>::Iterator
i = m_active_blocks.m_list.getIterator();
i.atEnd()==false; i++)
// 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();
-
- 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;
- }
- }
+ /* Handle ActiveBlockModifiers */
+ abmhandler.apply(block);
+ }
- 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 mud under proper lighting to grass
- */
- if(n.getContent() == CONTENT_MUD)
- {
- if(myrand()%20 == 0)
- {
- MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
- if(content_features(n_top).air_equivalent &&
- n_top.getLightBlend(getDayNightRatio()) >= 13)
- {
- n.setContent(CONTENT_GRASS);
- m_map->addNodeWithEvent(p, n);
- }
- }
- }
- /*
- Convert grass into mud if under something else than air
- */
- if(n.getContent() == CONTENT_GRASS)
- {
- //if(myrand()%20 == 0)
- {
- MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
- if(content_features(n_top).air_equivalent == false)
- {
- n.setContent(CONTENT_MUD);
- m_map->addNodeWithEvent(p, n);
- }
- }
- }
- /*
- Rats spawn around regular trees
- */
- if(n.getContent() == CONTENT_TREE ||
- n.getContent() == CONTENT_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() == CONTENT_GRASS &&
- n1.getContent() == CONTENT_AIR)
- {
- v3f pos = intToFloat(p1, BS);
- ServerActiveObject *obj = new RatSAO(this, 0, pos);
- addActiveObject(obj);
- }
- }
- }
- /*
- Fun things spawn in caves and dungeons
- */
- if(n.getContent() == CONTENT_STONE ||
- n.getContent() == CONTENT_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()) <= 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, 0, 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, 0, pos);
- addActiveObject(obj);
- }
- } else {
- actionstream<<"An oerkki spawns at "
- <<PP(p1)<<std::endl;
- ServerActiveObject *obj = new Oerkki1SAO(
- this, 0, pos);
- addActiveObject(obj);
- }
- }
- }
- }
- }
- /*
- Make trees from saplings!
- */
- if(n.getContent() == CONTENT_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);
- 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 max_time_ms = 200;
+ if(time_ms > max_time_ms){
+ infostream<<"WARNING: active block modifiers took "
+ <<time_ms<<"ms (longer than "
+ <<max_time_ms<<"ms)"<<std::endl;
}
}
+ /*
+ Step script environment (run global on_step())
+ */
+ scriptapi_environment_step(m_lua, dtime);
+
/*
Step active objects
*/
// This helps the objects to send data at the same time
bool send_recommended = false;
m_send_recommended_timer += dtime;
- if(m_send_recommended_timer > 0.10)
+ if(m_send_recommended_timer > getSendRecommendedInterval())
{
- m_send_recommended_timer = 0;
+ m_send_recommended_timer -= getSendRecommendedInterval();
send_recommended = true;
}
*/
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, 0, pos);
- //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1");
- //ServerActiveObject *obj = new RatSAO(this, 0, pos);
- //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos);
- //ServerActiveObject *obj = new FireflySAO(this, 0, 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, 0, pos, &properties);
- addActiveObject(obj);
- }
-#endif
-
- } // enable_experimental
}
ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
if(block)
{
block->m_static_objects.insert(0, s_obj);
- block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
+ block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
+ "addActiveObjectAsStatic");
succeeded = true;
}
else{
succeeded = false;
}
- delete obj;
+ if(obj->environmentDeletes())
+ delete obj;
return succeeded;
}
// Discard if removed
if(object->m_removed)
continue;
- // Discard if too far
- f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
- if(distance_f > radius_f)
- continue;
+ if(object->unlimitedTransferDistance() == false){
+ // Discard if too far
+ f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+ if(distance_f > radius_f)
+ continue;
+ }
// Discard if already on current_objects
core::map<u16, bool>::Node *n;
n = current_objects.find(id);
{
u16 id = i.getNode()->getKey();
ServerActiveObject *object = getActiveObject(id);
- if(object == NULL)
- {
+
+ if(object == NULL){
infostream<<"ServerEnvironment::getRemovedActiveObjects():"
<<" object in current_objects is NULL"<<std::endl;
+ removed_objects.insert(id, false);
+ continue;
}
- else if(object->m_removed == false)
+
+ if(object->m_removed)
{
- f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
- /*infostream<<"removed == false"
- <<"distance_f = "<<distance_f
- <<", radius_f = "<<radius_f<<std::endl;*/
- if(distance_f < radius_f)
- {
- // Not removed
- continue;
- }
+ removed_objects.insert(id, false);
+ continue;
}
- removed_objects.insert(id, false);
+
+ // If transfer distance is unlimited, don't remove
+ if(object->unlimitedTransferDistance())
+ continue;
+
+ f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+
+ if(distance_f >= radius_f)
+ {
+ removed_objects.insert(id, false);
+ continue;
+ }
+
+ // Not removed
}
}
{
errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"no free ids available"<<std::endl;
- delete object;
+ if(object->environmentDeletes())
+ delete object;
return 0;
}
object->setId(new_id);
{
errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"id is not free ("<<object->getId()<<")"<<std::endl;
- delete object;
+ if(object->environmentDeletes())
+ delete object;
return 0;
}
/*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
<<m_active_objects.size()<<" active objects."
<<std::endl;
- // Add static object to active static list of the block
- v3f objectpos = object->getBasePosition();
- std::string staticdata = object->getStaticData();
- StaticObject s_obj(object->getType(), objectpos, staticdata);
- // Add to the block where the object is located in
- v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
- MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
- if(block)
+ // Register reference in scripting api (must be done before post-init)
+ scriptapi_add_object_reference(m_lua, object);
+ // Post-initialize object
+ object->addedToEnvironment();
+
+ // Add static data to block
+ if(object->isStaticAllowed())
{
- block->m_static_objects.m_active.insert(object->getId(), s_obj);
- object->m_static_exists = true;
- object->m_static_block = blockpos;
+ // Add static object to active static list of the block
+ v3f objectpos = object->getBasePosition();
+ std::string staticdata = object->getStaticData();
+ StaticObject s_obj(object->getType(), objectpos, staticdata);
+ // Add to the block where the object is located in
+ v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
+ MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
+ if(block)
+ {
+ block->m_static_objects.m_active.insert(object->getId(), s_obj);
+ object->m_static_exists = true;
+ object->m_static_block = blockpos;
- if(set_changed)
- block->raiseModified(MOD_STATE_WRITE_NEEDED);
- }
- else{
- errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
- <<"could not find block for storing id="<<object->getId()
- <<" statically"<<std::endl;
+ if(set_changed)
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "addActiveObjectRaw");
+ }
+ else{
+ errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
+ <<"could not find block for storing id="<<object->getId()
+ <<" statically"<<std::endl;
+ }
}
-
+
return object->getId();
}
if(block)
{
block->m_static_objects.remove(id);
- block->raiseModified(MOD_STATE_WRITE_NEEDED);
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "removeRemovedObjects");
obj->m_static_exists = false;
}
}
if(obj->m_known_by_count > 0)
continue;
+ // Tell the object about removal
+ obj->removingFromEnvironment();
+ // Deregister in scripting api
+ scriptapi_rm_object_reference(m_lua, obj);
+
// Delete
- delete obj;
+ if(obj->environmentDeletes())
+ delete obj;
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
<<"activating objects of block "<<PP(block->getPos())
<<" ("<<block->m_static_objects.m_stored.size()
<<" objects)"<<std::endl;
- bool large_amount = (block->m_static_objects.m_stored.size() >= 51);
+ bool large_amount = (block->m_static_objects.m_stored.size() > 49);
if(large_amount){
errorstream<<"suspiciously large amount of objects detected: "
<<block->m_static_objects.m_stored.size()<<" in "
<<PP(block->getPos())
- <<"; not activating."<<std::endl;
+ <<"; removing all of them."<<std::endl;
+ // Clear stored list
+ block->m_static_objects.m_stored.clear();
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "stored list cleared in activateObjects due to "
+ "large amount of objects");
return;
}
// A list for objects that couldn't be converted to static for some
The objects have just been activated and moved from the stored
static list to the active static list.
As such, the block is essentially the same.
- Thus, do not call block->setChangedFlag().
+ Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
Otherwise there would be a huge amount of unnecessary I/O.
*/
}
i.atEnd()==false; i++)
{
ServerActiveObject* obj = i.getNode()->getValue();
+ assert(obj);
+
+ // Do not deactivate if static data creation not allowed
+ if(!force_delete && !obj->isStaticAllowed())
+ continue;
- // This shouldn't happen but check it
- if(obj == NULL)
- {
- errorstream<<"NULL object found in ServerEnvironment"
- <<std::endl;
- assert(0);
+ // If pending deactivation, let removeRemovedObjects() do it
+ if(!force_delete && obj->m_pending_deactivation)
continue;
- }
u16 id = i.getNode()->getKey();
v3f objectpos = obj->getBasePosition();
v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
// If block is active, don't remove
- if(m_active_blocks.contains(blockpos_o))
+ if(!force_delete && m_active_blocks.contains(blockpos_o))
continue;
verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
<<"deactivating object id="<<id<<" on inactive block "
<<PP(blockpos_o)<<std::endl;
+ // If known by some client, don't immediately delete.
+ bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
+
/*
Update the static data
*/
- // Delete old static object
- if(obj->m_static_exists)
+ if(obj->isStaticAllowed())
{
- MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
- if(block)
+ // Create new static object
+ std::string staticdata_new = obj->getStaticData();
+ StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
+
+ bool stays_in_same_block = false;
+ bool data_changed = true;
+
+ if(obj->m_static_exists){
+ if(obj->m_static_block == blockpos_o)
+ stays_in_same_block = true;
+
+ MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+
+ core::map<u16, StaticObject>::Node *n =
+ block->m_static_objects.m_active.find(id);
+ if(n){
+ StaticObject static_old = n->getValue();
+
+ float save_movem = obj->getMinimumSavedMovement();
+
+ if(static_old.data == staticdata_new &&
+ (static_old.pos - objectpos).getLength() < save_movem)
+ data_changed = false;
+ } else {
+ errorstream<<"ServerEnvironment::deactivateFarObjects(): "
+ <<"id="<<id<<" m_static_exists=true but "
+ <<"static data doesn't actually exist in "
+ <<PP(obj->m_static_block)<<std::endl;
+ }
+ }
+
+ bool shall_be_written = (!stays_in_same_block || data_changed);
+
+ // Delete old static object
+ if(obj->m_static_exists)
{
- block->m_static_objects.remove(id);
- block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
- obj->m_static_exists = false;
+ MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+ if(block)
+ {
+ block->m_static_objects.remove(id);
+ obj->m_static_exists = false;
+ // Only mark block as modified if data changed considerably
+ if(shall_be_written)
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "deactivateFarObjects: Static data "
+ "changed considerably");
+ }
}
- }
- // Create new static object
- std::string staticdata = obj->getStaticData();
- StaticObject s_obj(obj->getType(), objectpos, staticdata);
- // Add to the block where the object is located in
- v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
- // Get or generate the block
- MapBlock *block = m_map->emergeBlock(blockpos);
+ // Add to the block where the object is located in
+ v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
+ // Get or generate the block
+ MapBlock *block = m_map->emergeBlock(blockpos);
- if(block)
- {
- if(block->m_static_objects.m_stored.size() >= 50){
- errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
- <<" statically but block "<<PP(blockpos)
- <<" already contains over 50 objects."
- <<" Forcing delete."<<std::endl;
- force_delete = true;
- } else {
- block->m_static_objects.insert(0, s_obj);
- block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
- obj->m_static_exists = true;
- obj->m_static_block = block->getPos();
+ if(block)
+ {
+ if(block->m_static_objects.m_stored.size() >= 49){
+ errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
+ <<" statically but block "<<PP(blockpos)
+ <<" already contains "
+ <<block->m_static_objects.m_stored.size()
+ <<" (over 49) objects."
+ <<" Forcing delete."<<std::endl;
+ force_delete = true;
+ } else {
+ u16 new_id = pending_delete ? id : 0;
+ block->m_static_objects.insert(new_id, s_obj);
+
+ // Only mark block as modified if data changed considerably
+ if(shall_be_written)
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "deactivateFarObjects: Static data "
+ "changed considerably");
+
+ obj->m_static_exists = true;
+ obj->m_static_block = block->getPos();
+ }
+ }
+ else{
+ if(!force_delete){
+ errorstream<<"ServerEnv: Could not find or generate "
+ <<"a block for storing id="<<obj->getId()
+ <<" statically"<<std::endl;
+ continue;
+ }
}
- }
- else{
- errorstream<<"ServerEnv: Could not find or generate "
- <<"a block for storing id="<<obj->getId()
- <<" statically"<<std::endl;
- continue;
}
/*
- Delete active object if not known by some client,
- else set pending deactivation
+ If known by some client, set pending deactivation.
+ Otherwise delete it immediately.
*/
- // If known by some client, don't delete.
- if(obj->m_known_by_count > 0 && force_delete == false)
+ if(pending_delete && !force_delete)
{
verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
<<"object id="<<id<<" is known by clients"
verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
<<"object id="<<id<<" is not known by clients"
<<"; deleting"<<std::endl;
+
+ // Tell the object about removal
+ obj->removingFromEnvironment();
+ // Deregister in scripting api
+ scriptapi_rm_object_reference(m_lua, obj);
+
// Delete active object
- delete obj;
+ if(obj->environmentDeletes())
+ delete obj;
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
#ifndef SERVER
+#include "clientsimpleobject.h"
+
/*
ClientEnvironment
*/
-ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
+ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
+ ITextureSource *texturesource, IGameDef *gamedef,
+ IrrlichtDevice *irr):
m_map(map),
- m_smgr(smgr)
+ m_smgr(smgr),
+ m_texturesource(texturesource),
+ m_gamedef(gamedef),
+ m_irr(irr)
{
- assert(m_map);
- assert(m_smgr);
}
ClientEnvironment::~ClientEnvironment()
delete i.getNode()->getValue();
}
+ for(core::list<ClientSimpleObject*>::Iterator
+ i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
+ {
+ delete *i;
+ }
+
// Drop/delete map
m_map->drop();
}
// Get some settings
bool free_move = g_settings->getBool("free_move");
- bool footprints = g_settings->getBool("footprints");
// Get local player
LocalPlayer *lplayer = getLocalPlayer();
*/
bool is_climbing = lplayer->is_climbing;
- f32 player_speed = 0.001; // just some small value
- player_speed = lplayer->getSpeed().getLength();
+ f32 player_speed = lplayer->getSpeed().getLength();
/*
Maximum position increment
// Maximum time increment (for collision detection etc)
// time = distance / speed
- f32 dtime_max_increment = position_max_increment / player_speed;
+ f32 dtime_max_increment = 1;
+ if(player_speed > 0.001)
+ dtime_max_increment = position_max_increment / player_speed;
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
{
f32 damage_f = (info.speed - tolerance)/BS*factor;
u16 damage = (u16)(damage_f+0.5);
- if(lplayer->hp > damage)
- lplayer->hp -= damage;
- else
- lplayer->hp = 0;
-
- ClientEnvEvent event;
- event.type = CEE_PLAYER_DAMAGE;
- event.player_damage.amount = damage;
- m_client_event_queue.push_back(event);
+ damageLocalPlayer(damage, true);
}
}
}
u32 damage_per_second = 0;
damage_per_second = MYMAX(damage_per_second,
- content_features(n1).damage_per_second);
+ m_gamedef->ndef()->get(n1).damage_per_second);
damage_per_second = MYMAX(damage_per_second,
- content_features(n2).damage_per_second);
+ m_gamedef->ndef()->get(n2).damage_per_second);
damage_per_second = MYMAX(damage_per_second,
- content_features(n3).damage_per_second);
+ m_gamedef->ndef()->get(n3).damage_per_second);
if(damage_per_second != 0)
{
- ClientEnvEvent event;
- event.type = CEE_PLAYER_DAMAGE;
- event.player_damage.amount = damage_per_second;
- m_client_event_queue.push_back(event);
+ damageLocalPlayer(damage_per_second, true);
}
}
// Get node at head
v3s16 p = player->getLightPosition();
MapNode n = m_map->getNode(p);
- light = n.getLightBlend(getDayNightRatio());
+ light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
}
- catch(InvalidPositionException &e) {}
- player->updateLight(light);
-
- /*
- Add footsteps to grass
- */
- if(footprints)
- {
- // Get node that is at BS/4 under player
- v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
- try{
- MapNode n = m_map->getNode(bottompos);
- if(n.getContent() == CONTENT_GRASS)
- {
- n.setContent(CONTENT_GRASS_FOOTSTEPS);
- m_map->setNode(bottompos, n);
- // Update mesh on client
- if(m_map->mapType() == MAPTYPE_CLIENT)
- {
- v3s16 p_blocks = getNodeBlockPos(bottompos);
- MapBlock *b = m_map->getBlockNoCreate(p_blocks);
- //b->updateMesh(getDayNightRatio());
- b->setMeshExpired(true);
- }
- }
- }
- catch(InvalidPositionException &e)
- {
- }
+ catch(InvalidPositionException &e){
+ light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
}
+ player->updateLight(light);
}
/*
if(m_active_object_light_update_interval.step(dtime, 0.21))
{
// Update lighting
- //u8 light = LIGHT_MAX;
u8 light = 0;
try{
// Get node at head
v3s16 p = obj->getLightPosition();
MapNode n = m_map->getNode(p);
- light = n.getLightBlend(getDayNightRatio());
+ light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
+ }
+ catch(InvalidPositionException &e){
+ light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
}
- catch(InvalidPositionException &e) {}
obj->updateLight(light);
}
}
-}
-void ClientEnvironment::updateMeshes(v3s16 blockpos)
-{
- m_map->updateMeshes(blockpos, getDayNightRatio());
+ /*
+ Step and handle simple objects
+ */
+ for(core::list<ClientSimpleObject*>::Iterator
+ i = m_simple_objects.begin(); i != m_simple_objects.end();)
+ {
+ ClientSimpleObject *simple = *i;
+ core::list<ClientSimpleObject*>::Iterator cur = i;
+ i++;
+ simple->step(dtime);
+ if(simple->m_to_be_removed){
+ delete simple;
+ m_simple_objects.erase(cur);
+ }
+ }
}
-
-void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
+
+void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
{
- m_map->expireMeshes(only_daynight_diffed);
+ m_simple_objects.push_back(simple);
}
ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
infostream<<"ClientEnvironment::addActiveObject(): "
<<"added (id="<<object->getId()<<")"<<std::endl;
m_active_objects.insert(object->getId(), object);
- object->addToScene(m_smgr);
+ object->addToScene(m_smgr, m_texturesource, m_irr);
+ { // Update lighting immediately
+ u8 light = 0;
+ try{
+ // Get node at head
+ v3s16 p = object->getLightPosition();
+ MapNode n = m_map->getNode(p);
+ light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
+ }
+ catch(InvalidPositionException &e){
+ light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
+ }
+ object->updateLight(light);
+ }
return object->getId();
}
void ClientEnvironment::addActiveObject(u16 id, u8 type,
const std::string &init_data)
{
- ClientActiveObject* obj = ClientActiveObject::create(type);
+ ClientActiveObject* obj =
+ ClientActiveObject::create(type, m_gamedef, this);
if(obj == NULL)
{
infostream<<"ClientEnvironment::addActiveObject(): "
void ClientEnvironment::removeActiveObject(u16 id)
{
- infostream<<"ClientEnvironment::removeActiveObject(): "
+ verbosestream<<"ClientEnvironment::removeActiveObject(): "
<<"id="<<id<<std::endl;
ClientActiveObject* obj = getActiveObject(id);
if(obj == NULL)
Callbacks for activeobjects
*/
-void ClientEnvironment::damageLocalPlayer(u8 damage)
+void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
{
LocalPlayer *lplayer = getLocalPlayer();
assert(lplayer);
-
- if(lplayer->hp > damage)
- lplayer->hp -= damage;
- else
- lplayer->hp = 0;
+
+ if(handle_hp){
+ if(lplayer->hp > damage)
+ lplayer->hp -= damage;
+ else
+ lplayer->hp = 0;
+ }
ClientEnvEvent event;
event.type = CEE_PLAYER_DAMAGE;
event.player_damage.amount = damage;
+ event.player_damage.send_to_server = handle_hp;
m_client_event_queue.push_back(event);
}