diff --git a/build.gradle.kts b/build.gradle.kts index 0c91567..a25aa4a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } group = "com.bitaspire" -version = "1.2.7" +version = "1.2.8" repositories { mavenLocal() diff --git a/src/main/java/com/bitaspire/cyberlevels/DatabaseFactory.java b/src/main/java/com/bitaspire/cyberlevels/DatabaseFactory.java index 40d29b1..a6b04cf 100644 --- a/src/main/java/com/bitaspire/cyberlevels/DatabaseFactory.java +++ b/src/main/java/com/bitaspire/cyberlevels/DatabaseFactory.java @@ -144,6 +144,27 @@ public boolean isConnected() { return dataSource != null && !dataSource.isClosed(); } + private boolean isDatabaseSyncStopping() { + return main.userManager instanceof UserManagerImpl && + ((UserManagerImpl) main.userManager).isDatabaseSyncStopping(); + } + + private boolean isConnectionFailure(Throwable throwable) { + for (Throwable current = throwable; current != null; current = current.getCause()) { + if (current instanceof SQLException) { + String state = ((SQLException) current).getSQLState(); + if (state != null && state.startsWith("08")) return true; + } + + if (current instanceof java.net.SocketException) return true; + + String name = current.getClass().getName(); + if (name.contains("CommunicationsException")) return true; + } + + return false; + } + @Override public void connect() { if (isConnected()) return; @@ -439,6 +460,8 @@ List getUsersUpdatedSince(long updatedAfter) { return users; } } catch (Exception e) { + if (isDatabaseSyncStopping() && isConnectionFailure(e)) return Collections.emptyList(); + main.logger("&cFailed to fetch recent player updates from " + type + ".", ""); e.printStackTrace(); return Collections.emptyList(); diff --git a/src/main/java/com/bitaspire/cyberlevels/UserManagerImpl.java b/src/main/java/com/bitaspire/cyberlevels/UserManagerImpl.java index 985b974..e30ae5d 100644 --- a/src/main/java/com/bitaspire/cyberlevels/UserManagerImpl.java +++ b/src/main/java/com/bitaspire/cyberlevels/UserManagerImpl.java @@ -42,6 +42,7 @@ final class UserManagerImpl implements UserManager { private final Map localOfflineSnapshots = new ConcurrentHashMap<>(); private final Map knownDatabaseUpdatedAt = new ConcurrentHashMap<>(); private volatile long lastObservedDatabaseUpdateAt = System.currentTimeMillis(); + private volatile boolean databaseSyncStopping = false; GlobalTask autoSaveTask = null; GlobalTask databaseSyncTask = null; @@ -549,26 +550,34 @@ private DatabaseFactory.DatabaseImpl databaseImpl() { void startDatabaseSync() { if (databaseSyncTask != null || databaseImpl() == null) return; + databaseSyncStopping = false; scheduleDatabaseSync(); } private void scheduleDatabaseSync() { + if (databaseSyncStopping) return; + databaseSyncTask = main.scheduler().runTaskLaterAsynchronously(() -> { try { - pollDatabaseUpdates(); + if (!databaseSyncStopping) + pollDatabaseUpdates(); } finally { - if (main.isEnabled() && databaseSyncTask != null) + if (main.isEnabled() && databaseSyncTask != null && !databaseSyncStopping) scheduleDatabaseSync(); } }, DATABASE_SYNC_INTERVAL_TICKS); } private void pollDatabaseUpdates() { + if (databaseSyncStopping) return; + DatabaseFactory.DatabaseImpl databaseImpl = databaseImpl(); if (databaseImpl == null || users.isEmpty()) return; if (!databaseSyncInFlight.compareAndSet(false, true)) return; try { + if (databaseSyncStopping) return; + long queryAfter = Math.max(0L, lastObservedDatabaseUpdateAt - DATABASE_SYNC_LOOKBACK_MS); List updates = databaseImpl.getUsersUpdatedSince(queryAfter); @@ -638,6 +647,33 @@ public void startAutoSave() { }, 20L * config.getAutoSaveInterval()); } + boolean isDatabaseSyncStopping() { + return databaseSyncStopping; + } + + private void cancelDatabaseSync() { + databaseSyncStopping = true; + + if (databaseSyncTask != null) { + databaseSyncTask.cancel(); + databaseSyncTask = null; + } + + waitForDatabaseSync(); + } + + private void waitForDatabaseSync() { + long deadline = System.currentTimeMillis() + 5_000L; + while (databaseSyncInFlight.get() && System.currentTimeMillis() < deadline) { + try { + Thread.sleep(25L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + } + @Override public void cancelAutoSave() { if (autoSaveTask != null) { @@ -645,9 +681,6 @@ public void cancelAutoSave() { autoSaveTask = null; } - if (databaseSyncTask != null) { - databaseSyncTask.cancel(); - databaseSyncTask = null; - } + cancelDatabaseSync(); } }