Make node timers more efficient

This commit is contained in:
Ekdohibs 2016-03-21 12:58:52 +01:00 committed by paramat
parent 27aff22a9b
commit 559dd99469
8 changed files with 125 additions and 77 deletions

@ -186,7 +186,7 @@ void content_nodemeta_deserialize_legacy(std::istream &is,
meta->set(p, data); meta->set(p, data);
if(need_timer) if(need_timer)
timers->set(p, NodeTimer(1., 0.)); timers->set(NodeTimer(1., 0., p));
} }
} }

@ -1030,17 +1030,17 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
m_lbm_mgr.applyLBMs(this, block, stamp); m_lbm_mgr.applyLBMs(this, block, stamp);
// Run node timers // Run node timers
std::map<v3s16, NodeTimer> elapsed_timers = std::vector<NodeTimer> elapsed_timers =
block->m_node_timers.step((float)dtime_s); block->m_node_timers.step((float)dtime_s);
if(!elapsed_timers.empty()){ if (!elapsed_timers.empty()) {
MapNode n; MapNode n;
for(std::map<v3s16, NodeTimer>::iterator for (std::vector<NodeTimer>::iterator
i = elapsed_timers.begin(); i = elapsed_timers.begin();
i != elapsed_timers.end(); ++i){ i != elapsed_timers.end(); ++i){
n = block->getNodeNoEx(i->first); n = block->getNodeNoEx(i->position);
v3s16 p = i->first + block->getPosRelative(); v3s16 p = i->position + block->getPosRelative();
if(m_script->node_on_timer(p,n,i->second.elapsed)) if (m_script->node_on_timer(p, n, i->elapsed))
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
} }
} }
@ -1434,17 +1434,19 @@ void ServerEnvironment::step(float dtime)
MOD_REASON_BLOCK_EXPIRED); MOD_REASON_BLOCK_EXPIRED);
// Run node timers // Run node timers
std::map<v3s16, NodeTimer> elapsed_timers = std::vector<NodeTimer> elapsed_timers =
block->m_node_timers.step((float)dtime); block->m_node_timers.step((float)dtime);
if(!elapsed_timers.empty()){ if (!elapsed_timers.empty()) {
MapNode n; MapNode n;
for(std::map<v3s16, NodeTimer>::iterator for (std::vector<NodeTimer>::iterator
i = elapsed_timers.begin(); i = elapsed_timers.begin();
i != elapsed_timers.end(); ++i){ i != elapsed_timers.end(); ++i) {
n = block->getNodeNoEx(i->first); n = block->getNodeNoEx(i->position);
p = i->first + block->getPosRelative(); p = i->position + block->getPosRelative();
if(m_script->node_on_timer(p,n,i->second.elapsed)) if (m_script->node_on_timer(p, n, i->elapsed)) {
block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); block->setNodeTimer(NodeTimer(
i->timeout, 0, i->position));
}
} }
} }
} }

@ -2087,11 +2087,13 @@ NodeTimer Map::getNodeTimer(v3s16 p)
return NodeTimer(); return NodeTimer();
} }
NodeTimer t = block->m_node_timers.get(p_rel); NodeTimer t = block->m_node_timers.get(p_rel);
return t; NodeTimer nt(t.timeout, t.elapsed, p);
return nt;
} }
void Map::setNodeTimer(v3s16 p, NodeTimer t) void Map::setNodeTimer(const NodeTimer &t)
{ {
v3s16 p = t.position;
v3s16 blockpos = getNodeBlockPos(p); v3s16 blockpos = getNodeBlockPos(p);
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE; v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
MapBlock *block = getBlockNoCreateNoEx(blockpos); MapBlock *block = getBlockNoCreateNoEx(blockpos);
@ -2105,7 +2107,8 @@ void Map::setNodeTimer(v3s16 p, NodeTimer t)
<<std::endl; <<std::endl;
return; return;
} }
block->m_node_timers.set(p_rel, t); NodeTimer nt(t.timeout, t.elapsed, p_rel);
block->m_node_timers.set(nt);
} }
void Map::removeNodeTimer(v3s16 p) void Map::removeNodeTimer(v3s16 p)

@ -327,7 +327,7 @@ public:
*/ */
NodeTimer getNodeTimer(v3s16 p); NodeTimer getNodeTimer(v3s16 p);
void setNodeTimer(v3s16 p, NodeTimer t); void setNodeTimer(const NodeTimer &t);
void removeNodeTimer(v3s16 p); void removeNodeTimer(v3s16 p);
/* /*

@ -488,9 +488,9 @@ public:
m_node_timers.remove(p); m_node_timers.remove(p);
} }
inline void setNodeTimer(v3s16 p, NodeTimer t) inline void setNodeTimer(const NodeTimer &t)
{ {
m_node_timers.set(p,t); m_node_timers.set(t);
} }
inline void clearNodeTimers() inline void clearNodeTimers()

@ -47,36 +47,38 @@ void NodeTimerList::serialize(std::ostream &os, u8 map_format_version) const
{ {
if (map_format_version == 24) { if (map_format_version == 24) {
// Version 0 is a placeholder for "nothing to see here; go away." // Version 0 is a placeholder for "nothing to see here; go away."
if (m_data.empty()) { if (m_timers.empty()) {
writeU8(os, 0); // version writeU8(os, 0); // version
return; return;
} }
writeU8(os, 1); // version writeU8(os, 1); // version
writeU16(os, m_data.size()); writeU16(os, m_timers.size());
} }
if (map_format_version >= 25) { if (map_format_version >= 25) {
writeU8(os, 2 + 4 + 4); // length of the data for a single timer writeU8(os, 2 + 4 + 4); // length of the data for a single timer
writeU16(os, m_data.size()); writeU16(os, m_timers.size());
} }
for (std::map<v3s16, NodeTimer>::const_iterator for (std::multimap<double, NodeTimer>::const_iterator
i = m_data.begin(); i = m_timers.begin();
i != m_data.end(); ++i) { i != m_timers.end(); ++i) {
v3s16 p = i->first;
NodeTimer t = i->second; NodeTimer t = i->second;
NodeTimer nt = NodeTimer(t.timeout,
t.timeout - (f32)(i->first - m_time), t.position);
v3s16 p = t.position;
u16 p16 = p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE + p.Y * MAP_BLOCKSIZE + p.X; u16 p16 = p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE + p.Y * MAP_BLOCKSIZE + p.X;
writeU16(os, p16); writeU16(os, p16);
t.serialize(os); nt.serialize(os);
} }
} }
void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version) void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
{ {
m_data.clear(); clear();
if(map_format_version == 24){ if (map_format_version == 24) {
u8 timer_version = readU8(is); u8 timer_version = readU8(is);
if(timer_version == 0) if(timer_version == 0)
return; return;
@ -84,7 +86,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
throw SerializationError("unsupported NodeTimerList version"); throw SerializationError("unsupported NodeTimerList version");
} }
if(map_format_version >= 25){ if (map_format_version >= 25) {
u8 timer_data_len = readU8(is); u8 timer_data_len = readU8(is);
if(timer_data_len != 2+4+4) if(timer_data_len != 2+4+4)
throw SerializationError("unsupported NodeTimer data length"); throw SerializationError("unsupported NodeTimer data length");
@ -92,8 +94,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
u16 count = readU16(is); u16 count = readU16(is);
for(u16 i=0; i<count; i++) for (u16 i = 0; i < count; i++) {
{
u16 p16 = readU16(is); u16 p16 = readU16(is);
v3s16 p; v3s16 p;
@ -103,11 +104,10 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
p16 &= MAP_BLOCKSIZE - 1; p16 &= MAP_BLOCKSIZE - 1;
p.X = p16; p.X = p16;
NodeTimer t; NodeTimer t(p);
t.deSerialize(is); t.deSerialize(is);
if(t.timeout <= 0) if (t.timeout <= 0) {
{
warningstream<<"NodeTimerList::deSerialize(): " warningstream<<"NodeTimerList::deSerialize(): "
<<"invalid data at position" <<"invalid data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring." <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
@ -115,8 +115,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
continue; continue;
} }
if(m_data.find(p) != m_data.end()) if (m_iterators.find(p) != m_iterators.end()) {
{
warningstream<<"NodeTimerList::deSerialize(): " warningstream<<"NodeTimerList::deSerialize(): "
<<"already set data at position" <<"already set data at position"
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring." <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
@ -124,31 +123,30 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version)
continue; continue;
} }
m_data.insert(std::make_pair(p, t)); insert(t);
} }
} }
std::map<v3s16, NodeTimer> NodeTimerList::step(float dtime) std::vector<NodeTimer> NodeTimerList::step(float dtime)
{ {
std::map<v3s16, NodeTimer> elapsed_timers; std::vector<NodeTimer> elapsed_timers;
// Increment timers m_time += dtime;
for(std::map<v3s16, NodeTimer>::iterator if (m_next_trigger_time == -1. || m_time < m_next_trigger_time) {
i = m_data.begin(); return elapsed_timers;
i != m_data.end(); ++i){ }
v3s16 p = i->first; std::multimap<double, NodeTimer>::iterator i = m_timers.begin();
// Process timers
for (; i != m_timers.end() && i->first <= m_time; ++i) {
NodeTimer t = i->second; NodeTimer t = i->second;
t.elapsed += dtime; t.elapsed = t.timeout + (f32)(m_time - i->first);
if(t.elapsed >= t.timeout) elapsed_timers.push_back(t);
elapsed_timers.insert(std::make_pair(p, t)); m_iterators.erase(t.position);
else
i->second = t;
} }
// Delete elapsed timers // Delete elapsed timers
for(std::map<v3s16, NodeTimer>::const_iterator m_timers.erase(m_timers.begin(), i);
i = elapsed_timers.begin(); if (m_timers.empty())
i != elapsed_timers.end(); ++i){ m_next_trigger_time = -1.;
v3s16 p = i->first; else
m_data.erase(p); m_next_trigger_time = m_timers.begin()->first;
}
return elapsed_timers; return elapsed_timers;
} }

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v3d.h" #include "irr_v3d.h"
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <vector>
/* /*
NodeTimer provides per-node timed callback functionality. NodeTimer provides per-node timed callback functionality.
@ -36,8 +37,10 @@ class NodeTimer
{ {
public: public:
NodeTimer(): timeout(0.), elapsed(0.) {} NodeTimer(): timeout(0.), elapsed(0.) {}
NodeTimer(f32 timeout_, f32 elapsed_): NodeTimer(const v3s16 &position_):
timeout(timeout_), elapsed(elapsed_) {} timeout(0.), elapsed(0.), position(position_) {}
NodeTimer(f32 timeout_, f32 elapsed_, v3s16 position_):
timeout(timeout_), elapsed(elapsed_), position(position_) {}
~NodeTimer() {} ~NodeTimer() {}
void serialize(std::ostream &os) const; void serialize(std::ostream &os) const;
@ -45,6 +48,7 @@ public:
f32 timeout; f32 timeout;
f32 elapsed; f32 elapsed;
v3s16 position;
}; };
/* /*
@ -54,37 +58,78 @@ public:
class NodeTimerList class NodeTimerList
{ {
public: public:
NodeTimerList() {} NodeTimerList(): m_next_trigger_time(-1.), m_time(0.) {}
~NodeTimerList() {} ~NodeTimerList() {}
void serialize(std::ostream &os, u8 map_format_version) const; void serialize(std::ostream &os, u8 map_format_version) const;
void deSerialize(std::istream &is, u8 map_format_version); void deSerialize(std::istream &is, u8 map_format_version);
// Get timer // Get timer
NodeTimer get(v3s16 p){ NodeTimer get(const v3s16 &p) {
std::map<v3s16, NodeTimer>::iterator n = m_data.find(p); std::map<v3s16, std::multimap<double, NodeTimer>::iterator>::iterator n =
if(n == m_data.end()) m_iterators.find(p);
if (n == m_iterators.end())
return NodeTimer(); return NodeTimer();
return n->second; NodeTimer t = n->second->second;
t.elapsed = t.timeout - (n->second->first - m_time);
return t;
} }
// Deletes timer // Deletes timer
void remove(v3s16 p){ void remove(v3s16 p) {
m_data.erase(p); std::map<v3s16, std::multimap<double, NodeTimer>::iterator>::iterator n =
m_iterators.find(p);
if(n != m_iterators.end()) {
double removed_time = n->second->first;
m_timers.erase(n->second);
m_iterators.erase(n);
// Yes, this is float equality, but it is not a problem
// since we only test equality of floats as an ordered type
// and thus we never lose precision
if (removed_time == m_next_trigger_time) {
if (m_timers.empty())
m_next_trigger_time = -1.;
else
m_next_trigger_time = m_timers.begin()->first;
}
}
}
// Undefined behaviour if there already is a timer
void insert(NodeTimer timer) {
v3s16 p = timer.position;
double trigger_time = m_time + (double)(timer.timeout - timer.elapsed);
std::multimap<double, NodeTimer>::iterator it =
m_timers.insert(std::pair<double, NodeTimer>(
trigger_time, timer
));
m_iterators.insert(
std::pair<v3s16, std::multimap<double, NodeTimer>::iterator>(p, it));
if (m_next_trigger_time == -1. || trigger_time < m_next_trigger_time)
m_next_trigger_time = trigger_time;
} }
// Deletes old timer and sets a new one // Deletes old timer and sets a new one
void set(v3s16 p, NodeTimer t){ inline void set(const NodeTimer &timer) {
m_data[p] = t; remove(timer.position);
insert(timer);
} }
// Deletes all timers // Deletes all timers
void clear(){ void clear() {
m_data.clear(); m_timers.clear();
m_iterators.clear();
m_next_trigger_time = -1.;
} }
// A step in time. Returns map of elapsed timers. inline double getNextTriggerTime() {
std::map<v3s16, NodeTimer> step(float dtime); return m_next_trigger_time;
}
// Move forward in time, returns elapsed timers
std::vector<NodeTimer> step(float dtime);
private: private:
std::map<v3s16, NodeTimer> m_data; std::multimap<double, NodeTimer> m_timers;
std::map<v3s16, std::multimap<double, NodeTimer>::iterator> m_iterators;
double m_next_trigger_time;
double m_time;
}; };
#endif #endif

@ -45,7 +45,7 @@ int NodeTimerRef::l_set(lua_State *L)
if(env == NULL) return 0; if(env == NULL) return 0;
f32 t = luaL_checknumber(L,2); f32 t = luaL_checknumber(L,2);
f32 e = luaL_checknumber(L,3); f32 e = luaL_checknumber(L,3);
env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e)); env->getMap().setNodeTimer(NodeTimer(t, e, o->m_p));
return 0; return 0;
} }
@ -56,7 +56,7 @@ int NodeTimerRef::l_start(lua_State *L)
ServerEnvironment *env = o->m_env; ServerEnvironment *env = o->m_env;
if(env == NULL) return 0; if(env == NULL) return 0;
f32 t = luaL_checknumber(L,2); f32 t = luaL_checknumber(L,2);
env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0)); env->getMap().setNodeTimer(NodeTimer(t, 0, o->m_p));
return 0; return 0;
} }