Create Train Crash Fix

Mod

Fixes the issue where trains by the Create mod might crash the server due to infinite positions.

Server Technology

87 downloads
0 followers
Follow Save
90% of ad revenue goes to creators. Go ad-free while supporting creators with Modrinth Plus. Subscribe today!

Create Train Crash Fix

Fixes the crash that may occur when a train has infinite positions (even if the train data file doesn't show an infinite)

a very simple mod

Info for nerds (how it was fixed) First we've got to take a look at the issue: the train does not have an invalid position in the train data file. That means it has to be somewhere in the serialisation.

Creation of issue #6795
August 7th, 2024

I added two mixin injections. One at createEntity (from create) and readNbt (from minecraft).

@Mixin(Carriage.DimensionalCarriageEntity.class)
public abstract class DimensionalCarriageMixin {
  @Shadow public Vec3d positionAnchor;

  @Inject(at = @At("HEAD"), method = "createEntity")
  private void sendEntityInfo(World level, boolean loadPassengers, CallbackInfo ci) {
    CreateTrainFix.LOGGER.info(positionAnchor.toString());

    if (!Double.isFinite(positionAnchor.getX()) || !Double.isFinite(positionAnchor.getY()) || !Double.isFinite(positionAnchor.getZ())) {
        CreateTrainFix.LOGGER.info("Train failed to be created, because of infinity checks.");
    }
  }
}

@Mixin(Entity.class)
public abstract class EntityMixin {
  @Shadow public abstract double getX();

  @Shadow public abstract double getY();

  @Shadow public abstract double getZ();

  @Shadow public abstract Vec3d getPos();

  @Inject(method = "readNbt", at = @At(value = "INVOKE", target = "Ljava/lang/Double;isFinite(D)Z"))
  private void checkFiniteDebug(NbtCompound nbt, CallbackInfo ci) {
    if (!Double.isFinite(getX()) || !Double.isFinite(getY()) || !Double.isFinite(getZ())) {
      CreateTrainFix.LOGGER.info("INFINITE location " + getPos());
    }
  }
}

This is what's being logged, after which it inevitably crashes. The coordinates are somehow invalid, while still being normal in the createEntity method.

[18:18:15] [Server thread/INFO]: (225.5, 58.0, -165.8600004762411)
[18:18:15] [Server thread/INFO]: INFINITE location (NaN, NaN, NaN)

After more thorough checking the NBT already comes as NaN, while the create_tracks.dat file doesn't contain anything like that. create_tracks.dat check see line 123 (heh, funny number)


~2.5 months later IThundxr proposes the idea of adding something similar to the following into the code:

serialisedEntity.remove("Pos");
serialisedEntity.put("Pos", newDoubleList(positionAnchor.x(), positionAnchor.y(), positionAnchor.z()));

This is the final concept and it works!


This is the final version of what has been added

@Inject(at = @At("HEAD"), method = "createEntity")
private void createTrainFix$fixEntity(World level, boolean loadPassengers, CallbackInfo ci) {
  try {
    // RefUtil is a class with a few methods to access private fields from the superclass.
    NbtCompound serialisedEntity = (NbtCompound) RefUtil.getPrivateFieldValue(this$0, "serialisedEntity");
    serialisedEntity.remove("Pos");
    serialisedEntity.put("Pos", newDoubleList(positionAnchor.x, positionAnchor.y, positionAnchor.z));
    // Set the value again
    RefUtil.setFieldValue(this$0, "serialisedEntity", serialisedEntity);
  } catch (NoSuchFieldException | IllegalAccessException e) {
    // If this all didn't work, throw an error
    CreateTrainFix.LOGGER.error("(CreateTrainFix) Failed to fix train position");
    throw new RuntimeException(e);
  }

  // Final check
  if (!Double.isFinite(positionAnchor.getX()) || !Double.isFinite(positionAnchor.getY()) || !Double.isFinite(positionAnchor.getZ())) {
    CreateTrainFix.LOGGER.info("Train failed to be created, because of infinity checks.");
  }
}

Project members

JXSnack

Member

Details

Licensed AGPL-3.0-only
Published 17 days ago
Updated 20 days ago