Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[no sq] Server/env optimizations #14582

Merged
merged 5 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions builtin/game/misc_s.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,25 @@ function core.encode_png(width, height, data, compression)

return o_encode_png(width, height, data, compression or 6)
end

-- Helper that pushes a collisionMoveResult structure
if core.set_push_moveresult1 then
-- must match CollisionAxis in collision.h
local AXES = {"x", "y", "z"}
-- <=> script/common/c_content.cpp push_collision_move_result()
core.set_push_moveresult1(function(b0, b1, b2, axis, npx, npy, npz, v0x, v0y, v0z, v1x, v1y, v1z)
return {
touching_ground = b0,
collides = b1,
standing_on_object = b2,
collisions = {{
type = "node",
axis = AXES[axis],
node_pos = vector.new(npx, npy, npz),
old_velocity = vector.new(v0x, v0y, v0z),
new_velocity = vector.new(v1x, v1y, v1z),
}},
}
end)
core.set_push_moveresult1 = nil
end
172 changes: 72 additions & 100 deletions src/collision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,45 +36,47 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#warning "-ffast-math is known to cause bugs in collision code, do not use!"
#endif

namespace {

struct NearbyCollisionInfo {
// node
NearbyCollisionInfo(bool is_ul, int bouncy, const v3s16 &pos,
const aabb3f &box) :
is_unloaded(is_ul),
NearbyCollisionInfo(bool is_ul, int bouncy, v3s16 pos, const aabb3f &box) :
obj(nullptr),
bouncy(bouncy),
box(box),
position(pos),
box(box)
bouncy(bouncy),
is_unloaded(is_ul),
is_step_up(false)
{}

// object
NearbyCollisionInfo(ActiveObject *obj, int bouncy,
const aabb3f &box) :
is_unloaded(false),
NearbyCollisionInfo(ActiveObject *obj, int bouncy, const aabb3f &box) :
obj(obj),
box(box),
bouncy(bouncy),
box(box)
is_unloaded(false),
is_step_up(false)
{}

inline bool isObject() const { return obj != nullptr; }

bool is_unloaded;
bool is_step_up = false;
ActiveObject *obj;
int bouncy;
v3s16 position;
aabb3f box;
v3s16 position;
u8 bouncy;
// bitfield to save space
bool is_unloaded:1, is_step_up:1;
};

// Helper functions:
// Truncate floating point numbers to specified number of decimal places
// in order to move all the floating point error to one side of the correct value
static inline f32 truncate(const f32 val, const f32 factor)
inline f32 truncate(const f32 val, const f32 factor)
{
return truncf(val * factor) / factor;
}

static inline v3f truncate(const v3f& vec, const f32 factor)
inline v3f truncate(const v3f vec, const f32 factor)
{
return v3f(
truncate(vec.X, factor),
Expand All @@ -83,6 +85,8 @@ static inline v3f truncate(const v3f& vec, const f32 factor)
);
}

}

// Helper function:
// Checks for collision of a moving aabbox with a static aabbox
// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
Expand Down Expand Up @@ -270,11 +274,12 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
/*
Collect node boxes in movement range
*/
std::vector<NearbyCollisionInfo> cinfo;
{
//TimeTaker tt2("collisionMoveSimple collect boxes");
ScopeProfiler sp2(g_profiler, PROFILER_NAME("collision collect boxes"), SPT_AVG, PRECISION_MICRO);

// cached allocation
thread_local std::vector<NearbyCollisionInfo> cinfo;
cinfo.clear();

{
v3f minpos_f(
MYMIN(pos_f->X, newpos_f.X),
MYMIN(pos_f->Y, newpos_f.Y) + 0.01f * BS, // bias rounding, player often at +/-n.5
Expand All @@ -288,20 +293,22 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);

const auto *nodedef = gamedef->getNodeDefManager();
bool any_position_valid = false;

thread_local std::vector<aabb3f> nodeboxes;

v3s16 p;
for (p.X = min.X; p.X <= max.X; p.X++)
for (p.Z = min.Z; p.Z <= max.Z; p.Z++)
for (p.Y = min.Y; p.Y <= max.Y; p.Y++)
for (p.Z = min.Z; p.Z <= max.Z; p.Z++) {
for (p.X = min.X; p.X <= max.X; p.X++) {
bool is_position_valid;
MapNode n = map->getNode(p, &is_position_valid);

if (is_position_valid && n.getContent() != CONTENT_IGNORE) {
// Object collides into walkable nodes

any_position_valid = true;
const NodeDefManager *nodedef = gamedef->getNodeDefManager();
const ContentFeatures &f = nodedef->get(n);

if (!f.walkable)
Expand All @@ -310,36 +317,10 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
// Negative bouncy may have a meaning, but we need +value here.
int n_bouncy_value = abs(itemgroup_get(f.groups, "bouncy"));

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);
u8 neighbors = n.getNeighbors(p, map);

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);
nodeboxes.clear();
n.getCollisionBoxes(nodedef, &nodeboxes, neighbors);

// Calculate float position only once
v3f posf = intToFloat(p, BS);
Expand All @@ -365,78 +346,69 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
return result;
}

} // tt2
}

if(collideWithObjects)
{
/* add object boxes to cinfo */
/*
Collect object boxes in movement range
*/

auto process_object = [] (ActiveObject *object) {
if (object && object->collideWithObjects()) {
aabb3f box;
if (object->getCollisionBox(&box))
cinfo.emplace_back(object, 0, box);
}
};

if (collideWithObjects) {
// Calculate distance by speed, add own extent and 1.5m of tolerance
const f32 distance = speed_f->getLength() * dtime +
box_0.getExtent().getLength() + 1.5f * BS;

std::vector<ActiveObject*> objects;
#ifndef SERVER
ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
if (c_env != 0) {
// Calculate distance by speed, add own extent and 1.5m of tolerance
f32 distance = speed_f->getLength() * dtime +
box_0.getExtent().getLength() + 1.5f * BS;
if (c_env) {
std::vector<DistanceSortedActiveObject> clientobjects;
c_env->getActiveObjects(*pos_f, distance, clientobjects);

for (auto &clientobject : clientobjects) {
// Do collide with everything but itself and the parent CAO
if (!self || (self != clientobject.obj &&
self != clientobject.obj->getParent())) {
objects.push_back((ActiveObject*) clientobject.obj);
process_object(clientobject.obj);
}
}
}
else
#endif
{
if (s_env != NULL) {
// Calculate distance by speed, add own extent and 1.5m of tolerance
f32 distance = speed_f->getLength() * dtime +
box_0.getExtent().getLength() + 1.5f * BS;

// search for objects which are not us, or we are not its parent
// we directly use the callback to populate the result to prevent
// a useless result loop here
auto include_obj_cb = [self, &objects] (ServerActiveObject *obj) {
if (!obj->isGone() &&
(!self || (self != obj && self != obj->getParent()))) {
objects.push_back((ActiveObject *)obj);
}
return false;
};

std::vector<ServerActiveObject *> s_objects;
s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb);
}
}

for (std::vector<ActiveObject*>::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
ActiveObject *object = *iter;

if (object && object->collideWithObjects()) {
aabb3f object_collisionbox;
if (object->getCollisionBox(&object_collisionbox))
cinfo.emplace_back(object, 0, object_collisionbox);
}
}
#ifndef SERVER
if (self && c_env) {
// add collision with local player
LocalPlayer *lplayer = c_env->getLocalPlayer();
if (lplayer->getParent() == nullptr) {
aabb3f lplayer_collisionbox = lplayer->getCollisionbox();
v3f lplayer_pos = lplayer->getPosition();
lplayer_collisionbox.MinEdge += lplayer_pos;
lplayer_collisionbox.MaxEdge += lplayer_pos;
ActiveObject *obj = (ActiveObject*) lplayer->getCAO();
auto *obj = (ActiveObject*) lplayer->getCAO();
cinfo.emplace_back(obj, 0, lplayer_collisionbox);
}
}
else
#endif
} //tt3
if (s_env) {
// search for objects which are not us, or we are not its parent.
// we directly process the object in this callback to avoid useless
// looping afterwards.
auto include_obj_cb = [self, &process_object] (ServerActiveObject *obj) {
if (!obj->isGone() &&
(!self || (self != obj && self != obj->getParent()))) {
process_object(obj);
}
return false;
};

// nothing is put into this vector
std::vector<ServerActiveObject*> s_objects;
s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb);
}
}

/*
Collision detection
Expand Down Expand Up @@ -572,7 +544,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,

if (is_collision) {
info.axis = nearest_collided;
result.collisions.push_back(info);
result.collisions.push_back(std::move(info));
}
}
}
Expand Down
30 changes: 14 additions & 16 deletions src/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,8 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
if (state->m_objects_pointable) {
std::vector<PointedThing> found;
getSelectedActiveObjects(state->m_shootline, found, state->m_pointabilities);
for (const PointedThing &pointed : found) {
state->m_found.push(pointed);
}
for (auto &pointed : found)
state->m_found.push(std::move(pointed));
}
// Set search range
core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
Expand All @@ -150,14 +149,10 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
}

Map &map = getMap();
// If a node is found, this is the center of the
// first nodebox the shootline meets.
v3f found_boxcenter(0, 0, 0);
// The untested nodes are in this range.
core::aabbox3d<s16> new_nodes;
std::vector<aabb3f> boxes;
while (state->m_iterator.m_current_index <= lastIndex) {
// Test the nodes around the current node in search_range.
new_nodes = state->m_search_range;
core::aabbox3d<s16> new_nodes = state->m_search_range;
new_nodes.MinEdge += state->m_iterator.m_current_node_pos;
new_nodes.MaxEdge += state->m_iterator.m_current_node_pos;

Expand Down Expand Up @@ -185,9 +180,9 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
}

// For each untested node
for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++)
for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++)
for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++)
for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
MapNode n;
v3s16 np(x, y, z);
bool is_valid_position;
Expand All @@ -205,7 +200,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)

PointedThing result;

std::vector<aabb3f> boxes;
boxes.clear();
n.getSelectionBoxes(nodedef, &boxes,
n.getNeighbors(np, &map));

Expand All @@ -215,6 +210,9 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
float min_distance_sq = 10000000;
// ID of the current box (loop counter)
u16 id = 0;
// If a node is found, this is the center of the
// first nodebox the shootline meets.
v3f found_boxcenter(0, 0, 0);

// Do calculations relative to the node center
// to translate the ray rather than the boxes
Expand Down Expand Up @@ -253,7 +251,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
result.node_undersurface = np;
result.distanceSq = min_distance_sq;
// Set undersurface and abovesurface nodes
f32 d = 0.002 * BS;
const f32 d = 0.002 * BS;
v3f fake_intersection = result.intersection_point;
found_boxcenter += npf; // translate back to world coords
// Move intersection towards its source block.
Expand All @@ -276,8 +274,9 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
fake_intersection, BS);
result.node_abovesurface = result.node_real_undersurface
+ floatToInt(result.intersection_normal, 1.0f);

// Push found PointedThing
state->m_found.push(result);
state->m_found.push(std::move(result));
// If this is nearer than the old nearest object,
// the search can be shorter
s16 newIndex = state->m_iterator.getIndex(
Expand All @@ -297,9 +296,8 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
} else {
*result_p = state->m_found.top();
state->m_found.pop();
if (result_p->pointability == PointabilityType::POINTABLE_BLOCKING) {
if (result_p->pointability == PointabilityType::POINTABLE_BLOCKING)
result_p->type = POINTEDTHING_NOTHING;
}
}
}

Expand Down