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); }