08-18-2014, 02:42 AM
Hi everyone
So I'd like to work on the minecart system. There are several bugs, like derailing, that can only be fixed with an overhaul of their movement system. I'd like to explain why and how I'd go about it. As I am not completly familiar with the codebase I might make mistakes, so please correct me anywhere if I'm wrong. And sorry for the wall of text - I might have overdone it a bit ^^
Where in the code is the movement system implemented?
Well, obviously in Entities/Minecart.cpp. The important functions are
[1] void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
and
[2a] void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
[2b] void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
[2c] void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
[2d] void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
and
[3] void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
and
[4a] bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
[4b] bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
These alone define how the movement system of minecarts works. I'll give a brief explanation of all the functions and what they do.
[1] basically wraps all the other functions. It checks if the minecart even is on a rail and if so it will call the according [2] function. Is called every tick (?)
[2a] checks on what rail direction the cart is and what its movement direction is. It accordingly decelerates the car because of friction. [2b] is more or less the same as [2a], only that it accelerates the cart (if the rail is powered ofc) rather then deccelerate it. [2c] activates the detetor rail and then calls [2a]. [2d] is not implemented yet and only calls [2a].
[3] rotates the minecart accordingly to the rail and setsthe speed opposing the rail to 0. Is called each time in [1]
[4a] and [4b] test possible collision and react followingly. [4b] is for example responsible for pushing minecarts.
How are things implemented now?
[2a]/[2b] right now adjust the speed according to the underlying block. For example: the minecart is on a powered rail with orientation -X +X. It will increase/decrease (depending of direction) SpeedX.
[3] not much to say - it just checks the railtype and then sets stuff accordingly.
[4a] checks for collision with blocks, sets the carts speed to 0
[4b] checks on what rail the cart is and in which direction the entity pushes the cart. It then moves it along this direction. Right now it uses some strange equations, see discussion on https://github.com/mc-server/MCServer/pull/1324
The problems
Yeah, the actual reason why we're actually talking about this. So here are some major and minor problems with this system:
[A] DERAILING:
This is the most important problem. When a minecart goes fast it can randomly derail in curves. Actually it can already derail from the speed of 1m/s on. The thing is there is a chance that between two calls of HandlePhysics the minecart "misses" the curve, because its speed was too great. At the next update it's not on a rail anymore, but on some other block. Please excuse my photo editing skills:
[B] PUSHING:
So right now as, discussed on github (https://github.com/mc-server/MCServer/pull/1324), the implementation of [4b] is a bit wanky. It isn't really documented and I'd suggest to reimplement it.
[C] ORIENTATION:
I know this is very nit-picky, but this is a bug that is actually even on vanilla! The orientation of minecarts only depends on the rail type - therefore it can change in curves. This fortunately isn't noticeable with players in it. This has no priority, but it would be of course nice-to-have ;-)
Image (too wide for forum)
Proposed solutions
So let's get to the interesting part. This is how I'd go about solving the above problems:
[A]:
First of all the concept: In [2] we should analyze the surrounding tracks and see where the minecart will be at the next tick. Once we know that we move the minecart into the calculated direction rather than just in the direction of the rail.
[3] (called before [2] in [1]) would then adjust the direction of the speed to the rail (with the same absolute value of the vector)
As of now [2b] is more or less a copy of [2a]. I'd change it like that: First it adds the speed and then calls [2a].
Furthermore the constants of acceleration/decceleration are set everywhere separately in the code. I'd suggest defining them at the beginning of the file. We could even make them changable through the plugin API at some point. It is not documented in the code, so I have to ask: are these constants in the current code extracted from the game? I'm not an expert on this, but it would be a nice addition.
[B]:
I'd suggest following solution: The entity creates a force in the opposing direction. The closer the entity, the greater the force. The force that is actually accounted to the minecart is only the projection on the rail axis:
This gives the advantage that the cart cannot be pushed from the side.
If however somebody knows how this is actually handeled in vanilla (I guess one would have to decompile the code to know) this would be even better.
[C]:
This would be quite easy with the solution of [A]. Knowing the direction before a curved rail allows us to calculate the direction after the curved rail.
So... what now?
Before implementing I'd like to put forward the exact equations I'm going to be using. And before doing that I'd like to hear some of your feedback on my approach
Thanks for reading through all of this!
So I'd like to work on the minecart system. There are several bugs, like derailing, that can only be fixed with an overhaul of their movement system. I'd like to explain why and how I'd go about it. As I am not completly familiar with the codebase I might make mistakes, so please correct me anywhere if I'm wrong. And sorry for the wall of text - I might have overdone it a bit ^^
Where in the code is the movement system implemented?
Well, obviously in Entities/Minecart.cpp. The important functions are
[1] void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
and
[2a] void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
[2b] void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
[2c] void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
[2d] void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
and
[3] void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
and
[4a] bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
[4b] bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
These alone define how the movement system of minecarts works. I'll give a brief explanation of all the functions and what they do.
[1] basically wraps all the other functions. It checks if the minecart even is on a rail and if so it will call the according [2] function. Is called every tick (?)
[2a] checks on what rail direction the cart is and what its movement direction is. It accordingly decelerates the car because of friction. [2b] is more or less the same as [2a], only that it accelerates the cart (if the rail is powered ofc) rather then deccelerate it. [2c] activates the detetor rail and then calls [2a]. [2d] is not implemented yet and only calls [2a].
[3] rotates the minecart accordingly to the rail and setsthe speed opposing the rail to 0. Is called each time in [1]
[4a] and [4b] test possible collision and react followingly. [4b] is for example responsible for pushing minecarts.
How are things implemented now?
[2a]/[2b] right now adjust the speed according to the underlying block. For example: the minecart is on a powered rail with orientation -X +X. It will increase/decrease (depending of direction) SpeedX.
[3] not much to say - it just checks the railtype and then sets stuff accordingly.
[4a] checks for collision with blocks, sets the carts speed to 0
[4b] checks on what rail the cart is and in which direction the entity pushes the cart. It then moves it along this direction. Right now it uses some strange equations, see discussion on https://github.com/mc-server/MCServer/pull/1324
The problems
Yeah, the actual reason why we're actually talking about this. So here are some major and minor problems with this system:
[A] DERAILING:
This is the most important problem. When a minecart goes fast it can randomly derail in curves. Actually it can already derail from the speed of 1m/s on. The thing is there is a chance that between two calls of HandlePhysics the minecart "misses" the curve, because its speed was too great. At the next update it's not on a rail anymore, but on some other block. Please excuse my photo editing skills:
[B] PUSHING:
So right now as, discussed on github (https://github.com/mc-server/MCServer/pull/1324), the implementation of [4b] is a bit wanky. It isn't really documented and I'd suggest to reimplement it.
[C] ORIENTATION:
I know this is very nit-picky, but this is a bug that is actually even on vanilla! The orientation of minecarts only depends on the rail type - therefore it can change in curves. This fortunately isn't noticeable with players in it. This has no priority, but it would be of course nice-to-have ;-)
Image (too wide for forum)
Proposed solutions
So let's get to the interesting part. This is how I'd go about solving the above problems:
[A]:
First of all the concept: In [2] we should analyze the surrounding tracks and see where the minecart will be at the next tick. Once we know that we move the minecart into the calculated direction rather than just in the direction of the rail.
[3] (called before [2] in [1]) would then adjust the direction of the speed to the rail (with the same absolute value of the vector)
As of now [2b] is more or less a copy of [2a]. I'd change it like that: First it adds the speed and then calls [2a].
Furthermore the constants of acceleration/decceleration are set everywhere separately in the code. I'd suggest defining them at the beginning of the file. We could even make them changable through the plugin API at some point. It is not documented in the code, so I have to ask: are these constants in the current code extracted from the game? I'm not an expert on this, but it would be a nice addition.
[B]:
I'd suggest following solution: The entity creates a force in the opposing direction. The closer the entity, the greater the force. The force that is actually accounted to the minecart is only the projection on the rail axis:
This gives the advantage that the cart cannot be pushed from the side.
If however somebody knows how this is actually handeled in vanilla (I guess one would have to decompile the code to know) this would be even better.
[C]:
This would be quite easy with the solution of [A]. Knowing the direction before a curved rail allows us to calculate the direction after the curved rail.
So... what now?
Before implementing I'd like to put forward the exact equations I'm going to be using. And before doing that I'd like to hear some of your feedback on my approach
Thanks for reading through all of this!