From cec9422a13a00a42241d0e3914b9c8870a0485b1 Mon Sep 17 00:00:00 2001 From: Brettflan Date: Sun, 21 Apr 2013 20:26:19 -0500 Subject: [PATCH] AutoLeaveTask is now throttled to only spend a certain amount of time per server tick on removing inactive players, to prevent it from potentially choking very large servers. This amount of time can be specified by the new setting "autoLeaveRoutineMaxMillisecondsPerTick" (default 5ms, ~10% of a tick). Also some very minor improvements to the config command. --- src/com/massivecraft/factions/Conf.java | 1 + .../massivecraft/factions/cmd/CmdConfig.java | 20 +++- .../factions/util/AutoLeaveProcessTask.java | 94 +++++++++++++++++++ .../factions/util/AutoLeaveTask.java | 12 ++- 4 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 src/com/massivecraft/factions/util/AutoLeaveProcessTask.java diff --git a/src/com/massivecraft/factions/Conf.java b/src/com/massivecraft/factions/Conf.java index b6f83269..734fff68 100644 --- a/src/com/massivecraft/factions/Conf.java +++ b/src/com/massivecraft/factions/Conf.java @@ -75,6 +75,7 @@ public class Conf public static double autoLeaveAfterDaysOfInactivity = 10.0; public static double autoLeaveRoutineRunsEveryXMinutes = 5.0; + public static int autoLeaveRoutineMaxMillisecondsPerTick = 5; // 1 server tick is roughly 50ms, so default max 10% of a tick public static boolean removePlayerDataWhenBanned = true; public static boolean worldGuardChecking = false; diff --git a/src/com/massivecraft/factions/cmd/CmdConfig.java b/src/com/massivecraft/factions/cmd/CmdConfig.java index 7040105b..f3b0a032 100644 --- a/src/com/massivecraft/factions/cmd/CmdConfig.java +++ b/src/com/massivecraft/factions/cmd/CmdConfig.java @@ -108,6 +108,22 @@ public class CmdConfig extends FCommand } } + // long + else if (target.getType() == long.class) + { + try + { + long longVal = Long.parseLong(value); + target.setLong(null, longVal); + success = "\""+fieldName+"\" option set to "+longVal+"."; + } + catch(NumberFormatException ex) + { + sendMessage("Cannot set \""+fieldName+"\": long integer (whole number) value required."); + return; + } + } + // double else if (target.getType() == double.class) { @@ -268,11 +284,13 @@ public class CmdConfig extends FCommand if (!success.isEmpty()) { - sendMessage(success); if (sender instanceof Player) { + sendMessage(success); P.p.log(success + " Command was run by "+fme.getName()+"."); } + else // using P.p.log() instead of sendMessage if run from server console so that "[Factions v#.#.#]" is prepended in server log + P.p.log(success); } // save change to disk Conf.save(); diff --git a/src/com/massivecraft/factions/util/AutoLeaveProcessTask.java b/src/com/massivecraft/factions/util/AutoLeaveProcessTask.java new file mode 100644 index 00000000..1d85635a --- /dev/null +++ b/src/com/massivecraft/factions/util/AutoLeaveProcessTask.java @@ -0,0 +1,94 @@ +package com.massivecraft.factions.util; + +import java.util.ArrayList; +import java.util.ListIterator; + +import org.bukkit.scheduler.BukkitRunnable; + +import com.massivecraft.factions.Conf; +import com.massivecraft.factions.Faction; +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Role; + +public class AutoLeaveProcessTask extends BukkitRunnable +{ + private transient boolean readyToGo = false; + private transient boolean finished = false; + private transient ArrayList fplayers; + private transient ListIterator iterator; + private transient double toleranceMillis; + + public AutoLeaveProcessTask() + { + fplayers = new ArrayList(FPlayers.i.get()); + this.iterator = fplayers.listIterator(); + this.toleranceMillis = Conf.autoLeaveAfterDaysOfInactivity * 24 * 60 * 60 * 1000; + this.readyToGo = true; + this.finished = false; + } + + public void run() + { + if (Conf.autoLeaveAfterDaysOfInactivity <= 0.0 || Conf.autoLeaveRoutineMaxMillisecondsPerTick <= 0.0) + { + this.stop(); + return; + } + + if ( ! readyToGo) return; + // this is set so it only does one iteration at a time, no matter how frequently the timer fires + readyToGo = false; + // and this is tracked to keep one iteration from dragging on too long and possibly choking the system if there are a very large number of players to go through + long loopStartTime = System.currentTimeMillis(); + + while(iterator.hasNext()) + { + long now = System.currentTimeMillis(); + + // if this iteration has been running for maximum time, stop to take a breather until next tick + if (now > loopStartTime + Conf.autoLeaveRoutineMaxMillisecondsPerTick) + { + readyToGo = true; + return; + } + + FPlayer fplayer = iterator.next(); + if (fplayer.isOffline() && now - fplayer.getLastLoginTime() > toleranceMillis) + { + if (Conf.logFactionLeave || Conf.logFactionKick) + P.p.log("Player "+fplayer.getName()+" was auto-removed due to inactivity."); + + // if player is faction admin, sort out the faction since he's going away + if (fplayer.getRole() == Role.ADMIN) + { + Faction faction = fplayer.getFaction(); + if (faction != null) + fplayer.getFaction().promoteNewLeader(); + } + + fplayer.leave(false); + iterator.remove(); // go ahead and remove this list's link to the FPlayer object + fplayer.detach(); + } + } + + // looks like we've finished + this.stop(); + } + + // we're done, shut down + public void stop() + { + readyToGo = false; + finished = true; + + this.cancel(); + } + + public boolean isFinished() + { + return finished; + } +} diff --git a/src/com/massivecraft/factions/util/AutoLeaveTask.java b/src/com/massivecraft/factions/util/AutoLeaveTask.java index bccb33ff..e42883cb 100644 --- a/src/com/massivecraft/factions/util/AutoLeaveTask.java +++ b/src/com/massivecraft/factions/util/AutoLeaveTask.java @@ -1,11 +1,11 @@ package com.massivecraft.factions.util; import com.massivecraft.factions.Conf; -import com.massivecraft.factions.FPlayers; import com.massivecraft.factions.P; public class AutoLeaveTask implements Runnable { + private static AutoLeaveProcessTask task; double rate; public AutoLeaveTask() @@ -13,11 +13,15 @@ public class AutoLeaveTask implements Runnable this.rate = Conf.autoLeaveRoutineRunsEveryXMinutes; } - public void run() + public synchronized void run() { - FPlayers.i.autoLeaveOnInactivityRoutine(); + if (task != null && ! task.isFinished()) + return; - // maybe setting has been changed? if so, restart task at new rate + task = new AutoLeaveProcessTask(); + task.runTaskTimer(P.p, 1, 1); + + // maybe setting has been changed? if so, restart this task at new rate if (this.rate != Conf.autoLeaveRoutineRunsEveryXMinutes) P.p.startAutoLeaveTask(true); }