-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Implements a cache for train collision checks for better performance #9513
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
base: mc1.21.1/dev
Are you sure you want to change the base?
Implements a cache for train collision checks for better performance #9513
Conversation
|
If this works properly this is brilliant @stacode123 and definately needed. We've turned train collisions off entirely to help with lag but thats definately not a fix most servers want to implement. Using Copilot is a little worrying though because you need to know exactly whats going on for this. Make sure you've tested it thoroughly and used the debugger to step through the code to see whats going on and if it aligns with what you expect. |
Jozufozu
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very excited about this and the spark profile is extremely promising. A 5mspt difference is huge
How many trains did you use in your test world? It would be helpful to upload a screenshot of the chaos 😆
Also after the requested changes and before this merges I'd like to request a longer profile to compare. I'm a little nervous the 16 second profiles may not have enough signal to noise
src/main/java/com/simibubi/create/content/trains/entity/Train.java
Outdated
Show resolved
Hide resolved
src/main/java/com/simibubi/create/content/trains/entity/Train.java
Outdated
Show resolved
Hide resolved
src/main/java/com/simibubi/create/content/trains/entity/Train.java
Outdated
Show resolved
Hide resolved
src/main/java/com/simibubi/create/content/trains/entity/Train.java
Outdated
Show resolved
Hide resolved
| collisionCache.addSegment(lastPoint, start, segmentIndex++); | ||
| } | ||
|
|
||
| if (segmentIndex < totalSegments) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will segmentIndex ever be greater or equal to totalSegments? Same for above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, do we need these checks? They could hide critical bugs for the same reason I described here: #9513 (comment)
src/main/java/com/simibubi/create/content/trains/entity/Train.java
Outdated
Show resolved
Hide resolved
src/main/java/com/simibubi/create/content/trains/entity/Train.java
Outdated
Show resolved
Hide resolved
| if (length > 0.0001) { | ||
| direction[index] = diff.normalize(); | ||
| } else { | ||
| direction[index] = Vec3.ZERO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How will this happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you've removed this but please justify it
|
To be honest it's rare u see such a stark contrast between before and after. A similar number of players are connected to the server as well so the difference likely isn't down to that. :) |
…llisionCache calls.
|
@Jozufozu Please review the changes :D |
|
|
||
| Vec3 start = (speed < 0 ? trailingPoint : leadingPoint).getPosition(graph); | ||
| Vec3 end = (speed < 0 ? leadingPoint : trailingPoint).getPosition(graph); | ||
| Vec3 start = collisionCache.start[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did the speed < 0 checks go? If the train is moving backwards we need to check for collisions with the trailing end as if it were the head
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh also do we know for sure that the collision cache is non-null here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- That is handled when collision cache is created.
- Earlier in the train tick we updated the collision cache but i can add a null check just to be sure
|
|
||
| Vec3 normedDiff = diff.normalize(); | ||
| double maxDistanceSqr = Math.pow(AllConfigs.server().trains.maxAssemblyLength.get(), 2.0); | ||
| double maxDistanceSqr = AllConfigs.server().trains.maxAssemblyLength.get()*AllConfigs.server().trains.maxAssemblyLength.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please only fetch the config value once. This call showed up in your spark profile already and I do not want to rely on the JVM to deduplicate these.
Also formatting
double maxDistance = AllConfigs.server().trains.maxAssemblyLength.get();
double maxDistanceSqr = maxDistance * maxDistance;|
|
||
| // Use cached data if available, otherwise fall back to old method | ||
| if (train.collisionCache != null && train.collisionCache.isValid()) { | ||
| if (train.collisionCache != null ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit invert the check and early continue to save on indentation
| if (length > 0.0001) { | ||
| direction[index] = diff.normalize(); | ||
| } else { | ||
| direction[index] = Vec3.ZERO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you've removed this but please justify it
| collisionCache.addSegment(lastPoint, start, segmentIndex++); | ||
| } | ||
|
|
||
| if (segmentIndex < totalSegments) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, do we need these checks? They could hide critical bugs for the same reason I described here: #9513 (comment)
| if(segmentIndex == 0) { | ||
| collisionCache=null; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit formatting
if (segmentIndex == 0) {
collisionCache = null;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I dont know why it was there in the first place
- ok ill remove them
- fixed
| for (Train train : movingTrains){ | ||
| train.updateCollisionCache(); | ||
| train.earlyTick(level);} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
formatting please
for (Train train : movingTrains) {
train.updateCollisionCache();
train.earlyTick(level);
}do we not need the collision cache for waiting trains? do we not expect moving trains to collide with trains that are stationary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wont stationary trains have already built up cache ? And trying to calculate it again would just yield the same result?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about on initial level load? Can we really assume that a train that just became stationary this tick will have valid positions in the cache from the previous tick where it was moving?
Still, that's a good point. If you're feeling ambition you could probably check some easy to compute value at the start of updateCollisionCache to see if the train has moved from the previous tick and decide whether to update the collision cache. If you do though, I'd like to see proof that it works and waiting trains still get collided with
| if (derailed || graph == null) { | ||
| if (collisionCache != null) | ||
| collisionCache.invalidate(); | ||
| collisionCache = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably don't need the collisionCache != null check anymore. just assigning is fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by that
| collisionCache = new CollisionCache(totalSegments); | ||
| } | ||
|
|
||
| Vec3 lastPoint = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment in the code please
| private void updateCollisionCache() { | ||
| if (derailed || graph == null) { | ||
| if (collisionCache != null) | ||
| collisionCache.invalidate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment in the code please




Implements a cache for train collision checks for better performance
Before: https://spark.lucko.me/cgjtsUl25f
After: https://spark.lucko.me/p7ZcDxAUFA
Criticism welcome.
Made with the help of Copilot
World used for testing :
world.zip