Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/cr0s/warpdrive/config/WarpDriveConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ public class WarpDriveConfig {
public static int G_REGISTRY_UPDATE_INTERVAL_TICKS = 20 * WarpDriveConfig.G_REGISTRY_UPDATE_INTERVAL_SECONDS;
public static boolean G_ENFORCE_VALID_CELESTIAL_OBJECTS = true;
public static int G_BLOCKS_PER_TICK = 3500;
public static int G_CHUNKS_PER_TICK = 4;
public static boolean G_ENABLE_FAST_SET_BLOCKSTATE = false;
public static boolean G_ENABLE_PROTECTION_CHECKS = true;
public static boolean G_ENABLE_EXPERIMENTAL_REFRESH = false;
Expand Down Expand Up @@ -861,6 +862,9 @@ public static void loadConfig(final File file) {
G_BLOCKS_PER_TICK = Commons.clamp(100, 100000,
config.get("general", "blocks_per_tick", G_BLOCKS_PER_TICK,
"Number of blocks to move per ticks, too high will cause lag spikes on ship jumping or deployment, too low may break the ship wirings").getInt());
G_CHUNKS_PER_TICK = Commons.clamp(1, 1000,
config.get("general", "chunks_per_tick", G_CHUNKS_PER_TICK,
"Number of target chunks to load or generate per tick before a ship jump, too high will cause lag spikes or watchdog kills when jumping to ungenerated areas, too low will delay the jump").getInt());
G_ENABLE_FAST_SET_BLOCKSTATE = config.get("general", "enable_fast_set_blockstate", G_ENABLE_FAST_SET_BLOCKSTATE,
"Enable fast blockstate placement, skipping light computation. Disable if you have world implementations conflicts").getBoolean(G_ENABLE_FAST_SET_BLOCKSTATE);
G_ENABLE_PROTECTION_CHECKS = config.get("general", "enable_protection_checks", G_ENABLE_PROTECTION_CHECKS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum EnumJumpSequencerState implements IStringSerializable {
CHECK_BORDERS ("check_borders"),
SAVE_TO_DISK ("save_to_disk"),
GET_INITIAL_VECTOR ("get_initial_vector"),
PRELOAD_TARGET_CHUNKS ("preload_target_chunks"),
ADJUST_JUMP_VECTOR ("adjust_jump_vector"),
LOAD_TARGET_CHUNKS ("load_target_chunks"),
SAVE_ENTITIES ("save_entities"),
Expand Down
78 changes: 76 additions & 2 deletions src/main/java/cr0s/warpdrive/event/JumpSequencer.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public class JumpSequencer extends AbstractSequencer {
protected final JumpShip ship;
private boolean betweenWorlds;
private boolean isPluginCheckDone = false;
private boolean isJumpDistanceCheckPending = false;
private ArrayList<ChunkPos> chunksToPreload;
private int indexPreloadChunk = 0;
private WarpDriveText firstAdjustmentReason = null;

private long msCounter = 0;
Expand Down Expand Up @@ -300,6 +303,12 @@ public boolean onUpdate() {
case GET_INITIAL_VECTOR:
state_getInitialVector();
if (isEnabled) {
enumJumpSequencerState = EnumJumpSequencerState.PRELOAD_TARGET_CHUNKS;
}
break;

case PRELOAD_TARGET_CHUNKS:
if (state_preloadTargetChunks() && isEnabled) {
enumJumpSequencerState = EnumJumpSequencerState.ADJUST_JUMP_VECTOR;
}
break;
Expand Down Expand Up @@ -688,7 +697,8 @@ protected void state_getInitialVector() {
final int rangeX = Math.abs(moveX) - (ship.maxX - ship.minX);
final int rangeZ = Math.abs(moveZ) - (ship.maxZ - ship.minZ);
if (Math.max(rangeX, rangeZ) < 256) {
firstAdjustmentReason = getPossibleJumpDistance();
// delayed until the path is loaded, see state_preloadTargetChunks()
isJumpDistanceCheckPending = true;
isPluginCheckDone = true;
}
break;
Expand All @@ -706,6 +716,70 @@ protected void state_getInitialVector() {
LocalProfiler.stop();
}

@Nonnull
private ArrayList<ChunkPos> getChunksToPreload() {
// Cover the target position, plus the source position when a vector adjustment is pending:
// the movement is linear, so intermediate positions are within the union of both boxes.
// A diagonal move loads corner chunks the ship won't touch, and a rotating ship may reach
// a few chunks outside the envelope which will lazy load as before: both are perf only.
final BlockPos targetMin = transformation.apply(ship.minX, ship.minY, ship.minZ);
final BlockPos targetMax = transformation.apply(ship.maxX, ship.maxY, ship.maxZ);
int minX = Math.min(targetMin.getX(), targetMax.getX());
int maxX = Math.max(targetMin.getX(), targetMax.getX());
int minZ = Math.min(targetMin.getZ(), targetMax.getZ());
int maxZ = Math.max(targetMin.getZ(), targetMax.getZ());
if (isJumpDistanceCheckPending && worldTarget == worldSource) {
minX = Math.min(minX, ship.minX);
maxX = Math.max(maxX, ship.maxX);
minZ = Math.min(minZ, ship.minZ);
maxZ = Math.max(maxZ, ship.maxZ);
}
final int chunkMinX = minX >> 4;
final int chunkMaxX = maxX >> 4;
final int chunkMinZ = minZ >> 4;
final int chunkMaxZ = maxZ >> 4;
final ArrayList<ChunkPos> chunkPositions = new ArrayList<>((chunkMaxX - chunkMinX + 1) * (chunkMaxZ - chunkMinZ + 1));
for (int xChunk = chunkMinX; xChunk <= chunkMaxX; xChunk++) {
for (int zChunk = chunkMinZ; zChunk <= chunkMaxZ; zChunk++) {
chunkPositions.add(new ChunkPos(xChunk, zChunk));
}
}
return chunkPositions;
}

protected boolean state_preloadTargetChunks() {
LocalProfiler.start("Jump.preloadTargetChunks");

if (chunksToPreload == null) {
chunksToPreload = getChunksToPreload();
if (WarpDriveConfig.LOGGING_JUMP) {
WarpDrive.logger.info(String.format("%s Preloading %d chunks at target, %d per tick",
this, chunksToPreload.size(), WarpDriveConfig.G_CHUNKS_PER_TICK));
}
}

// getChunk will load or generate the chunk: this is the expensive part, hence the tick budget
final int indexLastChunk = Math.min(chunksToPreload.size(), indexPreloadChunk + WarpDriveConfig.G_CHUNKS_PER_TICK);
for (; indexPreloadChunk < indexLastChunk; indexPreloadChunk++) {
final ChunkPos chunkPos = chunksToPreload.get(indexPreloadChunk);
worldTarget.getChunk(chunkPos.x, chunkPos.z);
}
if (indexPreloadChunk < chunksToPreload.size()) {
LocalProfiler.stop();
return false;
}

// run the delayed vector adjustment now that the whole path is loaded
if (isJumpDistanceCheckPending) {
isJumpDistanceCheckPending = false;
firstAdjustmentReason = getPossibleJumpDistance();
transformation = new Transformation(ship, worldTarget, moveX, moveY, moveZ, rotationSteps);
}

LocalProfiler.stop();
return true;
}

protected void state_adjustJumpVector() {
LocalProfiler.start("Jump.adjustJumpVector");
if (WarpDriveConfig.LOGGING_JUMP) {
Expand Down Expand Up @@ -1490,7 +1564,7 @@ private void doCollisionDamage(final boolean atTarget) {
}

// inform players on board

final double rx = Math.round(min.x + worldSource.rand.nextInt(Math.max(1, (int) (max.x - min.x))));
final double ry = Math.round(min.y + worldSource.rand.nextInt(Math.max(1, (int) (max.y - min.y))));
final double rz = Math.round(min.z + worldSource.rand.nextInt(Math.max(1, (int) (max.z - min.z))));
Expand Down