diff options
author | est31 <MTest31@outlook.com> | 2016-02-09 07:08:31 +0100 |
---|---|---|
committer | est31 <MTest31@outlook.com> | 2016-03-07 19:54:26 +0100 |
commit | d494733839e9cf6cb557462326ed21e7a58816c7 (patch) | |
tree | 07ead00672b0096aa2a60938b769bf030bba2951 /src/environment.cpp | |
parent | 88fbe7ca1e5451851ee0c7ab5524c39a7bb703c2 (diff) | |
download | minetest-d494733839e9cf6cb557462326ed21e7a58816c7.tar.gz minetest-d494733839e9cf6cb557462326ed21e7a58816c7.tar.bz2 minetest-d494733839e9cf6cb557462326ed21e7a58816c7.zip |
Add minetest.register_lbm() to run code on block load only
Diffstat (limited to 'src/environment.cpp')
-rw-r--r-- | src/environment.cpp | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/src/environment.cpp b/src/environment.cpp index 081079a79..6da376a15 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -48,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" + Environment::Environment(): m_time_of_day_speed(0), m_time_of_day(9000), @@ -271,6 +273,223 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_): } /* + LBMManager +*/ + +void LBMContentMapping::deleteContents() +{ + for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin(); + it != lbm_list.end(); ++it) { + delete *it; + } +} + +void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) +{ + // Add the lbm_def to the LBMContentMapping. + // Unknown names get added to the global NameIdMapping. + INodeDefManager *nodedef = gamedef->ndef(); + + lbm_list.push_back(lbm_def); + + for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin(); + it != lbm_def->trigger_contents.end(); ++it) { + std::set<content_t> c_ids; + bool found = nodedef->getIds(*it, c_ids); + if (!found) { + content_t c_id = gamedef->allocateUnknownNodeId(*it); + if (c_id == CONTENT_IGNORE) { + // Seems it can't be allocated. + warningstream << "Could not internalize node name \"" << *it + << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; + continue; + } + c_ids.insert(c_id); + } + + for (std::set<content_t>::const_iterator iit = + c_ids.begin(); iit != c_ids.end(); ++iit) { + content_t c_id = *iit; + map[c_id].push_back(lbm_def); + } + } +} + +const std::vector<LoadingBlockModifierDef *> * + LBMContentMapping::lookup(content_t c) const +{ + container_map::const_iterator it = map.find(c); + if (it == map.end()) + return NULL; + // This first dereferences the iterator, returning + // a std::vector<LoadingBlockModifierDef *> + // reference, then we convert it to a pointer. + return &(it->second); +} + +LBMManager::~LBMManager() +{ + for (std::map<std::string, LoadingBlockModifierDef *>::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + delete it->second; + } + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + (it->second).deleteContents(); + } +} + +void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) +{ + // Precondition, in query mode the map isn't used anymore + FATAL_ERROR_IF(m_query_mode == true, + "attempted to modify LBMManager in query mode"); + + if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { + throw ModError("Error adding LBM \"" + lbm_def->name + + "\": Does not follow naming conventions: " + "Only chararacters [a-z0-9_:] are allowed."); + } + + m_lbm_defs[lbm_def->name] = lbm_def; +} + +void LBMManager::loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now) +{ + m_query_mode = true; + + // name -> time map. + // Storing it in a map first instead of + // handling the stuff directly in the loop + // removes all duplicate entries. + // TODO make this std::unordered_map + std::map<std::string, u32> introduction_times; + + /* + The introduction times string consists of name~time entries, + with each entry terminated by a semicolon. The time is decimal. + */ + + size_t idx = 0; + size_t idx_new; + while ((idx_new = times.find(";", idx)) != std::string::npos) { + std::string entry = times.substr(idx, idx_new - idx); + std::vector<std::string> components = str_split(entry, '~'); + if (components.size() != 2) + throw SerializationError("Introduction times entry \"" + + entry + "\" requires exactly one '~'!"); + const std::string &name = components[0]; + u32 time = from_string<u32>(components[1]); + introduction_times[name] = time; + idx = idx_new + 1; + } + + // Put stuff from introduction_times into m_lbm_lookup + for (std::map<std::string, u32>::const_iterator it = introduction_times.begin(); + it != introduction_times.end(); ++it) { + const std::string &name = it->first; + u32 time = it->second; + + std::map<std::string, LoadingBlockModifierDef *>::iterator def_it = + m_lbm_defs.find(name); + if (def_it == m_lbm_defs.end()) { + // This seems to be an LBM entry for + // an LBM we haven't loaded. Discard it. + continue; + } + LoadingBlockModifierDef *lbm_def = def_it->second; + if (lbm_def->run_at_every_load) { + // This seems to be an LBM entry for + // an LBM that runs at every load. + // Don't add it just yet. + continue; + } + + m_lbm_lookup[time].addLBM(lbm_def, gamedef); + + // Erase the entry so that we know later + // what elements didn't get put into m_lbm_lookup + m_lbm_defs.erase(name); + } + + // Now also add the elements from m_lbm_defs to m_lbm_lookup + // that weren't added in the previous step. + // They are introduced first time to this world, + // or are run at every load (introducement time hardcoded to U32_MAX). + + LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now]; + LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX]; + + for (std::map<std::string, LoadingBlockModifierDef *>::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + if (it->second->run_at_every_load) { + lbms_running_always.addLBM(it->second, gamedef); + } else { + lbms_we_introduce_now.addLBM(it->second, gamedef); + } + } + + // Clear the list, so that we don't delete remaining elements + // twice in the destructor + m_lbm_defs.clear(); +} + +std::string LBMManager::createIntroductionTimesString() +{ + // Precondition, we must be in query mode + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + + std::ostringstream oss; + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + u32 time = it->first; + std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list; + for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin(); + iit != lbm_list.end(); ++iit) { + // Don't add if the LBM runs at every load, + // then introducement time is hardcoded + // and doesn't need to be stored + if ((*iit)->run_at_every_load) + continue; + oss << (*iit)->name << "~" << time << ";"; + } + } + return oss.str(); +} + +void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) +{ + // Precondition, we need m_lbm_lookup to be initialized + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + v3s16 pos_of_block = block->getPosRelative(); + v3s16 pos; + MapNode n; + content_t c; + lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp); + for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) + for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) + for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) + { + n = block->getNodeNoEx(pos); + c = n.getContent(); + for (LBMManager::lbm_lookup_map::const_iterator iit = it; + iit != m_lbm_lookup.end(); ++iit) { + const std::vector<LoadingBlockModifierDef *> *lbm_list = + iit->second.lookup(c); + if (!lbm_list) + continue; + for (std::vector<LoadingBlockModifierDef *>::const_iterator iit = + lbm_list->begin(); iit != lbm_list->end(); ++iit) { + (*iit)->trigger(env, pos + pos_of_block, n); + } + } + } +} + +/* ActiveBlockList */ @@ -505,6 +724,9 @@ void ServerEnvironment::saveMeta() args.setU64("game_time", m_game_time); args.setU64("time_of_day", getTimeOfDay()); args.setU64("last_clear_objects_time", m_last_clear_objects_time); + args.setU64("lbm_introduction_times_version", 1); + args.set("lbm_introduction_times", + m_lbm_mgr.createIntroductionTimesString()); args.writeLines(ss); ss<<"EnvArgsEnd\n"; @@ -555,6 +777,26 @@ void ServerEnvironment::loadMeta() // If missing, do as if clearObjects was never called m_last_clear_objects_time = 0; } + + std::string lbm_introduction_times = ""; + try { + u64 ver = args.getU64("lbm_introduction_times_version"); + if (ver == 1) { + lbm_introduction_times = args.get("lbm_introduction_times"); + } else { + infostream << "ServerEnvironment::loadMeta(): Non-supported" + << " introduction time version " << ver << std::endl; + } + } catch (SettingNotFoundException &e) { + // No problem, this is expected. Just continue with an empty string + } + m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + +} + +void ServerEnvironment::loadDefaultMeta() +{ + m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time); } struct ActiveABM @@ -770,6 +1012,9 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) // Activate stored objects activateObjects(block, dtime_s); + /* Handle LoadingBlockModifiers */ + m_lbm_mgr.applyLBMs(this, block, stamp); + // Run node timers std::map<v3s16, NodeTimer> elapsed_timers = block->m_node_timers.step((float)dtime_s); @@ -795,6 +1040,11 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) m_abms.push_back(ABMWithState(abm)); } +void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) +{ + m_lbm_mgr.addLBMDef(lbm); +} + bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { INodeDefManager *ndef = m_gamedef->ndef(); |