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

Beds and slabs in low rooms can get you stuck, but only across y = 0 #6197

Closed
JRottm opened this issue Aug 3, 2017 · 26 comments
Closed

Beds and slabs in low rooms can get you stuck, but only across y = 0 #6197

JRottm opened this issue Aug 3, 2017 · 26 comments
Assignees
Labels
Bug Issues that were confirmed to be a bug @ Server / Client / Env.

Comments

@JRottm
Copy link
Contributor

JRottm commented Aug 3, 2017

To reproduce, build a small room which is 2 nodes high. Place a bed in it. So, all nodes counted from bottom to top should be 1x ground, 1x bed, 1x air, 1x ceiling. Then walk towards the bed. Normally the bed blocks you from walking on top of it, because the ceiling is too low for that. BUT, if this is done near sea level then you CAN walk onto the bed and will get stuck with your head inside the ceiling.

To be precise, the bug can be triggered at the 2 possible different heights where ceiling Y>0, but ground (and player) Y<0:

  • ceiling nodes at Y=+2, bed at Y=0, ground at Y=-1 (i.e. player standing at Y=-0.5)
  • ceiling nodes at Y=+1, bed at Y=-1, ground at Y=-2 (i.e. player standing at Y=-1.5)

On servers it's quite possible that the ceiling blocks may be protected by someone else so you can't dig yourself out, and that you don't have noclip or teleport permissions either. In this case the only way out is dying. If you've also slept in the bed and respawn there then you're in trouble.

Tested locally with Minetest Game from Minetest 0.4.15 and 0.4.16 on Debian 9, 64 bit, and on a public game server running 0.4.16. Previously minetest/minetest_game#1865.

@Wuzzy2
Copy link
Contributor

Wuzzy2 commented Aug 3, 2017

This issue needs to be moved to https://github.com/minetest/minetest_game.

@JRottm
Copy link
Contributor Author

JRottm commented Aug 3, 2017

@Wuzzy2 this is where I came from in the first place. I got sent here by @SmallJoker. Please see the link at the end of my bug description.

@SmallJoker
Copy link
Member

SmallJoker commented Aug 3, 2017

@Wuzzy2, I requested to move this issue as it addresses the collision detection, which is a core thing. MTG's beds only trigger this behaviour.

@SmallJoker SmallJoker added @ Server / Client / Env. Bug Issues that were confirmed to be a bug labels Aug 3, 2017
@JRottm
Copy link
Contributor Author

JRottm commented Aug 3, 2017

I just found out that the bug can be triggered only near sea level where ceiling and ground Y coordinates have different (math.) sign. I edited my bug description above to describe the precise conditions.

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

Happens for fancy and simple beds?

@Ezhh
Copy link
Contributor

Ezhh commented Aug 3, 2017

It previously happened for both kinds of beds on Dark Lands.

@Ezhh
Copy link
Contributor

Ezhh commented Aug 3, 2017

Tested and confirmed it's at those two specific heights only.

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

Confirmed in latest master with new move code (the default).
I can walk up onto the simple bed from all 6 approaches, but the head of the fancy bed blocks this from happening.
This bug also happens with 1/2 slabs and 1/4 slabs (snow), but only around y = 0, so not bed specific.
Something to do with the effect of the y co-ordinate change of sign, maybe altering the rounding to an integer in collision detection.

@paramat paramat changed the title [Bug] Beds in low rooms can get you stuck Beds and slabs in low rooms can get you stuck, but only across y = 0 Aug 3, 2017
@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

Happens with a slab of height 0.56 (same height as beds).

	node_box = {
		type = "fixed",
		fixed = {
			{-0.5, -0.5, -0.5, 0.5, 0.06, 0.5},
		},
	},

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

Confirmed collision is determined by nodeboxes not selectionbox, which is why the fancy bed head blocks approach.

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

Also happens in singleplayer 0.4.16 stable so not caused by 'settable player collisionbox' changes.

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

During experimentation this fixes it:
https://github.com/minetest/minetest/blob/master/src/collision.cpp#L260

	/*
		Collect node boxes in movement range
	*/
	std::vector<NearbyCollisionInfo> cinfo;
	{
	//TimeTaker tt2("collisionMoveSimple collect boxes");
	ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);

	v3s16 oldpos_i = floatToInt(*pos_f, BS);
	v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS);
	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
	s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
	s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
-	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
+	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 2;
	s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;

Seems to be a float to int rounding error when collecting nearby nodeboxes, such that the nodeboxes of the low ceiling are not collected.

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

If i print out max_y it is 1 while the ceiling is at y = 2, so nodeboxes just above the player are not being collected for collision detection.

	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
	s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
	s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
	s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
	printf("maxy %d\n", max_y);

@paramat
Copy link
Contributor

paramat commented Aug 3, 2017

By printing max_y i confirm that the current code correctly includes the ceiling when both player feet and ceiling are above, or below, y = 0.

@paramat paramat self-assigned this Aug 3, 2017
@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

I'll try some casts, in my experience converting floats to ints can produce errors when division is involved.

@Ekdohibs
Copy link
Member

Ekdohibs commented Aug 4, 2017

If this only happens across y = 0, it seems to me it must be related to round-towards-zero division or cast behaviour. Consistently rounding towards negative infinity should fix that easily.

@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

With current code printing y_min also:
https://github.com/minetest/minetest/blob/master/src/collision.cpp#L260

	v3s16 oldpos_i = floatToInt(*pos_f, BS);
	v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS);
	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
	s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
	s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
	s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
	printf("min_y %d\n", min_y);
	printf("max_y %d\n", max_y);

and with player not moving (speed = 0).

Across y = 0 i get:
player at y 0 and 1
min_y -2 (1 too low)
max_y 1 (1 too low)

Well below y = 0 i get:
player at y -5 and -4
min_y -7 (1 too low)
max_y -3 (correct)

Well above y = 0 i get:
player at y 3 and 4
min_y 1 (1 too low)
max_y 5 (correct)

So the volume of nodebox collection is too big by 1 node in almost all situations. Which means we can gain performance by fixing this.

@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

Current code, also printing 'oldpos_i.Y':https://github.com/minetest/minetest/blob/master/src/collision.cpp#L260

	v3s16 oldpos_i = floatToInt(*pos_f, BS);
	v3s16 newpos_i = floatToInt(*pos_f + *speed_f * dtime, BS);
	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
	s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
	s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
	s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
	printf("oldpos_i.Y %d\n", oldpos_i.Y);
	printf("min_y %d\n", min_y);
	printf("max_y %d\n", max_y);

Well above y = 0 i get:
player at y 3 and 4
oldpos_i 3 (correct)
min_y 1 (1 too low)
max_y 5 (correct)

Across y = 0 i get:
player at y 0 and 1
oldpos_i -1 (1 too low)
min_y -2 (1 too low)
max_y 1 (1 too low)

Well below y = 0 i get:
player at y -5 and -4
oldpos_i -6 (1 too low)
min_y -7 (1 too low)
max_y -3 (correct)

@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

I can correct oldpos_i by adding a small value to it's Y component:
https://github.com/minetest/minetest/blob/master/src/collision.cpp#L260

	v3f pos_f_mod = *pos_f;
	pos_f_mod.Y += 0.001f;
	v3s16 oldpos_i = floatToInt(pos_f_mod, BS);
	v3s16 newpos_i = floatToInt(pos_f_mod + *speed_f * dtime, BS);
	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
	s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
	s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
	s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;
	printf("oldpos_i.Y %d\n", oldpos_i.Y);
	printf("min_y %d\n", min_y);
	printf("max_y %d\n", max_y);

So it seems that when below y = 0 the player pos '*pos_f' is being rounded to a value 1 node too low by 'floatToInt()'. But how is this possible when 'floatToInt()' was specifically created to avoid this type of error?

// Size of node in floating-point units
// The original idea behind this is to disallow plain casts between
// floating-point and integer positions, which potentially give wrong
// results. (negative coordinates, values between nodes, ...)
// Use floatToInt(p, BS) and intToFloat(p, BS).
#define BS (10.0)

@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

I can correct oldpos_i by adding a small value to it's Y component:

However when doing so running up steps seems different and not as smooth, and movement sometimes seems jittery.

@JRottm
Copy link
Contributor Author

JRottm commented Aug 4, 2017

@paramat I think I have a fix, hang on ...

JRottm added a commit to JRottm/minetest that referenced this issue Aug 4, 2017
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
JRottm added a commit to JRottm/minetest that referenced this issue Aug 4, 2017
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
@JRottm
Copy link
Contributor Author

JRottm commented Aug 4, 2017

PR #6209

This is the first time I even looked at MT code, I hope I didn't mess up due to misunderstanding some basic reasoning behind the code I changed. Short test-playing looked ok, as far as I could tell.

@paramat Would you be so kind and test it, too? Thanks!

@JRottm
Copy link
Contributor Author

JRottm commented Aug 4, 2017

I also suspect another small general problem: the player is always standing exactly on the bondary between 2 nodes e.g. Y=1.5 is exactly between nodes Y=1 and Y=2. floatToInt() and myround() will round this differently depending where you are: rounded up above sea level and rounded down when underground. This inconsistency comes from the way the coordinates are calculated, independent of the specific C++ code.

If I understand this right the only negative effect should be a tiny bit of lost performance when moving underground, because 1 node level more than necessary is checked for collisions. This might be amended by adding a tiny offset to min.Y, like you (@paramat) already considered above. I tested the workaround, and it seemed to work, but I didn't want to do it, because it felt ugly and arbitrary. What you you think?

@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

I thought floatToInt() was designed to not alter rounding dependent on sign of Y? Well maybe it doesn't.
Let me test first then i'll think on that.

@JRottm
Copy link
Contributor Author

JRottm commented Aug 4, 2017

floatToInt() and myround() round to the nearest integer i.e. +1.3-->+1, -0.7-->-1. In case of +/-x.5 they always round "outwards" i,e, +1.5-->+2, -1.5-->-2. That's absolutely correct and logical for an abstract rounding function..

But in the specific case of a player standing on +1.5 rounding (upwards) to +2 selects the air node containing the player's legs. Whereas with a player standing on -1.5 rounding (downwards) to -2 selects the ground node below his/her feet. Finally we extend the search area with +/-v3s16(1,1,1) ==> above sea level-->ground node-->ok; whereas underground-->even 1 node below ground-->also ok, but more node checking than absolutely necessary.

I was thinking about this change (additional to what I already committed) to work around it, but it felt like an ugly hack, so my vote would be NOT to do that and just accept that we might check a few nodes more than necessary, that's no big deal:

        v3f newpos_f = *pos_f + *speed_f * dtime;
        v3f minpos_f(MYMIN(pos_f->X, newpos_f.X),
                     MYMIN(pos_f->Y, newpos_f.Y) + 0.01*BS,
                     MYMIN(pos_f->Z, newpos_f.Z));
        v3f maxpos_f(MYMAX(pos_f->X, newpos_f.X),
        ...

@paramat
Copy link
Contributor

paramat commented Aug 4, 2017

Ok, this text seemed to suggest otherwise, but of course we have to go by the code of floatToInt():

// Size of node in floating-point units
// The original idea behind this is to disallow plain casts between
// floating-point and integer positions, which potentially give wrong
// results. (negative coordinates, values between nodes, ...)
// Use floatToInt(p, BS) and intToFloat(p, BS).
#define BS (10.0)

This is a bit worrying since MT uses floatToInt() heavily for converting float co-ords to int co-ords.

/*
	Returns integer position of node in given floating point position
*/
inline v3s16 floatToInt(v3f p, f32 d)
{
	return v3s16(
		(p.X + (p.X > 0 ? d / 2 : -d / 2)) / d,
		(p.Y + (p.Y > 0 ? d / 2 : -d / 2)) / d,
		(p.Z + (p.Z > 0 ? d / 2 : -d / 2)) / d);
}

I'm not sure about the behaviour of this, as it returns integers from expressions including floats and divisions, i've experienced weird behaviour in these situations. Best to test how it behaves due to negative sign.

If your suggested small value is actually needed i think it's worth it, because if we are collecting nodeboxes in 9 too many nodes this can be a lot of extra nodeboxes collected. There may be complex nodes with multiple nodeboxes. The single addition is much more lightweight.

JRottm added a commit to JRottm/minetest that referenced this issue Aug 5, 2017
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
sfan5 pushed a commit that referenced this issue Aug 5, 2017
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
@sfan5 sfan5 closed this as completed Aug 5, 2017
sfan5 pushed a commit that referenced this issue Nov 19, 2017
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
@sfan5 sfan5 mentioned this issue Dec 5, 2017
sfan5 pushed a commit that referenced this issue Dec 5, 2017
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
sfan5 pushed a commit that referenced this issue Feb 3, 2018
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
SmallJoker pushed a commit that referenced this issue Jun 3, 2018
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
osjc pushed a commit to osjc/minetest that referenced this issue Jan 11, 2019
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
osjc pushed a commit to osjc/minetest that referenced this issue Jan 23, 2019
To determine the area (nodes) where a player movement took place
collisionMoveSimple() first took the old/new player coordinates and rounded
them to integers, then added the player character's collision box and
implicitely rounded the result. This has 2 problems:

Rounding the position and the box seperately, then adding the resulting
integers means you get twice the rounding error. And implicit rounding
always rounds towards 0.0, unlike floatToInt(), which rounds towards the
closest integer.

Previous (simplified) behavior: round(pos)+(int)box, for example player at
Y=0.9, body is 1.75m high: round(0.9)+(int)1.75 = 1+1 = 2.
==> A character's height of 1.75m always got rounded down to 1m, its width
of +/-0.3 even became 0.

Fixed by adding the floats first, then rounding properly: round(pos+box) =
round(0.9+1.75) = round(2.65) = 3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Issues that were confirmed to be a bug @ Server / Client / Env.
Projects
None yet
Development

No branches or pull requests

7 participants