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.
This commit is contained in:
Brettflan 2013-04-21 20:26:19 -05:00
parent 63837691ce
commit cec9422a13
4 changed files with 122 additions and 5 deletions

View File

@ -75,6 +75,7 @@ public class Conf
public static double autoLeaveAfterDaysOfInactivity = 10.0; public static double autoLeaveAfterDaysOfInactivity = 10.0;
public static double autoLeaveRoutineRunsEveryXMinutes = 5.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 removePlayerDataWhenBanned = true;
public static boolean worldGuardChecking = false; public static boolean worldGuardChecking = false;

View File

@ -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 // double
else if (target.getType() == double.class) else if (target.getType() == double.class)
{ {
@ -268,11 +284,13 @@ public class CmdConfig extends FCommand
if (!success.isEmpty()) if (!success.isEmpty())
{ {
sendMessage(success);
if (sender instanceof Player) if (sender instanceof Player)
{ {
sendMessage(success);
P.p.log(success + " Command was run by "+fme.getName()+"."); 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 // save change to disk
Conf.save(); Conf.save();

View File

@ -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<FPlayer> fplayers;
private transient ListIterator<FPlayer> iterator;
private transient double toleranceMillis;
public AutoLeaveProcessTask()
{
fplayers = new ArrayList<FPlayer>(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;
}
}

View File

@ -1,11 +1,11 @@
package com.massivecraft.factions.util; package com.massivecraft.factions.util;
import com.massivecraft.factions.Conf; import com.massivecraft.factions.Conf;
import com.massivecraft.factions.FPlayers;
import com.massivecraft.factions.P; import com.massivecraft.factions.P;
public class AutoLeaveTask implements Runnable public class AutoLeaveTask implements Runnable
{ {
private static AutoLeaveProcessTask task;
double rate; double rate;
public AutoLeaveTask() public AutoLeaveTask()
@ -13,11 +13,15 @@ public class AutoLeaveTask implements Runnable
this.rate = Conf.autoLeaveRoutineRunsEveryXMinutes; 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) if (this.rate != Conf.autoLeaveRoutineRunsEveryXMinutes)
P.p.startAutoLeaveTask(true); P.p.startAutoLeaveTask(true);
} }