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

Optimizing getPossibleEyeHeights() #1732

Merged
merged 16 commits into from
Oct 31, 2024

Conversation

Axionize
Copy link
Contributor

This PR makes getPossibleEyeHeights() return double[] and in Standing -> Sneaking -> Elytra order, which is far more likely, reducing the number of average iterations taken in practice to process these checks. In addition, there's no reason to use the list object over the more efficient primitive type, which this PR also changes.

getPossibleEyeHeights() is called for 5 checks in the current codebase. In most cases, its return values are iterated over in a loop to verify some player behavior's like reach or block placement is legitimate. In each of these cases it is far more likely that the player is standing than say flying on an elytra or swimming and hitting an entity.

@AoElite
Copy link
Collaborator

AoElite commented Sep 22, 2024

You could just cache the list/array. It would need to be recalculated if the scale changes though.

@Axionize
Copy link
Contributor Author

Axionize commented Sep 22, 2024

The thought crossed my mind but I saw the scale and wasn't sure how to handle it being changed.

I don't know how to detect when an attribute has been changed to know when to update the list/array.

In addition, I'm unsure if I need to do anything special in order to propagate that change in a latency-compensated way if I cached the array. (IE how to handle the case where a laggy player has scale 2 and the server sets their scale to 1 but the player doesn't receive that packet until 500 ms later.

@Axionize
Copy link
Contributor Author

Axionize commented Sep 22, 2024

I've cached what I can and added additional checks to see if the player is gliding, swimming, or sneaking and returning a different array for each of these cases.

I've been told by Cotander that these are desynced, which is why we need to return an array of possible eye heights to begin with. Regardless I've found this code reduces iterations for when the player is standing, sneaking, or flying by ~3x as expected.

Even in situations where the player desyncs, which are comparatively rare to the norm, correctness will obviously not be affected.

In addition to the existing checks that use this function, I've found it to be a big boon to the performance of LineOfSightPlace and @SamB440's experimental reach checks

All that remains is to recalculate on scale changes. I'd appreciate your guidance in helping me with that if possible.

@Axionize
Copy link
Contributor Author

Axionize commented Sep 24, 2024

I've handled changing the array when scale changes with a setRewriter. However while making these changes I've come across 1 major and 2 minor concerns.

We don't check when the player is crawling?

  • As usual the code still works for this case but LOSP goes through 42 iterations whenever a player is crawling.
  • If we can add a bool for isCrawling similar to isSwimming we can handle it in the same case (since height will still be 0.4f).
  • It doesn't have to be tick-perfect, just good enough and right-enough most of the time so we only have to do 1 iteration most of the time.

  • 1.14 and below in the original code references a scale attribute, which is the version it was added in, yet player's couldn't have their scale changed until 1.20.5 and the attribute handler rejects changes for < 1.20.5. Why does the possibleEyeHeights array bother checking scale between 1.14 and 1.20.5? It shouldn't have any big cost but its confusing to see nonetheless, shouldn't we only bother dealing with scale for eye height calculations in 1.20.5?

  • setRewriter seems to be called more often than it needs to be. I haven't investigated the cause deeply but it seems strange.

@Axionize
Copy link
Contributor Author

Axionize commented Sep 24, 2024

Update: Vanilla's isCrawling() function just returns isSwimming && !touchingWater

If we go vanilla's route of not storing a bool for crawling, we may made need to rethink how we treat isSwimming, especially in simulation.

Copy link
Collaborator

@SamB440 SamB440 left a comment

Choose a reason for hiding this comment

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

You should get the attribute on GrimPlayer and do the set rewriter there. Currently you are doing it for every entity.

@Axionize
Copy link
Contributor Author

Tested on 1.8 and 1.21. Everything works. LOSP gets the correct ray on the first iteration basically everytime, effectively making LOSP O(1).

In summary, I believe this PR in its current form is ready for merging.

For future todos, the only thing would be to fix ViaVersion/ViaRewind/ViaBackwards to deal with legacy players who have scales that aren't 1.

I discovered that ViaVersion doesn't handle a player joining on 1.21 -> changing scale -> joining on legacy version very well. We also flagged simulation a ton. I changed the attribute setWriter to accommodate this and most of the instant simulation falses were fixed. Nevertheless via veresion still have issues that persist without grimac being installed

@Axionize
Copy link
Contributor Author

@SamB440 Done. Can you merge this?

@Axionize
Copy link
Contributor Author

All requested changes have been made/addressed

Copy link
Collaborator

@MachineBreaker MachineBreaker left a comment

Choose a reason for hiding this comment

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

LGTM

@SamB440 SamB440 merged commit 30c2d36 into GrimAnticheat:2.0 Oct 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants