aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nodedef.cpp61
-rw-r--r--src/nodedef.h9
-rw-r--r--src/script/cpp_api/s_env.cpp12
-rw-r--r--src/script/lua_api/l_env.cpp27
-rw-r--r--src/script/lua_api/l_env.h12
-rw-r--r--src/serverenvironment.cpp22
-rw-r--r--src/serverenvironment.h4
7 files changed, 64 insertions, 83 deletions
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index e547e31b1..d13f93068 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h"
#include "mapnode.h"
#include <fstream> // Used in applyTextureOverrides()
+#include <algorithm>
/*
NodeBox
@@ -485,7 +486,7 @@ void ContentFeatures::deSerialize(std::istream &is)
u16 connects_to_size = readU16(is);
connects_to_ids.clear();
for (u16 i = 0; i < connects_to_size; i++)
- connects_to_ids.insert(readU16(is));
+ connects_to_ids.push_back(readU16(is));
post_effect_color.setAlpha(readU8(is));
post_effect_color.setRed(readU8(is));
post_effect_color.setGreen(readU8(is));
@@ -845,7 +846,7 @@ public:
inline virtual const ContentFeatures& get(const MapNode &n) const;
virtual bool getId(const std::string &name, content_t &result) const;
virtual content_t getId(const std::string &name) const;
- virtual bool getIds(const std::string &name, std::set<content_t> &result) const;
+ virtual bool getIds(const std::string &name, std::vector<content_t> &result) const;
virtual const ContentFeatures& get(const std::string &name) const;
content_t allocateId();
virtual content_t set(const std::string &name, const ContentFeatures &def);
@@ -892,10 +893,10 @@ private:
std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases;
- // A mapping from groups to a list of content_ts (and their levels)
- // that belong to it. Necessary for a direct lookup in getIds().
+ // A mapping from groups to a vector of content_ts that belong to it.
+ // Necessary for a direct lookup in getIds().
// Note: Not serialized.
- std::unordered_map<std::string, GroupItems> m_group_to_items;
+ std::unordered_map<std::string, std::vector<content_t>> m_group_to_items;
// Next possibly free id
content_t m_next_id;
@@ -1037,28 +1038,25 @@ content_t CNodeDefManager::getId(const std::string &name) const
bool CNodeDefManager::getIds(const std::string &name,
- std::set<content_t> &result) const
+ std::vector<content_t> &result) const
{
//TimeTaker t("getIds", NULL, PRECISION_MICRO);
if (name.substr(0,6) != "group:") {
content_t id = CONTENT_IGNORE;
bool exists = getId(name, id);
if (exists)
- result.insert(id);
+ result.push_back(id);
return exists;
}
std::string group = name.substr(6);
- std::unordered_map<std::string, GroupItems>::const_iterator
+ std::unordered_map<std::string, std::vector<content_t>>::const_iterator
i = m_group_to_items.find(group);
if (i == m_group_to_items.end())
return true;
- const GroupItems &items = i->second;
- for (const auto &item : items) {
- if (item.second != 0)
- result.insert(item.first);
- }
+ const std::vector<content_t> &items = i->second;
+ result.insert(result.end(), items.begin(), items.end());
//printf("getIds: %dus\n", t.stop());
return true;
}
@@ -1247,15 +1245,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
// belongs to when a node is re-registered
for (const auto &group : def.groups) {
const std::string &group_name = group.first;
-
- std::unordered_map<std::string, GroupItems>::iterator
- j = m_group_to_items.find(group_name);
- if (j == m_group_to_items.end()) {
- m_group_to_items[group_name].emplace_back(id, group.second);
- } else {
- GroupItems &items = j->second;
- items.emplace_back(id, group.second);
- }
+ m_group_to_items[group_name].push_back(id);
}
return id;
}
@@ -1283,16 +1273,10 @@ void CNodeDefManager::removeNode(const std::string &name)
}
// Erase node content from all groups it belongs to
- for (std::unordered_map<std::string, GroupItems>::iterator iter_groups =
+ for (std::unordered_map<std::string, std::vector<content_t>>::iterator iter_groups =
m_group_to_items.begin(); iter_groups != m_group_to_items.end();) {
- GroupItems &items = iter_groups->second;
- for (GroupItems::iterator iter_groupitems = items.begin();
- iter_groupitems != items.end();) {
- if (iter_groupitems->first == id)
- items.erase(iter_groupitems++);
- else
- ++iter_groupitems;
- }
+ std::vector<content_t> &items = iter_groups->second;
+ items.erase(std::remove(items.begin(), items.end(), id), items.end());
// Check if group is empty
if (items.empty())
@@ -1545,9 +1529,8 @@ void CNodeDefManager::mapNodeboxConnections()
if (f.drawtype != NDT_NODEBOX || f.node_box.type != NODEBOX_CONNECTED)
continue;
- for (std::vector<std::string>::const_iterator it = f.connects_to.begin();
- it != f.connects_to.end(); ++it) {
- getIds(*it, f.connects_to_ids);
+ for (const std::string &name : f.connects_to) {
+ getIds(name, f.connects_to_ids);
}
}
}
@@ -1560,14 +1543,14 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
return false;
// lookup target in connected set
- if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
+ if (!CONTAINS(f1.connects_to_ids, to.param0))
return false;
const ContentFeatures &f2 = get(to);
if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED))
// ignores actually looking if back connection exists
- return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end());
+ return CONTAINS(f2.connects_to_ids, from.param0);
// does to node declare usable faces?
if (f2.connect_sides > 0) {
@@ -1686,11 +1669,7 @@ bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
success = false;
}
} else {
- std::set<content_t> cids;
- std::set<content_t>::iterator it;
- m_ndef->getIds(name, cids);
- for (it = cids.begin(); it != cids.end(); ++it)
- result_out->push_back(*it);
+ m_ndef->getIds(name, *result_out);
}
}
diff --git a/src/nodedef.h b/src/nodedef.h
index 853b9bddd..23100a2ed 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <iostream>
#include <map>
-#include <list>
#include "mapnode.h"
#ifndef SERVER
#include "client/tile.h"
@@ -42,8 +41,6 @@ class IShaderSource;
class IGameDef;
class NodeResolver;
-typedef std::list<std::pair<content_t, int> > GroupItems;
-
enum ContentParamType
{
CPT_NONE,
@@ -290,7 +287,7 @@ struct ContentFeatures
// for NDT_CONNECTED pairing
u8 connect_sides;
std::vector<std::string> connects_to;
- std::set<content_t> connects_to_ids;
+ std::vector<content_t> connects_to_ids;
// Post effect color, drawn when the camera is inside the node.
video::SColor post_effect_color;
// Flowing liquid or snow, value = default level
@@ -424,7 +421,7 @@ public:
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
// returns false if node name not found, true otherwise
- virtual bool getIds(const std::string &name, std::set<content_t> &result)
+ virtual bool getIds(const std::string &name, std::vector<content_t> &result)
const=0;
virtual const ContentFeatures &get(const std::string &name) const=0;
@@ -452,7 +449,7 @@ public:
// If not found, returns CONTENT_IGNORE
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
- virtual bool getIds(const std::string &name, std::set<content_t> &result)
+ virtual bool getIds(const std::string &name, std::vector<content_t> &result)
const=0;
// If not found, returns the features of CONTENT_UNKNOWN
virtual const ContentFeatures &get(const std::string &name) const=0;
diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp
index b1404bf22..517892b03 100644
--- a/src/script/cpp_api/s_env.cpp
+++ b/src/script/cpp_api/s_env.cpp
@@ -108,7 +108,7 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
int id = lua_tonumber(L, -2);
int current_abm = lua_gettop(L);
- std::set<std::string> trigger_contents;
+ std::vector<std::string> trigger_contents;
lua_getfield(L, current_abm, "nodenames");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
@@ -116,16 +116,16 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
while (lua_next(L, table)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
- trigger_contents.insert(lua_tostring(L, -1));
+ trigger_contents.push_back(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
- trigger_contents.insert(lua_tostring(L, -1));
+ trigger_contents.push_back(lua_tostring(L, -1));
}
lua_pop(L, 1);
- std::set<std::string> required_neighbors;
+ std::vector<std::string> required_neighbors;
lua_getfield(L, current_abm, "neighbors");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
@@ -133,12 +133,12 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
while (lua_next(L, table)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
- required_neighbors.insert(lua_tostring(L, -1));
+ required_neighbors.push_back(lua_tostring(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
- required_neighbors.insert(lua_tostring(L, -1));
+ required_neighbors.push_back(lua_tostring(L, -1));
}
lua_pop(L, 1);
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index be92365ac..1d0716484 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -719,7 +719,7 @@ int ModApiEnvMod::l_find_node_near(lua_State *L)
INodeDefManager *ndef = getGameDef(L)->ndef();
v3s16 pos = read_v3s16(L, 1);
int radius = luaL_checkinteger(L, 2);
- std::set<content_t> filter;
+ std::vector<content_t> filter;
if (lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
@@ -748,7 +748,7 @@ int ModApiEnvMod::l_find_node_near(lua_State *L)
for (const v3s16 &i : list) {
v3s16 p = pos + i;
content_t c = env->getMap().getNodeNoEx(p).getContent();
- if (filter.count(c) != 0) {
+ if (CONTAINS(filter, c)) {
push_v3s16(L, p);
return 1;
}
@@ -780,7 +780,7 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
return 0;
}
- std::set<content_t> filter;
+ std::vector<content_t> filter;
if (lua_istable(L, 3)) {
lua_pushnil(L);
while (lua_next(L, 3) != 0) {
@@ -794,7 +794,8 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
ndef->getIds(lua_tostring(L, 3), filter);
}
- std::unordered_map<content_t, u32> individual_count;
+ std::vector<u32> individual_count;
+ individual_count.resize(filter.size());
lua_newtable(L);
u64 i = 0;
@@ -803,16 +804,20 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
for (s16 z = minp.Z; z <= maxp.Z; z++) {
v3s16 p(x, y, z);
content_t c = env->getMap().getNodeNoEx(p).getContent();
- if (filter.count(c) != 0) {
+
+ std::vector<content_t>::iterator it = std::find(filter.begin(), filter.end(), c);
+ if (it != filter.end()) {
push_v3s16(L, p);
lua_rawseti(L, -2, ++i);
- individual_count[c]++;
+
+ u32 filt_index = it - filter.begin();
+ individual_count[filt_index]++;
}
}
lua_newtable(L);
- for (content_t it : filter) {
- lua_pushnumber(L, individual_count[it]);
- lua_setfield(L, -2, ndef->get(it).name.c_str());
+ for (u32 i = 0; i < filter.size(); i++) {
+ lua_pushnumber(L, individual_count[i]);
+ lua_setfield(L, -2, ndef->get(filter[i]).name.c_str());
}
return 2;
}
@@ -847,7 +852,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
return 0;
}
- std::set<content_t> filter;
+ std::vector<content_t> filter;
if (lua_istable(L, 3)) {
lua_pushnil(L);
@@ -873,7 +878,7 @@ int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
v3s16 psurf(x, y + 1, z);
content_t csurf = env->getMap().getNodeNoEx(psurf).getContent();
if (c != CONTENT_AIR && csurf == CONTENT_AIR &&
- filter.count(c) != 0) {
+ CONTAINS(filter, c)) {
push_v3s16(L, v3s16(x, y, z));
lua_rawseti(L, -2, ++i);
}
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index 4d295b6eb..6cfdc0f6d 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -188,15 +188,15 @@ class LuaABM : public ActiveBlockModifier {
private:
int m_id;
- std::set<std::string> m_trigger_contents;
- std::set<std::string> m_required_neighbors;
+ std::vector<std::string> m_trigger_contents;
+ std::vector<std::string> m_required_neighbors;
float m_trigger_interval;
u32 m_trigger_chance;
bool m_simple_catch_up;
public:
LuaABM(lua_State *L, int id,
- const std::set<std::string> &trigger_contents,
- const std::set<std::string> &required_neighbors,
+ const std::vector<std::string> &trigger_contents,
+ const std::vector<std::string> &required_neighbors,
float trigger_interval, u32 trigger_chance, bool simple_catch_up):
m_id(id),
m_trigger_contents(trigger_contents),
@@ -206,11 +206,11 @@ public:
m_simple_catch_up(simple_catch_up)
{
}
- virtual const std::set<std::string> &getTriggerContents() const
+ virtual const std::vector<std::string> &getTriggerContents() const
{
return m_trigger_contents;
}
- virtual const std::set<std::string> &getRequiredNeighbors() const
+ virtual const std::vector<std::string> &getRequiredNeighbors() const
{
return m_required_neighbors;
}
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index 7d95b8280..1abe5054c 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#if USE_POSTGRESQL
#include "database-postgresql.h"
#endif
+#include <algorithm>
#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
@@ -84,7 +85,7 @@ void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamed
lbm_list.push_back(lbm_def);
for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
- std::set<content_t> c_ids;
+ std::vector<content_t> c_ids;
bool found = nodedef->getIds(nodeTrigger, c_ids);
if (!found) {
content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
@@ -94,7 +95,7 @@ void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamed
<< "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
continue;
}
- c_ids.insert(c_id);
+ c_ids.push_back(c_id);
}
for (content_t c_id : c_ids) {
@@ -646,7 +647,8 @@ struct ActiveABM
{
ActiveBlockModifier *abm;
int chance;
- std::set<content_t> required_neighbors;
+ std::vector<content_t> required_neighbors;
+ bool check_required_neighbors; // false if required_neighbors is known to be empty
};
class ABMHandler
@@ -693,16 +695,17 @@ public:
}
// Trigger neighbors
- const std::set<std::string> &required_neighbors_s =
+ const std::vector<std::string> &required_neighbors_s =
abm->getRequiredNeighbors();
for (const std::string &required_neighbor_s : required_neighbors_s) {
ndef->getIds(required_neighbor_s, aabm.required_neighbors);
}
+ aabm.check_required_neighbors = !required_neighbors_s.empty();
// Trigger contents
- const std::set<std::string> &contents_s = abm->getTriggerContents();
+ const std::vector<std::string> &contents_s = abm->getTriggerContents();
for (const std::string &content_s : contents_s) {
- std::set<content_t> ids;
+ std::vector<content_t> ids;
ndef->getIds(content_s, ids);
for (content_t c : ids) {
if (c >= m_aabms.size())
@@ -777,7 +780,7 @@ public:
continue;
// Check neighbors
- if (!aabm.required_neighbors.empty()) {
+ if (aabm.check_required_neighbors) {
v3s16 p1;
for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
@@ -796,11 +799,8 @@ public:
MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
c = n.getContent();
}
- std::set<content_t>::const_iterator k;
- k = aabm.required_neighbors.find(c);
- if(k != aabm.required_neighbors.end()){
+ if (CONTAINS(aabm.required_neighbors, c))
goto neighbor_found;
- }
}
// No required neighbor found
continue;
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
index 0eabfc08f..3fdd6d77e 100644
--- a/src/serverenvironment.h
+++ b/src/serverenvironment.h
@@ -53,10 +53,10 @@ public:
virtual ~ActiveBlockModifier() = default;
// Set of contents to trigger on
- virtual const std::set<std::string> &getTriggerContents() const = 0;
+ virtual const std::vector<std::string> &getTriggerContents() const = 0;
// Set of required neighbors (trigger doesn't happen if none are found)
// Empty = do not check neighbors
- virtual const std::set<std::string> &getRequiredNeighbors() const = 0;
+ virtual const std::vector<std::string> &getRequiredNeighbors() const = 0;
// Trigger interval in seconds
virtual float getTriggerInterval() = 0;
// Random chance of (1 / return value), 0 is disallowed
">}; enum NodeBoxType { NODEBOX_REGULAR, // Regular block; allows buildable_to NODEBOX_FIXED, // Static separately defined box(es) NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side) NODEBOX_LEVELED, // Same as fixed, but with dynamic height from param2. for snow, ... NODEBOX_CONNECTED, // optionally draws nodeboxes if a neighbor node attaches }; struct NodeBox { enum NodeBoxType type; // NODEBOX_REGULAR (no parameters) // NODEBOX_FIXED std::vector<aabb3f> fixed; // NODEBOX_WALLMOUNTED aabb3f wall_top; aabb3f wall_bottom; aabb3f wall_side; // being at the -X side // NODEBOX_CONNECTED std::vector<aabb3f> connect_top; std::vector<aabb3f> connect_bottom; std::vector<aabb3f> connect_front; std::vector<aabb3f> connect_left; std::vector<aabb3f> connect_back; std::vector<aabb3f> connect_right; std::vector<aabb3f> disconnected_top; std::vector<aabb3f> disconnected_bottom; std::vector<aabb3f> disconnected_front; std::vector<aabb3f> disconnected_left; std::vector<aabb3f> disconnected_back; std::vector<aabb3f> disconnected_right; std::vector<aabb3f> disconnected; std::vector<aabb3f> disconnected_sides; NodeBox() { reset(); } void reset(); void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); }; struct MapNode; class NodeMetadata; enum LeavesStyle { LEAVES_FANCY, LEAVES_SIMPLE, LEAVES_OPAQUE, }; enum AutoScale : u8 { AUTOSCALE_DISABLE, AUTOSCALE_ENABLE, AUTOSCALE_FORCE, }; enum WorldAlignMode : u8 { WORLDALIGN_DISABLE, WORLDALIGN_ENABLE, WORLDALIGN_FORCE, WORLDALIGN_FORCE_NODEBOX, }; class TextureSettings { public: LeavesStyle leaves_style; WorldAlignMode world_aligned_mode; AutoScale autoscale_mode; int node_texture_size; bool opaque_water; bool connected_glass; bool enable_mesh_cache; bool enable_minimap; TextureSettings() = default; void readSettings(); }; enum NodeDrawType { // A basic solid block NDT_NORMAL, // Nothing is drawn NDT_AIRLIKE, // Do not draw face towards same kind of flowing/source liquid NDT_LIQUID, // A very special kind of thing NDT_FLOWINGLIQUID, // Glass-like, don't draw faces towards other glass NDT_GLASSLIKE, // Leaves-like, draw all faces no matter what NDT_ALLFACES, // Enabled -> ndt_allfaces, disabled -> ndt_normal NDT_ALLFACES_OPTIONAL, // Single plane perpendicular to a surface NDT_TORCHLIKE, // Single plane parallel to a surface NDT_SIGNLIKE, // 2 vertical planes in a 'X' shape diagonal to XZ axes. // paramtype2 = "meshoptions" allows various forms, sizes and // vertical and horizontal random offsets. NDT_PLANTLIKE, // Fenceposts that connect to neighbouring fenceposts with horizontal bars NDT_FENCELIKE, // Selects appropriate junction texture to connect like rails to // neighbouring raillikes. NDT_RAILLIKE, // Custom Lua-definable structure of multiple cuboids NDT_NODEBOX, // Glass-like, draw connected frames and all visible faces. // param2 > 0 defines 64 levels of internal liquid // Uses 3 textures, one for frames, second for faces, // optional third is a 'special tile' for the liquid. NDT_GLASSLIKE_FRAMED, // Draw faces slightly rotated and only on neighbouring nodes NDT_FIRELIKE, // Enabled -> ndt_glasslike_framed, disabled -> ndt_glasslike NDT_GLASSLIKE_FRAMED_OPTIONAL, // Uses static meshes NDT_MESH, // Combined plantlike-on-solid NDT_PLANTLIKE_ROOTED, }; // Mesh options for NDT_PLANTLIKE with CPT2_MESHOPTIONS static const u8 MO_MASK_STYLE = 0x07; static const u8 MO_BIT_RANDOM_OFFSET = 0x08; static const u8 MO_BIT_SCALE_SQRT2 = 0x10; static const u8 MO_BIT_RANDOM_OFFSET_Y = 0x20; enum PlantlikeStyle { PLANT_STYLE_CROSS, PLANT_STYLE_CROSS2, PLANT_STYLE_STAR, PLANT_STYLE_HASH, PLANT_STYLE_HASH2, }; enum AlignStyle : u8 { ALIGN_STYLE_NODE, ALIGN_STYLE_WORLD, ALIGN_STYLE_USER_DEFINED, }; enum AlphaMode : u8 { ALPHAMODE_BLEND, ALPHAMODE_CLIP, ALPHAMODE_OPAQUE, ALPHAMODE_LEGACY_COMPAT, /* means either opaque or clip */ }; /* Stand-alone definition of a TileSpec (basically a server-side TileSpec) */ struct TileDef { std::string name = ""; bool backface_culling = true; // Takes effect only in special cases bool tileable_horizontal = true; bool tileable_vertical = true; //! If true, the tile has its own color. bool has_color = false; //! The color of the tile. video::SColor color = video::SColor(0xFFFFFFFF); AlignStyle align_style = ALIGN_STYLE_NODE; u8 scale = 0; struct TileAnimationParams animation; TileDef() { animation.type = TAT_NONE; } void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is, u8 contentfeatures_version, NodeDrawType drawtype); }; // Defines the number of special tiles per nodedef // // NOTE: When changing this value, the enum entries of OverrideTarget and // parser in TextureOverrideSource must be updated so that all special // tiles can be overridden. #define CF_SPECIAL_COUNT 6 struct ContentFeatures { /* Cached stuff */ #ifndef SERVER // 0 1 2 3 4 5 // up down right left back front TileSpec tiles[6]; // Special tiles // - Currently used for flowing liquids TileSpec special_tiles[CF_SPECIAL_COUNT]; u8 solidness; // Used when choosing which face is drawn u8 visual_solidness; // When solidness=0, this tells how it looks like bool backface_culling; #endif // Server-side cached callback existence for fast skipping bool has_on_construct; bool has_on_destruct; bool has_after_destruct; /* Actual data */ // --- GENERAL PROPERTIES --- std::string name; // "" = undefined node ItemGroupList groups; // Same as in itemdef // Type of MapNode::param1 ContentParamType param_type; // Type of MapNode::param2 ContentParamType2 param_type_2; // --- VISUAL PROPERTIES --- enum NodeDrawType drawtype; std::string mesh; #ifndef SERVER scene::IMesh *mesh_ptr[24]; video::SColor minimap_color; #endif float visual_scale; // Misc. scale parameter TileDef tiledef[6]; // These will be drawn over the base tiles. TileDef tiledef_overlay[6]; TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid AlphaMode alpha; // The color of the node. video::SColor color; std::string palette_name; std::vector<video::SColor> *palette; // Used for waving leaves/plants u8 waving; // for NDT_CONNECTED pairing u8 connect_sides; std::vector<std::string> connects_to; std::vector<content_t> connects_to_ids; // Post effect color, drawn when the camera is inside the node. video::SColor post_effect_color; // Flowing liquid or leveled nodebox, value = default level u8 leveled; // Maximum value for leveled nodes u8 leveled_max; // --- LIGHTING-RELATED --- bool light_propagates; bool sunlight_propagates; // Amount of light the node emits u8 light_source; // --- MAP GENERATION --- // True for all ground-like things like stone and mud, false for eg. trees bool is_ground_content; // --- INTERACTION PROPERTIES --- // This is used for collision detection. // Also for general solidness queries. bool walkable; // Player can point to these bool pointable; // Player can dig these bool diggable; // Player can climb these bool climbable; // Player can build on these bool buildable_to; // Player cannot build to these (placement prediction disabled) bool rightclickable; u32 damage_per_second; // client dig prediction std::string node_dig_prediction; // how slow players move through u8 move_resistance = 0; // --- LIQUID PROPERTIES --- // Whether the node is non-liquid, source liquid or flowing liquid enum LiquidType liquid_type; // If true, movement (e.g. of players) inside this node is liquid-like. bool liquid_move_physics; // If the content is liquid, this is the flowing version of the liquid. std::string liquid_alternative_flowing; content_t liquid_alternative_flowing_id; // If the content is liquid, this is the source version of the liquid. std::string liquid_alternative_source; content_t liquid_alternative_source_id; // Viscosity for fluid flow, ranging from 1 to 7, with // 1 giving almost instantaneous propagation and 7 being // the slowest possible u8 liquid_viscosity; // Is liquid renewable (new liquid source will be created between 2 existing) bool liquid_renewable; // Number of flowing liquids surrounding source u8 liquid_range; u8 drowning; // Liquids flow into and replace node bool floodable; // --- NODEBOXES --- NodeBox node_box; NodeBox selection_box; NodeBox collision_box; // --- SOUND PROPERTIES --- SimpleSoundSpec sound_footstep; SimpleSoundSpec sound_dig; SimpleSoundSpec sound_dug; // --- LEGACY --- // Compatibility with old maps // Set to true if paramtype used to be 'facedir_simple' bool legacy_facedir_simple; // Set to true if wall_mounted used to be set to true bool legacy_wallmounted; /* Methods */ ContentFeatures(); ~ContentFeatures(); void reset(); void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); /* Some handy methods */ void setDefaultAlphaMode() { switch (drawtype) { case NDT_NORMAL: case NDT_LIQUID: case NDT_FLOWINGLIQUID: alpha = ALPHAMODE_OPAQUE; break; case NDT_NODEBOX: case NDT_MESH: alpha = ALPHAMODE_LEGACY_COMPAT; // this should eventually be OPAQUE break; default: alpha = ALPHAMODE_CLIP; break; } } bool needsBackfaceCulling() const { switch (drawtype) { case NDT_TORCHLIKE: case NDT_SIGNLIKE: case NDT_FIRELIKE: case NDT_RAILLIKE: case NDT_PLANTLIKE: case NDT_PLANTLIKE_ROOTED: case NDT_MESH: return false; default: return true; } } bool isLiquid() const{ return (liquid_type != LIQUID_NONE); } bool sameLiquid(const ContentFeatures &f) const{ if(!isLiquid() || !f.isLiquid()) return false; return (liquid_alternative_flowing_id == f.liquid_alternative_flowing_id); } int getGroup(const std::string &group) const { return itemgroup_get(groups, group); } #ifndef SERVER void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif private: #ifndef SERVER /* * Checks if any tile texture has any transparent pixels. * Prints a warning and returns true if that is the case, false otherwise. * This is supposed to be used for use_texture_alpha backwards compatibility. */ bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length); #endif void setAlphaFromLegacy(u8 legacy_alpha); u8 getAlphaForLegacy() const; }; /*! * @brief This class is for getting the actual properties of nodes from their * content ID. * * @details The nodes on the map are represented by three numbers (see MapNode). * The first number (param0) is the type of a node. All node types have own * properties (see ContentFeatures). This class is for storing and getting the * properties of nodes. * The manager is first filled with registered nodes, then as the game begins, * functions only get `const` pointers to it, to prevent modification of * registered nodes. */ class NodeDefManager { public: /*! * Creates a NodeDefManager, and registers three ContentFeatures: * \ref CONTENT_AIR, \ref CONTENT_UNKNOWN and \ref CONTENT_IGNORE. */ NodeDefManager(); ~NodeDefManager(); /*! * Returns the properties for the given content type. * @param c content type of a node * @return properties of the given content type, or \ref CONTENT_UNKNOWN * if the given content type is not registered. */ inline const ContentFeatures& get(content_t c) const { return c < m_content_features.size() ? m_content_features[c] : m_content_features[CONTENT_UNKNOWN]; } /*! * Returns the properties of the given node. * @param n a map node * @return properties of the given node or @ref CONTENT_UNKNOWN if the * given content type is not registered. */ inline const ContentFeatures& get(const MapNode &n) const { return get(n.getContent()); } /*! * Returns the node properties for a node name. * @param name name of a node * @return properties of the given node or @ref CONTENT_UNKNOWN if * not found */ const ContentFeatures& get(const std::string &name) const; /*! * Returns the content ID for the given name. * @param name a node name * @param[out] result will contain the content ID if found, otherwise * remains unchanged * @return true if the ID was found, false otherwise */ bool getId(const std::string &name, content_t &result) const; /*! * Returns the content ID for the given name. * @param name a node name * @return ID of the node or @ref CONTENT_IGNORE if not found */ content_t getId(const std::string &name) const; /*! * Returns the content IDs of the given node name or node group name. * Group names start with "group:". * @param name a node name or node group name * @param[out] result will be appended with matching IDs * @return true if `name` is a valid node name or a (not necessarily * valid) group name */ bool getIds(const std::string &name, std::vector<content_t> &result) const; /*! * Returns the smallest box in integer node coordinates that * contains all nodes' selection boxes. The returned box might be larger * than the minimal size if the largest node is removed from the manager. */ inline core::aabbox3d<s16> getSelectionBoxIntUnion() const { return m_selection_box_int_union; } /*! * Checks whether a node connects to an adjacent node. * @param from the node to be checked * @param to the adjacent node * @param connect_face a bit field indicating which face of the node is * adjacent to the other node. * Bits: +y (least significant), -y, -z, -x, +z, +x (most significant). * @return true if the node connects, false otherwise */ bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face) const; /*! * Registers a NodeResolver to wait for the registration of * ContentFeatures. Once the node registration finishes, all * listeners are notified. */ void pendNodeResolve(NodeResolver *nr) const; /*! * Stops listening to the NodeDefManager. * @return true if the listener was registered before, false otherwise */ bool cancelNodeResolveCallback(NodeResolver *nr) const; /*! * Registers a new node type with the given name and allocates a new * content ID. * Should not be called with an already existing name. * @param name name of the node, must match with `def.name`. * @param def definition of the registered node type. * @return ID of the registered node or @ref CONTENT_IGNORE if * the function could not allocate an ID. */ content_t set(const std::string &name, const ContentFeatures &def); /*! * Allocates a blank node ID for the given name. * @param name name of a node * @return allocated ID or @ref CONTENT_IGNORE if could not allocate * an ID. */ content_t allocateDummy(const std::string &name); /*! * Removes the given node name from the manager. * The node ID will remain in the manager, but won't be linked to any name. * @param name name to be removed */ void removeNode(const std::string &name); /*! * Regenerates the alias list (a map from names to node IDs). * @param idef the item definition manager containing alias information */ void updateAliases(IItemDefManager *idef); /*! * Replaces the textures of registered nodes with the ones specified in * the texturepack's override.txt file * * @param overrides the texture overrides */ void applyTextureOverrides(const std::vector<TextureOverride> &overrides); /*! * Only the client uses this. Loads textures and shaders required for * rendering the nodes. * @param gamedef must be a Client. * @param progress_cbk called each time a node is loaded. Arguments: * `progress_cbk_args`, number of loaded ContentFeatures, number of * total ContentFeatures. * @param progress_cbk_args passed to the callback function */ void updateTextures(IGameDef *gamedef, void *progress_cbk_args); /*! * Writes the content of this manager to the given output stream. * @param protocol_version serialization version of ContentFeatures */ void serialize(std::ostream &os, u16 protocol_version) const; /*! * Restores the manager from a serialized stream. * This clears the previous state. * @param is input stream containing a serialized NodeDefManager */ void deSerialize(std::istream &is); /*! * Used to indicate that node registration has finished. * @param completed tells whether registration is complete */ inline void setNodeRegistrationStatus(bool completed) { m_node_registration_complete = completed; } /*! * Notifies the registered NodeResolver instances that node registration * has finished, then unregisters all listeners. * Must be called after node registration has finished! */ void runNodeResolveCallbacks(); /*! * Sets the registration completion flag to false and unregisters all * NodeResolver instances listening to the manager. */ void resetNodeResolveState(); /*! * Resolves (caches the IDs) cross-references between nodes, * like liquid alternatives. * Must be called after node registration has finished! */ void resolveCrossrefs(); private: /*! * Resets the manager to its initial state. * See the documentation of the constructor. */ void clear(); /*! * Allocates a new content ID, and returns it. * @return the allocated ID or \ref CONTENT_IGNORE if could not allocate */ content_t allocateId(); /*! * Binds the given content ID and node name. * Registers them in \ref m_name_id_mapping and * \ref m_name_id_mapping_with_aliases. * @param i a content ID * @param name a node name */ void addNameIdMapping(content_t i, const std::string &name); /*! * Removes a content ID from all groups. * Erases content IDs from vectors in \ref m_group_to_items and * removes empty vectors. * @param id Content ID */ void eraseIdFromGroups(content_t id); /*! * Recalculates m_selection_box_int_union based on * m_selection_box_union. */ void fixSelectionBoxIntUnion(); //! Features indexed by ID. std::vector<ContentFeatures> m_content_features; //! A mapping for fast conversion between names and IDs NameIdMapping m_name_id_mapping; /*! * Like @ref m_name_id_mapping, but maps only from names to IDs, and * includes aliases too. Updated by \ref updateAliases(). * Note: Not serialized. */ std::unordered_map<std::string, content_t> m_name_id_mapping_with_aliases; /*! * A mapping from group names to a vector of content types that belong * to it. Necessary for a direct lookup in \ref getIds(). * Note: Not serialized. */ std::unordered_map<std::string, std::vector<content_t>> m_group_to_items; /*! * The next ID that might be free to allocate. * It can be allocated already, because \ref CONTENT_AIR, * \ref CONTENT_UNKNOWN and \ref CONTENT_IGNORE are registered when the * manager is initialized, and new IDs are allocated from 0. */ content_t m_next_id; //! True if all nodes have been registered. bool m_node_registration_complete; /*! * The union of all nodes' selection boxes. * Might be larger if big nodes are removed from the manager. */ aabb3f m_selection_box_union; /*! * The smallest box in integer node coordinates that * contains all nodes' selection boxes. * Might be larger if big nodes are removed from the manager. */ core::aabbox3d<s16> m_selection_box_int_union; /*! * NodeResolver instances to notify once node registration has finished. * Even constant NodeDefManager instances can register listeners. */ mutable std::vector<NodeResolver *> m_pending_resolve_callbacks; }; NodeDefManager *createNodeDefManager(); // NodeResolver: Queue for node names which are then translated // to content_t after the NodeDefManager was initialized class NodeResolver { public: NodeResolver(); virtual ~NodeResolver(); // Callback which is run as soon NodeDefManager is ready virtual void resolveNodeNames() = 0; // required because this class is used as mixin for ObjDef void cloneTo(NodeResolver *res) const; bool getIdFromNrBacklog(content_t *result_out, const std::string &node_alt, content_t c_fallback, bool error_on_fallback = true); bool getIdsFromNrBacklog(std::vector<content_t> *result_out, bool all_required = false, content_t c_fallback = CONTENT_IGNORE); inline bool isResolveDone() const { return m_resolve_done; } void reset(bool resolve_done = false); // Vector containing all node names in the resolve "queue" std::vector<std::string> m_nodenames; // Specifies the "set size" of node names which are to be processed // this is used for getIdsFromNrBacklog // TODO: replace or remove std::vector<size_t> m_nnlistsizes; protected: friend class NodeDefManager; // m_ndef const NodeDefManager *m_ndef = nullptr; // Index of the next "m_nodenames" entry to resolve u32 m_nodenames_idx = 0; private: #if BUILD_UNITTESTS // Unittest requires access to m_resolve_done friend class TestSchematic; #endif void nodeResolveInternal(); // Index of the next "m_nnlistsizes" entry to process u32 m_nnlistsizes_idx = 0; bool m_resolve_done = false; };