forked from upstream/VotingPlugin
303 lines
11 KiB
Java
303 lines
11 KiB
Java
package com.bencodez.votingplugin.backgroundtask;
|
|
|
|
import java.time.Instant;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDateTime;
|
|
import java.time.ZoneId;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeoutException;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.Map.Entry;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import com.bencodez.advancedcore.api.user.UserDataFetchMode;
|
|
import com.bencodez.simpleapi.skull.SkullCache;
|
|
import com.bencodez.votingplugin.VotingPluginMain;
|
|
import com.bencodez.votingplugin.topvoter.TopVoter;
|
|
import com.bencodez.votingplugin.topvoter.TopVoterPlayer;
|
|
import com.bencodez.votingplugin.user.VotingPluginUser;
|
|
import com.bencodez.votingplugin.votesites.VoteSite;
|
|
|
|
/** Owns VotingPlugin's periodic background data refresh task and its state. */
|
|
public final class VotingPluginBackgroundTask {
|
|
|
|
private final VotingPluginMain plugin;
|
|
private final long snapshotWaitMillis;
|
|
private volatile boolean requested;
|
|
private volatile boolean running;
|
|
private volatile long lastRunTimeSeconds = -1;
|
|
|
|
public VotingPluginBackgroundTask(VotingPluginMain plugin) {
|
|
this(plugin, 5_000L);
|
|
}
|
|
|
|
VotingPluginBackgroundTask(VotingPluginMain plugin, long snapshotWaitMillis) {
|
|
this.plugin = plugin;
|
|
this.snapshotWaitMillis = snapshotWaitMillis;
|
|
}
|
|
|
|
public void run() {
|
|
CompletableFuture<Void> completion = new CompletableFuture<>();
|
|
CompletableFuture<Void> snapshotStarted = new CompletableFuture<>();
|
|
CompletableFuture<Void> storageStarted = new CompletableFuture<>();
|
|
AtomicBoolean snapshotPending = new AtomicBoolean(true);
|
|
AtomicBoolean storagePending = new AtomicBoolean(false);
|
|
synchronized (this) {
|
|
if (!(requested || plugin.getConfigFile().isAlwaysUpdate()) || !plugin.isEnabled() || running) return;
|
|
running = true;
|
|
}
|
|
try {
|
|
plugin.captureOnlineTopVoterIgnore(online -> {
|
|
if (!snapshotPending.compareAndSet(true, false)) return;
|
|
snapshotStarted.complete(null);
|
|
if (plugin.getConfigFile().isUpdateWithPlayersOnlineOnly() && online.isEmpty()) {
|
|
storageStarted.complete(null);
|
|
finishRun(completion);
|
|
return;
|
|
}
|
|
synchronized (VotingPluginBackgroundTask.this) { requested = false; }
|
|
storagePending.set(true);
|
|
try {
|
|
plugin.getUserManager().getDataManager().getTimer().execute(() -> {
|
|
if (!storagePending.compareAndSet(true, false)) return;
|
|
storageStarted.complete(null);
|
|
try {
|
|
runRefresh(online);
|
|
completion.complete(null);
|
|
} catch (Throwable failure) {
|
|
plugin.debug(failure);
|
|
setRequested(true);
|
|
completion.complete(null);
|
|
} finally { finishRun(completion); }
|
|
});
|
|
} catch (RuntimeException failure) {
|
|
plugin.debug(failure);
|
|
setRequested(true);
|
|
storagePending.set(false);
|
|
storageStarted.complete(null);
|
|
completion.complete(null);
|
|
finishRun(completion);
|
|
}
|
|
}, () -> {
|
|
setRequested(true);
|
|
snapshotPending.set(false);
|
|
snapshotStarted.complete(null);
|
|
storageStarted.complete(null);
|
|
finishRun(completion);
|
|
});
|
|
} catch (RuntimeException failure) {
|
|
plugin.debug(failure);
|
|
setRequested(true);
|
|
snapshotPending.set(false);
|
|
snapshotStarted.complete(null);
|
|
storageStarted.complete(null);
|
|
completion.complete(null);
|
|
finishRun(completion);
|
|
}
|
|
if (!isPlatformOwnedThread()) {
|
|
try {
|
|
snapshotStarted.get(snapshotWaitMillis, TimeUnit.MILLISECONDS);
|
|
} catch (TimeoutException failure) {
|
|
if (snapshotPending.compareAndSet(true, false)) {
|
|
setRequested(true);
|
|
finishRun(completion);
|
|
return;
|
|
}
|
|
} catch (InterruptedException failure) {
|
|
Thread.currentThread().interrupt();
|
|
if (snapshotPending.compareAndSet(true, false)) {
|
|
setRequested(true);
|
|
finishRun(completion);
|
|
return;
|
|
}
|
|
} catch (java.util.concurrent.ExecutionException impossible) {
|
|
throw new IllegalStateException(impossible);
|
|
}
|
|
if (completion.isDone()) return;
|
|
try {
|
|
storageStarted.get(snapshotWaitMillis, TimeUnit.MILLISECONDS);
|
|
} catch (TimeoutException failure) {
|
|
if (storagePending.compareAndSet(true, false)) {
|
|
setRequested(true);
|
|
finishRun(completion);
|
|
}
|
|
} catch (InterruptedException failure) {
|
|
Thread.currentThread().interrupt();
|
|
if (storagePending.compareAndSet(true, false)) finishRun(completion);
|
|
setRequested(true);
|
|
} catch (java.util.concurrent.ExecutionException impossible) {
|
|
throw new IllegalStateException(impossible);
|
|
}
|
|
}
|
|
}
|
|
|
|
private synchronized void finishRun(CompletableFuture<Void> completion) {
|
|
running = false;
|
|
if (!completion.isDone()) completion.complete(null);
|
|
}
|
|
|
|
private boolean isPlatformOwnedThread() {
|
|
if (Bukkit.getServer() == null) return false;
|
|
try { if (Bukkit.isPrimaryThread()) return true; } catch (RuntimeException ignored) { }
|
|
try {
|
|
java.lang.reflect.Method method = Bukkit.getServer().getClass().getMethod("isGlobalTickThread");
|
|
if (Boolean.TRUE.equals(method.invoke(Bukkit.getServer()))) return true;
|
|
} catch (ReflectiveOperationException | RuntimeException ignored) { }
|
|
try {
|
|
Class<?> tickThread = Class.forName("ca.spottedleaf.moonrise.common.util.TickThread");
|
|
if (Boolean.TRUE.equals(tickThread.getMethod("isTickThread").invoke(null))) return true;
|
|
} catch (ReflectiveOperationException | LinkageError | RuntimeException ignored) { }
|
|
return false;
|
|
}
|
|
|
|
private void runRefresh(Map<UUID, Boolean> onlineUsers) {
|
|
synchronized (plugin) {
|
|
try {
|
|
if (!plugin.isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
plugin.getUserManager().getDataManager().clearCacheBasic();
|
|
SkullCache.flushWeek();
|
|
plugin.debug("Starting background task, current cached users: "
|
|
+ plugin.getUserManager().getDataManager().getUserDataCache().keySet().size());
|
|
|
|
boolean extraBackgroundUpdate = plugin.getConfigFile().isExtraBackgroundUpdate();
|
|
long startTime = System.currentTimeMillis();
|
|
LinkedHashMap<TopVoterPlayer, HashMap<VoteSite, LocalDateTime>> voteToday = new LinkedHashMap<>();
|
|
LinkedHashMap<TopVoter, LinkedHashMap<TopVoterPlayer, Integer>> tempTopVoter = new LinkedHashMap<>();
|
|
|
|
ArrayList<TopVoter> topVotersToCheck = new ArrayList<>();
|
|
for (TopVoter top : TopVoter.values()) {
|
|
if (plugin.getConfigFile().getLoadTopVoter(top)) {
|
|
topVotersToCheck.add(top);
|
|
tempTopVoter.put(top, new LinkedHashMap<>());
|
|
}
|
|
}
|
|
|
|
boolean ignorePermission = plugin.getConfigFile().isTopVoterIgnorePermission();
|
|
ArrayList<String> blackList = plugin.getConfigFile().getBlackList();
|
|
ZoneId zone = ZoneId.systemDefault();
|
|
LocalDate today = LocalDate.now(zone);
|
|
long startOfDayMs = today.atStartOfDay(zone).toInstant().toEpochMilli();
|
|
long startOfNextDayMs = today.plusDays(1).atStartOfDay(zone).toInstant().toEpochMilli();
|
|
long afterSetup = System.currentTimeMillis();
|
|
|
|
plugin.getUserManager().forEachUserKeys((uuid, columns) -> {
|
|
if (!plugin.isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false);
|
|
user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY);
|
|
user.updateTempCacheWithColumns(columns);
|
|
try {
|
|
if (!user.isBanned() && !blackList.contains(user.getPlayerName())) {
|
|
if (!ignorePermission || !user.isTopVoterIgnore()) {
|
|
TopVoterPlayer player = user.getTopVoterPlayer();
|
|
for (TopVoter top : topVotersToCheck) {
|
|
int total = user.getTotal(top);
|
|
if (total > 0) {
|
|
tempTopVoter.get(top).put(player, total);
|
|
}
|
|
}
|
|
}
|
|
|
|
HashMap<VoteSite, LocalDateTime> times = null;
|
|
for (Entry<VoteSite, Long> entry : user.getLastVotes().entrySet()) {
|
|
VoteSite site = entry.getKey();
|
|
if (!site.isEnabled() || site.isHidden()) {
|
|
continue;
|
|
}
|
|
long time = entry.getValue();
|
|
if (time >= startOfDayMs && time < startOfNextDayMs) {
|
|
if (times == null) {
|
|
times = new HashMap<>();
|
|
}
|
|
times.put(site, LocalDateTime.ofInstant(Instant.ofEpochMilli(time), zone));
|
|
}
|
|
}
|
|
if (times != null && !times.isEmpty()) {
|
|
voteToday.put(user.getTopVoterPlayer(), times);
|
|
}
|
|
}
|
|
|
|
Boolean topVoterIgnore = onlineUsers.get(uuid);
|
|
boolean online = topVoterIgnore != null;
|
|
if (extraBackgroundUpdate && online) {
|
|
user.offVoteWithCapturedTopVoterIgnoreAsync(topVoterIgnore.booleanValue())
|
|
.whenComplete((ignored, failure) -> {
|
|
if (failure == null) user.checkOfflineRewards();
|
|
});
|
|
}
|
|
if (!plugin.getPlaceholders().getCacheLevel().onlineOnly() || online) {
|
|
plugin.getPlaceholders().onUpdate(user, false);
|
|
}
|
|
} finally {
|
|
user.clearTempCache();
|
|
}
|
|
}, count -> {
|
|
long elapsed = (System.currentTimeMillis() - afterSetup) / 1000;
|
|
plugin.debug("Finished getting player data in " + elapsed + " seconds, " + count + " users, "
|
|
+ plugin.getStorageType());
|
|
});
|
|
|
|
plugin.getTopVoterHandler().updateTopVoters(tempTopVoter);
|
|
plugin.getPlaceholders().checkNonCachedPlaceholders();
|
|
for (UUID onlineUuid : onlineUsers.keySet()) {
|
|
VotingPluginUser onlineUser = plugin.getVotingPluginUserManager().getVotingPluginUser(onlineUuid, false);
|
|
if (onlineUser != null) plugin.getPlaceholders().onUpdate(onlineUser, true);
|
|
}
|
|
plugin.setVoteToday(voteToday);
|
|
plugin.getServerData().updateValues();
|
|
plugin.getSigns().updateSigns();
|
|
|
|
if (plugin.getConfigFile().isDiscordSRVEnabled() && plugin.getDiscordHandler() != null) {
|
|
for (TopVoter top : TopVoter.values()) {
|
|
if (!plugin.getConfigFile().isDiscordSRVTopVoterNewMessageOnUpdate(top)) {
|
|
plugin.getDiscordHandler().updateTopVoterMessageId(top);
|
|
}
|
|
}
|
|
}
|
|
|
|
plugin.getUserManager().getDataManager().clearNonNeededCachedUsers();
|
|
plugin.extraDebug("Current cached users: "
|
|
+ plugin.getUserManager().getDataManager().getUserDataCache().keySet().size());
|
|
|
|
lastRunTimeSeconds = (System.currentTimeMillis() - startTime) / 1000;
|
|
plugin.debug("Background task finished. Total time: " + lastRunTimeSeconds + " seconds");
|
|
} catch (Exception exception) {
|
|
if (plugin.isEnabled()) {
|
|
plugin.getLogger().info("Looks like something went wrong");
|
|
}
|
|
exception.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public boolean isRequested() {
|
|
return requested;
|
|
}
|
|
|
|
public void setRequested(boolean requested) {
|
|
this.requested = requested;
|
|
}
|
|
|
|
public boolean isRunning() {
|
|
return running;
|
|
}
|
|
|
|
public long getLastRunTimeSeconds() {
|
|
return lastRunTimeSeconds;
|
|
}
|
|
}
|