Ok, first, a brief explanation of why this change was made to give some context to the madness:
Please read Item 1 ( Race Condition (Client vs. Server Thread) ) here: https://github.com/taurose/Unglitch
This is a real bitch and a half to resolve in a manner that separates collision data for both client and server without essentially having to modify every single vanilla block class in the game to adapt to the change.
In the end, I decided to leave the exist minX, maxX, etc. vanilla variables in the Block class intact so that vanilla references to those old vars would still compile and execute fine, without doing anything. This is why all your add-on blocks probably look like big 1m^3 cubes right now. On the bright side, this provides a clear indication that any given block with broken collisions needs to be refactored to fix them.
Secondly, I also wanted to prevent any future mistakes of the same kind that resulted in this whole mess from occurring so to that end, I decided on a system whereby instead of setting block bounds in internal variables as was done before, that the related functions would instead return temporary AxisAlignedBB objects, and that only the initial block bounds would be stored as a private variable that could never be modified post initialization. The following related code I've added to the Block class should be rather self-explanatory. I normally don't publish my code but I figured having the related code isolated, and to have access to my comments might be more useful than having to read through a deobfuscated mess:
Code: Select all
//-------- Collision Handling Functionality -------//
/**
* This should never be modified after a block is initialized to avoid multithreading issues
* that occurred with the old min/max bounds variables.
*/
private AxisAlignedBB m_fixedBlockBounds = new AxisAlignedBB( 0D, 0D, 0D, 1D, 1D, 1D );
private boolean m_bFixedBlockBoundsSet = false;
/**
* Should only ever be called once for a block. Repeated calls will silently fail without
* changing the bounds.
*/
protected void InitBlockBounds( double dMinX, double dMinY, double dMinZ,
double dMaxX, double dMaxY, double dMaxZ )
{
if ( !m_bFixedBlockBoundsSet )
{
// only allow the bounds to be set before they're ever accessed to
// discourage the kind of errors that necessitated it in the first
// place: client and server threads modifying the min/max values
// resulting in race conditions.
m_fixedBlockBounds.setBounds( dMinX, dMinY, dMinZ, dMaxX, dMaxY, dMaxZ );
}
}
protected void InitBlockBounds( AxisAlignedBB bounds )
{
if ( !m_bFixedBlockBoundsSet )
{
m_fixedBlockBounds.setBB( bounds );
}
}
protected AxisAlignedBB GetFixedBlockBoundsFromPool()
{
m_bFixedBlockBoundsSet = true;
return m_fixedBlockBounds.MakeTemporaryCopy();
}
public AxisAlignedBB getCollisionBoundingBoxFromPool( World world, int i, int j, int k )
{
return GetBlockBoundsFromPoolBasedOnState( world, i, j, k ).offset( i, j, k );
}
public AxisAlignedBB GetBlockBoundsFromPoolBasedOnState(
IBlockAccess blockAccess, int i, int j, int k )
{
return GetFixedBlockBoundsFromPool();
}
public MovingObjectPosition collisionRayTrace( World world, int i, int j, int k,
Vec3 startRay, Vec3 endRay )
{
return CollisionRayTraceVsBlockBounds( world, i, j, k, startRay, endRay );
}
public MovingObjectPosition CollisionRayTraceVsBlockBounds( World world, int i, int j, int k,
Vec3 startRay, Vec3 endRay )
{
AxisAlignedBB collisionBox = GetBlockBoundsFromPoolBasedOnState(
world, i, j, k ).offset( i, j, k );
MovingObjectPosition collisionPoint = collisionBox.calculateIntercept( startRay, endRay );
if ( collisionPoint != null )
{
collisionPoint.blockX = i;
collisionPoint.blockY = j;
collisionPoint.blockZ = k;
}
return collisionPoint;
}
public AxisAlignedBB getSelectedBoundingBoxFromPool( World world, int i, int j, int k )
{
return GetBlockBoundsFromPoolBasedOnState( world, i, j, k ).offset( i, j, k );
}
public AxisAlignedBB GetBlockBoundsFromPoolForItemRender( int iItemDamage )
{
return GetFixedBlockBoundsFromPool();
}
These are the steps I generally followed:
-Use InitBlockBounds() in your constructor to set what will be constant block bounds. This should replace any setBounds() calls that you previously had.
-If your block had a setBoundsBasedOnState() method, replace it instead with GetBlockBoundsFromPoolBasedOnState(). In many cases this is a very straightforward replacement of any setBounds() calls within that function with something akin to
Code: Select all
return AxisAlignedBB.getAABBPool().getAABB(
0D, 0D, 0.375D, 1D, 1D, 0.625D );
-In a lot of cases you can now completely delete any additional getCollisionBoundingBoxFromPool(), collisionRayTrace(), and getSelectedBoundingBoxFromPools() methods your block previously contained, as those methods now rely on common GetBlockBoundsFromPoolBasedOnState() functionality by default. In many cases, block code ends up being a lot simpler with a lot less code duplication as a result of these changes. Given it now also all relies on AxisAlignedBB objects instead of indivdual min/max doubles, you can leverage a lot of the functionality of that class to make things even simpler.
-Remove and replace any additional calls to setBlockBounds() that you may have within your block, as these won't do squat anymore.
-setBlockBoundsForItemRender() has been entirely deprecated and should be replaced with implementations of GetBlockBoundsFromPoolForItemRender().
-setBlockBoundsForItemRender() has been entirely deprecated and should be replaced with implementations of GetBlockBoundsFromPoolForItemRender().
-RenderBlocks.setRenderBoundsFromBlock() has also been deprecated, since it refers to the old vanilla block bounds, and calls to it should largely be replaced by stuff like:
Code: Select all
rendererBlocks.setRenderBounds( GetBlockBoundsFromPoolBasedOnState( rendererBlocks.blockAccess, i, j, k ) );