157 changes: 145 additions & 12 deletions src/nodedef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "debug.h"
#include "gamedef.h"
#include "mapnode.h"
#include <fstream> // Used in applyTextureOverrides()

/*
Expand All @@ -48,44 +49,91 @@ void NodeBox::reset()
wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2);
wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2);
// no default for other parts
connect_top.clear();
connect_bottom.clear();
connect_front.clear();
connect_left.clear();
connect_back.clear();
connect_right.clear();
}

void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
{
int version = protocol_version >= 21 ? 2 : 1;
int version = 1;
if (protocol_version >= 27)
version = 3;
else if (protocol_version >= 21)
version = 2;
writeU8(os, version);

if (version == 1 && type == NODEBOX_LEVELED)
writeU8(os, NODEBOX_FIXED);
else
writeU8(os, type);
switch (type) {
case NODEBOX_LEVELED:
case NODEBOX_FIXED:
if (version == 1)
writeU8(os, NODEBOX_FIXED);
else
writeU8(os, type);

if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
{
writeU16(os, fixed.size());
for(std::vector<aabb3f>::const_iterator
for (std::vector<aabb3f>::const_iterator
i = fixed.begin();
i != fixed.end(); ++i)
{
writeV3F1000(os, i->MinEdge);
writeV3F1000(os, i->MaxEdge);
}
}
else if(type == NODEBOX_WALLMOUNTED)
{
break;
case NODEBOX_WALLMOUNTED:
writeU8(os, type);

writeV3F1000(os, wall_top.MinEdge);
writeV3F1000(os, wall_top.MaxEdge);
writeV3F1000(os, wall_bottom.MinEdge);
writeV3F1000(os, wall_bottom.MaxEdge);
writeV3F1000(os, wall_side.MinEdge);
writeV3F1000(os, wall_side.MaxEdge);
break;
case NODEBOX_CONNECTED:
if (version <= 2) {
// send old clients nodes that can't be walked through
// to prevent abuse
writeU8(os, NODEBOX_FIXED);

writeU16(os, 1);
writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2));
writeV3F1000(os, v3f(BS/2, BS/2, BS/2));
} else {
writeU8(os, type);

#define WRITEBOX(box) do { \
writeU16(os, (box).size()); \
for (std::vector<aabb3f>::const_iterator \
i = (box).begin(); \
i != (box).end(); ++i) { \
writeV3F1000(os, i->MinEdge); \
writeV3F1000(os, i->MaxEdge); \
}; } while (0)

WRITEBOX(fixed);
WRITEBOX(connect_top);
WRITEBOX(connect_bottom);
WRITEBOX(connect_front);
WRITEBOX(connect_left);
WRITEBOX(connect_back);
WRITEBOX(connect_right);
}
break;
default:
writeU8(os, type);
break;
}
}

void NodeBox::deSerialize(std::istream &is)
{
int version = readU8(is);
if(version < 1 || version > 2)
if (version < 1 || version > 3)
throw SerializationError("unsupported NodeBox version");

reset();
Expand All @@ -112,6 +160,26 @@ void NodeBox::deSerialize(std::istream &is)
wall_side.MinEdge = readV3F1000(is);
wall_side.MaxEdge = readV3F1000(is);
}
else if (type == NODEBOX_CONNECTED)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you add one more type, please use a switch case on this function to improve condition handling performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rewritten this to be an if statement at the top to determine version number, and then everything else below in a single switch() block, which works well.

{
#define READBOXES(box) do { \
count = readU16(is); \
(box).reserve(count); \
while (count--) { \
v3f min = readV3F1000(is); \
v3f max = readV3F1000(is); \
(box).push_back(aabb3f(min, max)); }; } while (0)

u16 count;

READBOXES(fixed);
READBOXES(connect_top);
READBOXES(connect_bottom);
READBOXES(connect_front);
READBOXES(connect_left);
READBOXES(connect_back);
READBOXES(connect_right);
}
}

/*
Expand Down Expand Up @@ -261,6 +329,9 @@ void ContentFeatures::reset()
sound_footstep = SimpleSoundSpec();
sound_dig = SimpleSoundSpec("__group");
sound_dug = SimpleSoundSpec();
connects_to.clear();
connects_to_ids.clear();
connect_sides = 0;
}

void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
Expand Down Expand Up @@ -328,6 +399,11 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
os<<serializeString(mesh);
collision_box.serialize(os, protocol_version);
writeU8(os, floodable);
writeU16(os, connects_to_ids.size());
for (std::set<content_t>::const_iterator i = connects_to_ids.begin();
i != connects_to_ids.end(); ++i)
writeU16(os, *i);
writeU8(os, connect_sides);
}

void ContentFeatures::deSerialize(std::istream &is)
Expand Down Expand Up @@ -402,6 +478,10 @@ void ContentFeatures::deSerialize(std::istream &is)
mesh = deSerializeString(is);
collision_box.deSerialize(is);
floodable = readU8(is);
u16 connects_to_size = readU16(is);
for (u16 i = 0; i < connects_to_size; i++)
connects_to_ids.insert(readU16(is));
connect_sides = readU8(is);
}catch(SerializationError &e) {};
}

Expand Down Expand Up @@ -439,6 +519,8 @@ class CNodeDefManager: public IWritableNodeDefManager {
virtual bool cancelNodeResolveCallback(NodeResolver *nr);
virtual void runNodeResolveCallbacks();
virtual void resetNodeResolveState();
virtual void mapNodeboxConnections();
virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face);

private:
void addNameIdMapping(content_t i, std::string name);
Expand Down Expand Up @@ -1438,6 +1520,57 @@ void CNodeDefManager::resetNodeResolveState()
m_pending_resolve_callbacks.clear();
}

void CNodeDefManager::mapNodeboxConnections()
{
for (u32 i = 0; i < m_content_features.size(); i++) {
ContentFeatures *f = &m_content_features[i];
if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED))
continue;
for (std::vector<std::string>::iterator it = f->connects_to.begin();
it != f->connects_to.end(); ++it) {
getIds(*it, f->connects_to_ids);
}
}
}

bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face)
{
const ContentFeatures &f1 = get(from);

if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED))
return false;

// lookup target in connected set
if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end())
return false;

const ContentFeatures &f2 = get(to);

if ((f2.drawtype == NDT_NODEBOX) && (f1.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());

// does to node declare usable faces?
if (f2.connect_sides > 0) {
if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) {
static const u8 rot[33 * 4] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back
8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32, 16, 8, 4 // 32 - left
};
return (f2.connect_sides & rot[(connect_face * 4) + to.param2]);
}
return (f2.connect_sides & connect_face);
}
// the target is just a regular node, so connect no matter back connection
return true;
}

////
//// NodeResolver
Expand Down
15 changes: 15 additions & 0 deletions src/nodedef.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ enum NodeBoxType
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
Expand All @@ -92,6 +93,13 @@ struct NodeBox
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;

NodeBox()
{ reset(); }
Expand Down Expand Up @@ -263,12 +271,17 @@ struct ContentFeatures
bool legacy_facedir_simple;
// Set to true if wall_mounted used to be set to true
bool legacy_wallmounted;
// for NDT_CONNECTED pairing
u8 connect_sides;

// Sound properties
SimpleSoundSpec sound_footstep;
SimpleSoundSpec sound_dig;
SimpleSoundSpec sound_dug;

std::vector<std::string> connects_to;
std::set<content_t> connects_to_ids;

/*
Methods
*/
Expand Down Expand Up @@ -314,6 +327,7 @@ class INodeDefManager {

virtual void pendNodeResolve(NodeResolver *nr)=0;
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
virtual bool nodeboxConnects(const MapNode from, const MapNode to, u8 connect_face)=0;
};

class IWritableNodeDefManager : public INodeDefManager {
Expand Down Expand Up @@ -368,6 +382,7 @@ class IWritableNodeDefManager : public INodeDefManager {
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
virtual void runNodeResolveCallbacks()=0;
virtual void resetNodeResolveState()=0;
virtual void mapNodeboxConnections()=0;
};

IWritableNodeDefManager *createNodeDefManager();
Expand Down
88 changes: 69 additions & 19 deletions src/script/common/c_content.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,50 @@ ContentFeatures read_content_features(lua_State *L, int index)
f.node_box = read_nodebox(L, -1);
lua_pop(L, 1);

lua_getfield(L, index, "connects_to");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
int i = 0;
while (lua_next(L, table) != 0) {
// Value at -1
f.connects_to.push_back(lua_tostring(L, -1));
lua_pop(L, 1);
i++;
}
}
lua_pop(L, 1);

lua_getfield(L, index, "connect_sides");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
int i = 0;
while (lua_next(L, table) != 0) {
// Value at -1
std::string side(lua_tostring(L, -1));
// Note faces are flipped to make checking easier
if (side.compare("top") == 0)
f.connect_sides |= 2;
else if (side.compare("bottom") == 0)
f.connect_sides |= 1;
else if (side.compare("front") == 0)
f.connect_sides |= 16;
else if (side.compare("left") == 0)
f.connect_sides |= 32;
else if (side.compare("back") == 0)
f.connect_sides |= 4;
else if (side.compare("right") == 0)
f.connect_sides |= 8;
else
warningstream << "Unknown value for \"connect_sides\": "
<< side << std::endl;
lua_pop(L, 1);
i++;
}
}
lua_pop(L, 1);

lua_getfield(L, index, "selection_box");
if(lua_istable(L, -1))
f.selection_box = read_nodebox(L, -1);
Expand Down Expand Up @@ -627,25 +671,31 @@ NodeBox read_nodebox(lua_State *L, int index)
nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR);

lua_getfield(L, index, "fixed");
if(lua_istable(L, -1))
nodebox.fixed = read_aabb3f_vector(L, -1, BS);
lua_pop(L, 1);

lua_getfield(L, index, "wall_top");
if(lua_istable(L, -1))
nodebox.wall_top = read_aabb3f(L, -1, BS);
lua_pop(L, 1);

lua_getfield(L, index, "wall_bottom");
if(lua_istable(L, -1))
nodebox.wall_bottom = read_aabb3f(L, -1, BS);
lua_pop(L, 1);

lua_getfield(L, index, "wall_side");
if(lua_istable(L, -1))
nodebox.wall_side = read_aabb3f(L, -1, BS);
lua_pop(L, 1);
#define NODEBOXREAD(n, s) \
do { \
lua_getfield(L, index, (s)); \
if (lua_istable(L, -1)) \
(n) = read_aabb3f(L, -1, BS); \
lua_pop(L, 1); \
} while (0)

#define NODEBOXREADVEC(n, s) \
do { \
lua_getfield(L, index, (s)); \
if (lua_istable(L, -1)) \
(n) = read_aabb3f_vector(L, -1, BS); \
lua_pop(L, 1); \
} while (0)
NODEBOXREADVEC(nodebox.fixed, "fixed");
NODEBOXREAD(nodebox.wall_top, "wall_top");
NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
NODEBOXREAD(nodebox.wall_side, "wall_side");
NODEBOXREADVEC(nodebox.connect_top, "connect_top");
NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom");
NODEBOXREADVEC(nodebox.connect_front, "connect_front");
NODEBOXREADVEC(nodebox.connect_left, "connect_left");
NODEBOXREADVEC(nodebox.connect_back, "connect_back");
NODEBOXREADVEC(nodebox.connect_right, "connect_right");
}
return nodebox;
}
Expand Down
1 change: 1 addition & 0 deletions src/script/cpp_api/s_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
{NODEBOX_FIXED, "fixed"},
{NODEBOX_WALLMOUNTED, "wallmounted"},
{NODEBOX_LEVELED, "leveled"},
{NODEBOX_CONNECTED, "connected"},
{0, NULL},
};

Expand Down
3 changes: 3 additions & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ Server::Server(
// Perform pending node name resolutions
m_nodedef->runNodeResolveCallbacks();

// unmap node names for connected nodeboxes
m_nodedef->mapNodeboxConnections();

// init the recipe hashes to speed up crafting
m_craftdef->initHashes(this);

Expand Down