Nodebox: Allow nodeboxes to "connect"
We introduce a new nodebox type "connected", and allow these nodes to have optional nodeboxes that connect it to other connecting nodeboxes. This is all done at scenedraw time in the client. The client will inspect the surrounding nodes and if they are to be connected to, it will draw the appropriate connecting nodeboxes to make those connections. In the node_box definition, we have to specify separate nodeboxes for each valid connection. This allows us to make nodes that connect only horizontally (the common case) by providing optional nodeboxes for +x, -x, +z, -z directions. Or this allows us to make wires that can connect up and down, by providing nodeboxes that connect it up and down (+y, -y) as well. The optional nodeboxes can be arrays. They are named "connect_top, "connect_bottom", "connect_front", "connect_left", "connect_back" and "connect_right". Here, "front" means the south facing side of the node that has facedir = 0. Additionally, a "fixed" nodebox list present will always be drawn, so one can make a central post, for instance. This "fixed" nodebox can be omitted, or it can be an array of nodeboxes. Collision boxes are also updated in exactly the same fashion, which allows you to walk over the upper extremities of the individual node boxes, or stand really close to them. You can also walk up node noxes that are small in height, all as expected, and unlike the NDT_FENCELIKE nodes. I've posted a screenshot demonstrating the flexibility at http://i.imgur.com/zaJq8jo.png In the screenshot, all connecting nodes are of this new subtype. Transparent textures render incorrectly, Which I don't think is related to this text, as other nodeboxes also have issues with this. A protocol bump is performed in order to be able to send older clients a nodeblock that is usable for them. In order to avoid abuse of users we send older clients a "full-size" node, so that it's impossible for them to try and walk through a fence or wall that's created in this fashion. This was tested with a pre-bump client connected against a server running the new protocol. These nodes connect to other nodes, and you can select which ones those are by specifying node names (or group names) in the connects_to string array: connects_to = { "group:fence", "default:wood" } By default, nodes do not connect to anything, allowing you to create nodes that always have to be paired in order to connect. lua_api.txt is updated to reflect the extension to the node_box API. Example lua code needed to generate these nodes can be found here: https://gist.github.com/sofar/b381c8c192c8e53e6062master
parent
8c951cae5b
commit
e737b1c271
|
@ -617,6 +617,18 @@ A nodebox is defined as any of:
|
||||||
wall_bottom = box,
|
wall_bottom = box,
|
||||||
wall_side = box
|
wall_side = box
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
-- A node that has optional boxes depending on neighbouring nodes'
|
||||||
|
-- presence and type. See also `connects_to`.
|
||||||
|
type = "connected",
|
||||||
|
fixed = box OR {box1, box2, ...}
|
||||||
|
connect_top = box OR {box1, box2, ...}
|
||||||
|
connect_bottom = box OR {box1, box2, ...}
|
||||||
|
connect_front = box OR {box1, box2, ...}
|
||||||
|
connect_left = box OR {box1, box2, ...}
|
||||||
|
connect_back = box OR {box1, box2, ...}
|
||||||
|
connect_right = box OR {box1, box2, ...}
|
||||||
|
}
|
||||||
|
|
||||||
A `box` is defined as:
|
A `box` is defined as:
|
||||||
|
|
||||||
|
@ -3461,6 +3473,10 @@ Definition tables
|
||||||
light_source = 0, -- Amount of light emitted by node
|
light_source = 0, -- Amount of light emitted by node
|
||||||
damage_per_second = 0, -- If player is inside node, this damage is caused
|
damage_per_second = 0, -- If player is inside node, this damage is caused
|
||||||
node_box = {type="regular"}, -- See "Node boxes"
|
node_box = {type="regular"}, -- See "Node boxes"
|
||||||
|
connects_to = nodenames, --[[
|
||||||
|
* Used for nodebox nodes with the type == "connected"
|
||||||
|
* Specifies to what neighboring nodes connections will be drawn
|
||||||
|
* e.g. `{"group:fence", "default:wood"}` or `"default:stone"` ]]
|
||||||
mesh = "model",
|
mesh = "model",
|
||||||
selection_box = {type="regular"}, -- See "Node boxes" --[[
|
selection_box = {type="regular"}, -- See "Node boxes" --[[
|
||||||
^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]]
|
^ If drawtype "nodebox" is used and selection_box is nil, then node_box is used. ]]
|
||||||
|
|
|
@ -185,6 +185,13 @@ bool wouldCollideWithCeiling(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
|
||||||
|
Map *map, MapNode n, int v, int *neighbors)
|
||||||
|
{
|
||||||
|
MapNode n2 = map->getNodeNoEx(p);
|
||||||
|
if (nodedef->nodeboxConnects(n, n2))
|
||||||
|
*neighbors |= v;
|
||||||
|
}
|
||||||
|
|
||||||
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
||||||
f32 pos_max_d, const aabb3f &box_0,
|
f32 pos_max_d, const aabb3f &box_0,
|
||||||
|
@ -261,12 +268,41 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
||||||
// Object collides into walkable nodes
|
// Object collides into walkable nodes
|
||||||
|
|
||||||
any_position_valid = true;
|
any_position_valid = true;
|
||||||
const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
|
INodeDefManager *nodedef = gamedef->getNodeDefManager();
|
||||||
|
const ContentFeatures &f = nodedef->get(n);
|
||||||
if(f.walkable == false)
|
if(f.walkable == false)
|
||||||
continue;
|
continue;
|
||||||
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
|
int n_bouncy_value = itemgroup_get(f.groups, "bouncy");
|
||||||
|
|
||||||
std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(gamedef->ndef());
|
int neighbors = 0;
|
||||||
|
if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) {
|
||||||
|
v3s16 p2 = p;
|
||||||
|
|
||||||
|
p2.Y++;
|
||||||
|
getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.Y--;
|
||||||
|
getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.Z--;
|
||||||
|
getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.X--;
|
||||||
|
getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.Z++;
|
||||||
|
getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.X++;
|
||||||
|
getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors);
|
||||||
|
}
|
||||||
|
std::vector<aabb3f> nodeboxes;
|
||||||
|
n.getCollisionBoxes(gamedef->ndef(), &nodeboxes, neighbors);
|
||||||
for(std::vector<aabb3f>::iterator
|
for(std::vector<aabb3f>::iterator
|
||||||
i = nodeboxes.begin();
|
i = nodeboxes.begin();
|
||||||
i != nodeboxes.end(); ++i)
|
i != nodeboxes.end(); ++i)
|
||||||
|
|
|
@ -163,6 +163,14 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
|
||||||
|
MeshMakeData *data, MapNode n, int v, int *neighbors)
|
||||||
|
{
|
||||||
|
MapNode n2 = data->m_vmanip.getNodeNoEx(p);
|
||||||
|
if (nodedef->nodeboxConnects(n, n2))
|
||||||
|
*neighbors |= v;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: Fix alpha blending for special nodes
|
TODO: Fix alpha blending for special nodes
|
||||||
Currently only the last element rendered is blended correct
|
Currently only the last element rendered is blended correct
|
||||||
|
@ -1501,7 +1509,38 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
|
||||||
|
|
||||||
v3f pos = intToFloat(p, BS);
|
v3f pos = intToFloat(p, BS);
|
||||||
|
|
||||||
std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
|
int neighbors = 0;
|
||||||
|
|
||||||
|
// locate possible neighboring nodes to connect to
|
||||||
|
if (f.node_box.type == NODEBOX_CONNECTED) {
|
||||||
|
v3s16 p2 = p;
|
||||||
|
|
||||||
|
p2.Y++;
|
||||||
|
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.Y--;
|
||||||
|
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.Z--;
|
||||||
|
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.X--;
|
||||||
|
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.Z++;
|
||||||
|
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
|
||||||
|
|
||||||
|
p2 = p;
|
||||||
|
p2.X++;
|
||||||
|
getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<aabb3f> boxes;
|
||||||
|
n.getNodeBoxes(nodedef, &boxes, neighbors);
|
||||||
for(std::vector<aabb3f>::iterator
|
for(std::vector<aabb3f>::iterator
|
||||||
i = boxes.begin();
|
i = boxes.begin();
|
||||||
i != boxes.end(); ++i)
|
i != boxes.end(); ++i)
|
||||||
|
|
|
@ -358,7 +358,9 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio
|
||||||
if (!isPointableNode(n, client, liquids_pointable)) {
|
if (!isPointableNode(n, client, liquids_pointable)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
|
|
||||||
|
std::vector<aabb3f> boxes;
|
||||||
|
n.getSelectionBoxes(nodedef, &boxes);
|
||||||
|
|
||||||
v3s16 np(x, y, z);
|
v3s16 np(x, y, z);
|
||||||
v3f npf = intToFloat(np, BS);
|
v3f npf = intToFloat(np, BS);
|
||||||
|
@ -389,7 +391,8 @@ PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_positio
|
||||||
f32 d = 0.001 * BS;
|
f32 d = 0.001 * BS;
|
||||||
MapNode n = map.getNodeNoEx(pointed_pos);
|
MapNode n = map.getNodeNoEx(pointed_pos);
|
||||||
v3f npf = intToFloat(pointed_pos, BS);
|
v3f npf = intToFloat(pointed_pos, BS);
|
||||||
std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
|
std::vector<aabb3f> boxes;
|
||||||
|
n.getSelectionBoxes(nodedef, &boxes);
|
||||||
f32 face_min_distance = 1000 * BS;
|
f32 face_min_distance = 1000 * BS;
|
||||||
for (std::vector<aabb3f>::const_iterator
|
for (std::vector<aabb3f>::const_iterator
|
||||||
i = boxes.begin();
|
i = boxes.begin();
|
||||||
|
|
|
@ -310,7 +310,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|
||||||
if (sneak_node_found) {
|
if (sneak_node_found) {
|
||||||
f32 cb_max = 0;
|
f32 cb_max = 0;
|
||||||
MapNode n = map->getNodeNoEx(m_sneak_node);
|
MapNode n = map->getNodeNoEx(m_sneak_node);
|
||||||
std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
|
std::vector<aabb3f> nodeboxes;
|
||||||
|
n.getCollisionBoxes(nodemgr, &nodeboxes);
|
||||||
for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
|
for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
|
||||||
it != nodeboxes.end(); ++it) {
|
it != nodeboxes.end(); ++it) {
|
||||||
aabb3f box = *it;
|
aabb3f box = *it;
|
||||||
|
|
|
@ -214,12 +214,12 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<aabb3f> transformNodeBox(const MapNode &n,
|
void transformNodeBox(const MapNode &n, const NodeBox &nodebox,
|
||||||
const NodeBox &nodebox, INodeDefManager *nodemgr)
|
INodeDefManager *nodemgr, std::vector<aabb3f> *p_boxes, u8 neighbors = 0)
|
||||||
{
|
|
||||||
std::vector<aabb3f> boxes;
|
|
||||||
if(nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED)
|
|
||||||
{
|
{
|
||||||
|
std::vector<aabb3f> &boxes = *p_boxes;
|
||||||
|
|
||||||
|
if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) {
|
||||||
const std::vector<aabb3f> &fixed = nodebox.fixed;
|
const std::vector<aabb3f> &fixed = nodebox.fixed;
|
||||||
int facedir = n.getFaceDir(nodemgr);
|
int facedir = n.getFaceDir(nodemgr);
|
||||||
u8 axisdir = facedir>>2;
|
u8 axisdir = facedir>>2;
|
||||||
|
@ -395,32 +395,71 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n,
|
||||||
boxes.push_back(box);
|
boxes.push_back(box);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (nodebox.type == NODEBOX_CONNECTED)
|
||||||
|
{
|
||||||
|
size_t boxes_size = boxes.size();
|
||||||
|
boxes_size += nodebox.fixed.size();
|
||||||
|
if (neighbors & 1)
|
||||||
|
boxes_size += nodebox.connect_top.size();
|
||||||
|
if (neighbors & 2)
|
||||||
|
boxes_size += nodebox.connect_bottom.size();
|
||||||
|
if (neighbors & 4)
|
||||||
|
boxes_size += nodebox.connect_front.size();
|
||||||
|
if (neighbors & 8)
|
||||||
|
boxes_size += nodebox.connect_left.size();
|
||||||
|
if (neighbors & 16)
|
||||||
|
boxes_size += nodebox.connect_back.size();
|
||||||
|
if (neighbors & 32)
|
||||||
|
boxes_size += nodebox.connect_right.size();
|
||||||
|
boxes.reserve(boxes_size);
|
||||||
|
|
||||||
|
#define BOXESPUSHBACK(c) do { \
|
||||||
|
for (std::vector<aabb3f>::const_iterator \
|
||||||
|
it = (c).begin(); \
|
||||||
|
it != (c).end(); ++it) \
|
||||||
|
(boxes).push_back(*it); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
BOXESPUSHBACK(nodebox.fixed);
|
||||||
|
|
||||||
|
if (neighbors & 1)
|
||||||
|
BOXESPUSHBACK(nodebox.connect_top);
|
||||||
|
if (neighbors & 2)
|
||||||
|
BOXESPUSHBACK(nodebox.connect_bottom);
|
||||||
|
if (neighbors & 4)
|
||||||
|
BOXESPUSHBACK(nodebox.connect_front);
|
||||||
|
if (neighbors & 8)
|
||||||
|
BOXESPUSHBACK(nodebox.connect_left);
|
||||||
|
if (neighbors & 16)
|
||||||
|
BOXESPUSHBACK(nodebox.connect_back);
|
||||||
|
if (neighbors & 32)
|
||||||
|
BOXESPUSHBACK(nodebox.connect_right);
|
||||||
|
}
|
||||||
else // NODEBOX_REGULAR
|
else // NODEBOX_REGULAR
|
||||||
{
|
{
|
||||||
boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
|
boxes.push_back(aabb3f(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2));
|
||||||
}
|
}
|
||||||
return boxes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<aabb3f> MapNode::getNodeBoxes(INodeDefManager *nodemgr) const
|
void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
|
||||||
{
|
{
|
||||||
const ContentFeatures &f = nodemgr->get(*this);
|
const ContentFeatures &f = nodemgr->get(*this);
|
||||||
return transformNodeBox(*this, f.node_box, nodemgr);
|
transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<aabb3f> MapNode::getCollisionBoxes(INodeDefManager *nodemgr) const
|
void MapNode::getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors)
|
||||||
{
|
{
|
||||||
const ContentFeatures &f = nodemgr->get(*this);
|
const ContentFeatures &f = nodemgr->get(*this);
|
||||||
if (f.collision_box.fixed.empty())
|
if (f.collision_box.fixed.empty())
|
||||||
return transformNodeBox(*this, f.node_box, nodemgr);
|
transformNodeBox(*this, f.node_box, nodemgr, boxes, neighbors);
|
||||||
else
|
else
|
||||||
return transformNodeBox(*this, f.collision_box, nodemgr);
|
transformNodeBox(*this, f.collision_box, nodemgr, boxes, neighbors);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<aabb3f> MapNode::getSelectionBoxes(INodeDefManager *nodemgr) const
|
void MapNode::getSelectionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes)
|
||||||
{
|
{
|
||||||
const ContentFeatures &f = nodemgr->get(*this);
|
const ContentFeatures &f = nodemgr->get(*this);
|
||||||
return transformNodeBox(*this, f.selection_box, nodemgr);
|
transformNodeBox(*this, f.selection_box, nodemgr, boxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const
|
u8 MapNode::getMaxLevel(INodeDefManager *nodemgr) const
|
||||||
|
|
|
@ -240,17 +240,17 @@ struct MapNode
|
||||||
/*
|
/*
|
||||||
Gets list of node boxes (used for rendering (NDT_NODEBOX))
|
Gets list of node boxes (used for rendering (NDT_NODEBOX))
|
||||||
*/
|
*/
|
||||||
std::vector<aabb3f> getNodeBoxes(INodeDefManager *nodemgr) const;
|
void getNodeBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets list of selection boxes
|
Gets list of selection boxes
|
||||||
*/
|
*/
|
||||||
std::vector<aabb3f> getSelectionBoxes(INodeDefManager *nodemgr) const;
|
void getSelectionBoxes(INodeDefManager *nodemg, std::vector<aabb3f> *boxes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets list of collision boxes
|
Gets list of collision boxes
|
||||||
*/
|
*/
|
||||||
std::vector<aabb3f> getCollisionBoxes(INodeDefManager *nodemgr) const;
|
void getCollisionBoxes(INodeDefManager *nodemgr, std::vector<aabb3f> *boxes, u8 neighbors = 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Liquid helpers
|
Liquid helpers
|
||||||
|
|
|
@ -135,6 +135,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
PROTOCOL_VERSION 27:
|
PROTOCOL_VERSION 27:
|
||||||
backface_culling: backwards compatibility for playing with
|
backface_culling: backwards compatibility for playing with
|
||||||
newer client on pre-27 servers.
|
newer client on pre-27 servers.
|
||||||
|
Add nodedef v3 - connected nodeboxes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LATEST_PROTOCOL_VERSION 27
|
#define LATEST_PROTOCOL_VERSION 27
|
||||||
|
|
128
src/nodedef.cpp
128
src/nodedef.cpp
|
@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
|
#include "mapnode.h"
|
||||||
#include <fstream> // Used in applyTextureOverrides()
|
#include <fstream> // Used in applyTextureOverrides()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -48,20 +49,32 @@ void NodeBox::reset()
|
||||||
wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2);
|
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_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);
|
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
|
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);
|
writeU8(os, version);
|
||||||
|
|
||||||
if (version == 1 && type == NODEBOX_LEVELED)
|
switch (type) {
|
||||||
|
case NODEBOX_LEVELED:
|
||||||
|
case NODEBOX_FIXED:
|
||||||
|
if (version == 1)
|
||||||
writeU8(os, NODEBOX_FIXED);
|
writeU8(os, NODEBOX_FIXED);
|
||||||
else
|
else
|
||||||
writeU8(os, type);
|
writeU8(os, type);
|
||||||
|
|
||||||
if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED)
|
|
||||||
{
|
|
||||||
writeU16(os, fixed.size());
|
writeU16(os, fixed.size());
|
||||||
for (std::vector<aabb3f>::const_iterator
|
for (std::vector<aabb3f>::const_iterator
|
||||||
i = fixed.begin();
|
i = fixed.begin();
|
||||||
|
@ -70,22 +83,57 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const
|
||||||
writeV3F1000(os, i->MinEdge);
|
writeV3F1000(os, i->MinEdge);
|
||||||
writeV3F1000(os, i->MaxEdge);
|
writeV3F1000(os, i->MaxEdge);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
else if(type == NODEBOX_WALLMOUNTED)
|
case NODEBOX_WALLMOUNTED:
|
||||||
{
|
writeU8(os, type);
|
||||||
|
|
||||||
writeV3F1000(os, wall_top.MinEdge);
|
writeV3F1000(os, wall_top.MinEdge);
|
||||||
writeV3F1000(os, wall_top.MaxEdge);
|
writeV3F1000(os, wall_top.MaxEdge);
|
||||||
writeV3F1000(os, wall_bottom.MinEdge);
|
writeV3F1000(os, wall_bottom.MinEdge);
|
||||||
writeV3F1000(os, wall_bottom.MaxEdge);
|
writeV3F1000(os, wall_bottom.MaxEdge);
|
||||||
writeV3F1000(os, wall_side.MinEdge);
|
writeV3F1000(os, wall_side.MinEdge);
|
||||||
writeV3F1000(os, wall_side.MaxEdge);
|
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)
|
void NodeBox::deSerialize(std::istream &is)
|
||||||
{
|
{
|
||||||
int version = readU8(is);
|
int version = readU8(is);
|
||||||
if(version < 1 || version > 2)
|
if (version < 1 || version > 3)
|
||||||
throw SerializationError("unsupported NodeBox version");
|
throw SerializationError("unsupported NodeBox version");
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
@ -112,6 +160,26 @@ void NodeBox::deSerialize(std::istream &is)
|
||||||
wall_side.MinEdge = readV3F1000(is);
|
wall_side.MinEdge = readV3F1000(is);
|
||||||
wall_side.MaxEdge = readV3F1000(is);
|
wall_side.MaxEdge = readV3F1000(is);
|
||||||
}
|
}
|
||||||
|
else if (type == NODEBOX_CONNECTED)
|
||||||
|
{
|
||||||
|
#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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -261,6 +329,8 @@ void ContentFeatures::reset()
|
||||||
sound_footstep = SimpleSoundSpec();
|
sound_footstep = SimpleSoundSpec();
|
||||||
sound_dig = SimpleSoundSpec("__group");
|
sound_dig = SimpleSoundSpec("__group");
|
||||||
sound_dug = SimpleSoundSpec();
|
sound_dug = SimpleSoundSpec();
|
||||||
|
connects_to.clear();
|
||||||
|
connects_to_ids.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
||||||
|
@ -328,6 +398,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
||||||
os<<serializeString(mesh);
|
os<<serializeString(mesh);
|
||||||
collision_box.serialize(os, protocol_version);
|
collision_box.serialize(os, protocol_version);
|
||||||
writeU8(os, floodable);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentFeatures::deSerialize(std::istream &is)
|
void ContentFeatures::deSerialize(std::istream &is)
|
||||||
|
@ -402,6 +476,9 @@ void ContentFeatures::deSerialize(std::istream &is)
|
||||||
mesh = deSerializeString(is);
|
mesh = deSerializeString(is);
|
||||||
collision_box.deSerialize(is);
|
collision_box.deSerialize(is);
|
||||||
floodable = readU8(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));
|
||||||
}catch(SerializationError &e) {};
|
}catch(SerializationError &e) {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +516,8 @@ public:
|
||||||
virtual bool cancelNodeResolveCallback(NodeResolver *nr);
|
virtual bool cancelNodeResolveCallback(NodeResolver *nr);
|
||||||
virtual void runNodeResolveCallbacks();
|
virtual void runNodeResolveCallbacks();
|
||||||
virtual void resetNodeResolveState();
|
virtual void resetNodeResolveState();
|
||||||
|
virtual void mapNodeboxConnections();
|
||||||
|
virtual bool nodeboxConnects(MapNode from, MapNode to);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addNameIdMapping(content_t i, std::string name);
|
void addNameIdMapping(content_t i, std::string name);
|
||||||
|
@ -1438,6 +1517,39 @@ void CNodeDefManager::resetNodeResolveState()
|
||||||
m_pending_resolve_callbacks.clear();
|
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)
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
|
||||||
|
// the target is just a regular node, so connect no matter back connection
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
//// NodeResolver
|
//// NodeResolver
|
||||||
|
|
|
@ -80,6 +80,7 @@ enum NodeBoxType
|
||||||
NODEBOX_FIXED, // Static separately defined box(es)
|
NODEBOX_FIXED, // Static separately defined box(es)
|
||||||
NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
|
NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side)
|
||||||
NODEBOX_LEVELED, // Same as fixed, but with dynamic height from param2. for snow, ...
|
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
|
struct NodeBox
|
||||||
|
@ -92,6 +93,13 @@ struct NodeBox
|
||||||
aabb3f wall_top;
|
aabb3f wall_top;
|
||||||
aabb3f wall_bottom;
|
aabb3f wall_bottom;
|
||||||
aabb3f wall_side; // being at the -X side
|
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()
|
NodeBox()
|
||||||
{ reset(); }
|
{ reset(); }
|
||||||
|
@ -269,6 +277,9 @@ struct ContentFeatures
|
||||||
SimpleSoundSpec sound_dig;
|
SimpleSoundSpec sound_dig;
|
||||||
SimpleSoundSpec sound_dug;
|
SimpleSoundSpec sound_dug;
|
||||||
|
|
||||||
|
std::vector<std::string> connects_to;
|
||||||
|
std::set<content_t> connects_to_ids;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Methods
|
Methods
|
||||||
*/
|
*/
|
||||||
|
@ -314,6 +325,7 @@ public:
|
||||||
|
|
||||||
virtual void pendNodeResolve(NodeResolver *nr)=0;
|
virtual void pendNodeResolve(NodeResolver *nr)=0;
|
||||||
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
|
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
|
||||||
|
virtual bool nodeboxConnects(const MapNode from, const MapNode to)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IWritableNodeDefManager : public INodeDefManager {
|
class IWritableNodeDefManager : public INodeDefManager {
|
||||||
|
@ -368,6 +380,7 @@ public:
|
||||||
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
|
virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
|
||||||
virtual void runNodeResolveCallbacks()=0;
|
virtual void runNodeResolveCallbacks()=0;
|
||||||
virtual void resetNodeResolveState()=0;
|
virtual void resetNodeResolveState()=0;
|
||||||
|
virtual void mapNodeboxConnections()=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
IWritableNodeDefManager *createNodeDefManager();
|
IWritableNodeDefManager *createNodeDefManager();
|
||||||
|
|
|
@ -535,6 +535,18 @@ ContentFeatures read_content_features(lua_State *L, int index)
|
||||||
f.node_box = read_nodebox(L, -1);
|
f.node_box = read_nodebox(L, -1);
|
||||||
lua_pop(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);
|
||||||
|
while (lua_next(L, table) != 0) {
|
||||||
|
// Value at -1
|
||||||
|
f.connects_to.push_back(lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
lua_getfield(L, index, "selection_box");
|
lua_getfield(L, index, "selection_box");
|
||||||
if(lua_istable(L, -1))
|
if(lua_istable(L, -1))
|
||||||
f.selection_box = read_nodebox(L, -1);
|
f.selection_box = read_nodebox(L, -1);
|
||||||
|
@ -627,25 +639,31 @@ NodeBox read_nodebox(lua_State *L, int index)
|
||||||
nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
|
nodebox.type = (NodeBoxType)getenumfield(L, index, "type",
|
||||||
ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR);
|
ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR);
|
||||||
|
|
||||||
lua_getfield(L, index, "fixed");
|
#define NODEBOXREAD(n, s) \
|
||||||
if(lua_istable(L, -1))
|
do { \
|
||||||
nodebox.fixed = read_aabb3f_vector(L, -1, BS);
|
lua_getfield(L, index, (s)); \
|
||||||
lua_pop(L, 1);
|
if (lua_istable(L, -1)) \
|
||||||
|
(n) = read_aabb3f(L, -1, BS); \
|
||||||
|
lua_pop(L, 1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
lua_getfield(L, index, "wall_top");
|
#define NODEBOXREADVEC(n, s) \
|
||||||
if(lua_istable(L, -1))
|
do { \
|
||||||
nodebox.wall_top = read_aabb3f(L, -1, BS);
|
lua_getfield(L, index, (s)); \
|
||||||
lua_pop(L, 1);
|
if (lua_istable(L, -1)) \
|
||||||
|
(n) = read_aabb3f_vector(L, -1, BS); \
|
||||||
lua_getfield(L, index, "wall_bottom");
|
lua_pop(L, 1); \
|
||||||
if(lua_istable(L, -1))
|
} while (0)
|
||||||
nodebox.wall_bottom = read_aabb3f(L, -1, BS);
|
NODEBOXREADVEC(nodebox.fixed, "fixed");
|
||||||
lua_pop(L, 1);
|
NODEBOXREAD(nodebox.wall_top, "wall_top");
|
||||||
|
NODEBOXREAD(nodebox.wall_bottom, "wall_bottom");
|
||||||
lua_getfield(L, index, "wall_side");
|
NODEBOXREAD(nodebox.wall_side, "wall_side");
|
||||||
if(lua_istable(L, -1))
|
NODEBOXREADVEC(nodebox.connect_top, "connect_top");
|
||||||
nodebox.wall_side = read_aabb3f(L, -1, BS);
|
NODEBOXREADVEC(nodebox.connect_bottom, "connect_bottom");
|
||||||
lua_pop(L, 1);
|
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;
|
return nodebox;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
|
||||||
{NODEBOX_FIXED, "fixed"},
|
{NODEBOX_FIXED, "fixed"},
|
||||||
{NODEBOX_WALLMOUNTED, "wallmounted"},
|
{NODEBOX_WALLMOUNTED, "wallmounted"},
|
||||||
{NODEBOX_LEVELED, "leveled"},
|
{NODEBOX_LEVELED, "leveled"},
|
||||||
|
{NODEBOX_CONNECTED, "connected"},
|
||||||
{0, NULL},
|
{0, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,9 @@ Server::Server(
|
||||||
// Perform pending node name resolutions
|
// Perform pending node name resolutions
|
||||||
m_nodedef->runNodeResolveCallbacks();
|
m_nodedef->runNodeResolveCallbacks();
|
||||||
|
|
||||||
|
// unmap node names for connected nodeboxes
|
||||||
|
m_nodedef->mapNodeboxConnections();
|
||||||
|
|
||||||
// init the recipe hashes to speed up crafting
|
// init the recipe hashes to speed up crafting
|
||||||
m_craftdef->initHashes(this);
|
m_craftdef->initHashes(this);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue