in singleplayer you can now rejoin on active computers

This commit is contained in:
Robert MacRae
2025-09-03 12:31:13 -03:00
parent 2b4b1754a0
commit 3924d0fa4c
2 changed files with 80 additions and 23 deletions

View File

@@ -43,13 +43,32 @@ public class HardDriveDevice extends AbstractBlockStorageDevice<ByteBufferBlockD
protected CompletableFuture<ByteBufferBlockDevice> createBlockDevice() {
blobHandle = BlobStorage.validateHandle(blobHandle);
if (AsyncConfig.SERVER.asyncStorageOperations.get()) {
boolean useAsync = false;
try {
if (AsyncConfig.SERVER != null) {
useAsync = AsyncConfig.SERVER.asyncStorageOperations.get();
}
} catch (IllegalStateException e) {
LOGGER.trace("Config not loaded yet, using default async storage operations setting");
}
if (useAsync) {
return BlobStorage.getOrOpenAsync(blobHandle)
.thenApplyAsync(channel -> {
try {
if (AsyncConfig.SERVER.enableSuperDebug.get()) {
boolean debug = false;
try {
if (AsyncConfig.SERVER != null) {
debug = AsyncConfig.SERVER.enableSuperDebug.get();
}
} catch (IllegalStateException e) {
// Config not loaded yet, use default debug setting
}
if (debug) {
LOGGER.debug("Mapping buffer for blob: {}", blobHandle);
}
final MappedByteBuffer buffer = channel.map(MapMode.READ_WRITE, 0, size);
return ByteBufferBlockDevice.wrap(buffer, readonly);
} catch (final IOException e) {

View File

@@ -75,23 +75,47 @@ public final class AsyncUtils {
* @return a CompletableFuture that will complete when the task finishes
*/
public static <T> CompletableFuture<T> runAsync(Supplier<T> task, String description) {
if (AsyncConfig.SERVER.enableSuperDebug.get()) {
LOGGER.info("Starting async task: {}", description);
logStackTrace("Async task stack trace");
if (ASYNC_EXECUTOR.isShutdown()) {
LOGGER.warn("Attempted to submit async task '{}' after executor was shut down", description);
CompletableFuture<T> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(new RejectedExecutionException("Executor has been shut down"));
return failedFuture;
}
return CompletableFuture.supplyAsync(() -> {
try {
return task.get();
} catch (Throwable t) {
LOGGER.error("Error in async task: " + description, t);
throw t;
} finally {
if (AsyncConfig.SERVER.enableSuperDebug.get()) {
LOGGER.info("Completed async task: {}", description);
}
try {
if (AsyncConfig.SERVER != null && AsyncConfig.SERVER.enableSuperDebug.get()) {
LOGGER.info("Starting async task: {}", description);
logStackTrace("Async task stack trace");
}
}, ASYNC_EXECUTOR);
} catch (IllegalStateException e) {
// Config not loaded yet, skip debug logging
LOGGER.trace("Config not loaded yet, skipping debug logging for: {}", description);
}
try {
return CompletableFuture.supplyAsync(() -> {
try {
return task.get();
} catch (Throwable t) {
LOGGER.error("Error in async task: " + description, t);
throw t;
} finally {
try {
if (AsyncConfig.SERVER != null && AsyncConfig.SERVER.enableSuperDebug.get()) {
LOGGER.info("Completed async task: {}", description);
}
} catch (IllegalStateException e) {
// Config not loaded yet, skip debug logging
LOGGER.trace("Config not loaded yet, skipping debug logging for: {}", description);
}
}
}, ASYNC_EXECUTOR);
} catch (RejectedExecutionException e) {
LOGGER.warn("Failed to submit async task '{}' - executor is shutting down", description, e);
CompletableFuture<T> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(e);
return failedFuture;
}
}
/**
@@ -107,6 +131,15 @@ public final class AsyncUtils {
return null;
}, description);
}
/**
* Checks if the async executor has been shut down.
*
* @return true if the executor has been shut down, false otherwise
*/
public static boolean isShutdown() {
return ASYNC_EXECUTOR.isShutdown();
}
/**
* Logs the current stack trace if super debug mode is enabled.
@@ -114,14 +147,19 @@ public final class AsyncUtils {
* @param message the message to log with the stack trace
*/
public static void logStackTrace(String message) {
if (AsyncConfig.SERVER.enableSuperDebug.get()) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StringBuilder sb = new StringBuilder(message).append("\n");
// Skip the first 2 elements (getStackTrace and this method)
for (int i = 2; i < Math.min(stackTrace.length, 10); i++) {
sb.append(" at ").append(stackTrace[i]).append("\n");
try {
if (AsyncConfig.SERVER != null && AsyncConfig.SERVER.enableSuperDebug.get()) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StringBuilder sb = new StringBuilder(message).append("\n");
// Skip the first 2 elements (getStackTrace and this method)
for (int i = 2; i < stackTrace.length; i++) {
sb.append("\tat ").append(stackTrace[i]).append("\n");
}
LOGGER.info(sb.toString());
}
LOGGER.debug(sb.toString());
} catch (IllegalStateException e) {
// Config not loaded yet, skip debug logging
LOGGER.trace("Config not loaded yet, skipping stack trace logging");
}
}