[BREAKING ] Lazy permission updating.
* Each asynchronous permission check yields an update request anyway, thus frequent bulk update requests have been removed. * With join and world change, lazy permission updating is requested. Breaking: * Remove CheckConfig.getCachePermissions().
This commit is contained in:
parent
4bfc4f2cd2
commit
6dbb7d4299
|
@ -16,7 +16,6 @@ package fr.neatmonster.nocheatplus.checks.access;
|
|||
|
||||
import fr.neatmonster.nocheatplus.config.ConfPaths;
|
||||
import fr.neatmonster.nocheatplus.config.ConfigFile;
|
||||
import fr.neatmonster.nocheatplus.permissions.RegisteredPermission;
|
||||
|
||||
/**
|
||||
* Minimal implementation, doing nothing.
|
||||
|
@ -31,36 +30,17 @@ public abstract class ACheckConfig implements ICheckConfig {
|
|||
/** If to adapt to server side lag. */
|
||||
public final boolean lag;
|
||||
|
||||
/** Permissions to hold in player data cache, not final for flexibility. */
|
||||
protected RegisteredPermission[] cachePermissions;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @param pathPrefix Path prefix for the check section (example for use: prefix+"debug").
|
||||
*/
|
||||
public ACheckConfig(final ConfigFile config, final String pathPrefix){
|
||||
this(config, pathPrefix, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param config
|
||||
* @param pathPrefix Path prefix for the check section (example for use: prefix+"debug").
|
||||
* @param cachePermissions cachePermissions Permissions to hold in player data cache. Can be null.
|
||||
*/
|
||||
public ACheckConfig(final ConfigFile config, final String pathPrefix,
|
||||
final RegisteredPermission[] cachePermissions){
|
||||
public ACheckConfig(final ConfigFile config, final String pathPrefix){
|
||||
// TODO: Path prefix construction is somewhat inconsistent with debug hierarchy ?
|
||||
debug = config.getBoolean(pathPrefix + ConfPaths.SUB_DEBUG, config.getBoolean(ConfPaths.CHECKS_DEBUG, false));
|
||||
// TODO: Use lag flag where appropriate and document it (or get rid of it).
|
||||
lag = config.getBoolean(pathPrefix + ConfPaths.SUB_LAG, true) && config.getBoolean(ConfPaths.MISCELLANEOUS_LAG, true);
|
||||
this.cachePermissions = cachePermissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisteredPermission[] getCachePermissions() {
|
||||
return cachePermissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
package fr.neatmonster.nocheatplus.checks.access;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.permissions.RegisteredPermission;
|
||||
|
||||
/**
|
||||
* This interface must be implemented by all configuration classes.
|
||||
|
@ -39,10 +38,4 @@ public interface ICheckConfig {
|
|||
/** On the fly debug flags, to be set by commands and similar. */
|
||||
public void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* Retrieve the permissions that have to be updated for this check.
|
||||
* @return An array of permissions, may be null.
|
||||
*/
|
||||
public RegisteredPermission[] getCachePermissions();
|
||||
|
||||
}
|
||||
|
|
|
@ -51,9 +51,21 @@ public class ChatConfig extends ACheckConfig {
|
|||
}
|
||||
};
|
||||
|
||||
private static RegisteredPermission[] preferKeepUpdatedPermissions = new RegisteredPermission[]{
|
||||
// Only the permissions needed for async. checking.
|
||||
Permissions.CHAT_COLOR,
|
||||
Permissions.CHAT_TEXT,
|
||||
Permissions.CHAT_CAPTCHA,
|
||||
// TODO: COMMANDS, in case of handleascommand?
|
||||
};
|
||||
|
||||
/** The map containing the configurations per world. */
|
||||
private static final Map<String, ChatConfig> worldsMap = new HashMap<String, ChatConfig>();
|
||||
|
||||
public static RegisteredPermission[] getPreferKeepUpdatedPermissions() {
|
||||
return preferKeepUpdatedPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all the configurations.
|
||||
*/
|
||||
|
@ -161,12 +173,7 @@ public class ChatConfig extends ACheckConfig {
|
|||
* the data
|
||||
*/
|
||||
public ChatConfig(final ConfigFile config) {
|
||||
super(config, ConfPaths.CHAT, new RegisteredPermission[]{
|
||||
// Only the permissions needed for async. checking.
|
||||
Permissions.CHAT_COLOR,
|
||||
Permissions.CHAT_TEXT,
|
||||
Permissions.CHAT_CAPTCHA,
|
||||
});
|
||||
super(config, ConfPaths.CHAT);
|
||||
|
||||
captchaCheck = config.getBoolean(ConfPaths.CHAT_CAPTCHA_CHECK);
|
||||
captchaSkipCommands = config.getBoolean(ConfPaths.CHAT_CAPTCHA_SKIP_COMMANDS);
|
||||
|
|
|
@ -103,8 +103,6 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
|
|||
@EventHandler(priority=EventPriority.MONITOR)
|
||||
public void onPlayerChangedWorld(final PlayerChangedWorldEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
final PlayerData pData = DataManager.getPlayerData(player);
|
||||
pData.requestLazyPermissionUpdate(ChatConfig.getConfig(player).getCachePermissions());
|
||||
ChatData.getData(player).currentWorldName = player.getWorld().getName();
|
||||
|
||||
}
|
||||
|
@ -125,7 +123,6 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
|
|||
// (Might omit this if already cancelled.)
|
||||
final PlayerData pData = DataManager.getPlayerData(player);
|
||||
final ChatConfig cc = ChatConfig.getConfig(player);
|
||||
pData.requestLazyPermissionUpdate(cc.getCachePermissions());
|
||||
|
||||
// First the color check.
|
||||
if (!alreadyCancelled && color.isEnabled(player)) {
|
||||
|
@ -153,7 +150,6 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
|
|||
// Tell TickTask to update cached permissions.
|
||||
final PlayerData pData = DataManager.getPlayerData(player);
|
||||
final ChatConfig cc = ChatConfig.getConfig(player);
|
||||
pData.requestLazyPermissionUpdate(cc.getCachePermissions());
|
||||
|
||||
// Checks that replace parts of the message (color).
|
||||
if (color.isEnabled(player)) {
|
||||
|
@ -262,8 +258,6 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
|
|||
final ChatConfig cc = ChatConfig.getConfig(player);
|
||||
final ChatData data = ChatData.getData(player);
|
||||
|
||||
// Tell TickTask to update cached permissions.
|
||||
pData.requestLazyPermissionUpdate(cc.getCachePermissions());
|
||||
// (No forced permission update, because the associated permissions are treated as hints rather.)
|
||||
|
||||
// Reset captcha of player if needed.
|
||||
|
|
|
@ -31,6 +31,22 @@ import fr.neatmonster.nocheatplus.permissions.RegisteredPermission;
|
|||
*/
|
||||
public class NetConfig extends ACheckConfig {
|
||||
|
||||
private static RegisteredPermission[] preferKeepUpdatedPermissions = new RegisteredPermission[] {
|
||||
Permissions.NET_ATTACKFREQUENCY,
|
||||
Permissions.NET_FLYINGFREQUENCY,
|
||||
Permissions.NET_KEEPALIVEFREQUENCY,
|
||||
Permissions.NET_PACKETFREQUENCY,
|
||||
};
|
||||
|
||||
public static RegisteredPermission[] getPreferKeepUpdatedPermissions() {
|
||||
// TODO: Individual checks might want to register these, or just on permission checking.
|
||||
return preferKeepUpdatedPermissions;
|
||||
}
|
||||
|
||||
/////////////
|
||||
// Instance
|
||||
/////////////
|
||||
|
||||
public final boolean attackFrequencyActive;
|
||||
public final float attackFrequencyLimitSecondsHalf;
|
||||
public final float attackFrequencyLimitSecondsOne;
|
||||
|
@ -63,12 +79,7 @@ public class NetConfig extends ACheckConfig {
|
|||
|
||||
public NetConfig(final ConfigFile config) {
|
||||
// TODO: These permissions should have default policies.
|
||||
super(config, ConfPaths.NET, new RegisteredPermission[] {
|
||||
Permissions.NET_ATTACKFREQUENCY,
|
||||
Permissions.NET_FLYINGFREQUENCY,
|
||||
Permissions.NET_KEEPALIVEFREQUENCY,
|
||||
Permissions.NET_PACKETFREQUENCY,
|
||||
});
|
||||
super(config, ConfPaths.NET);
|
||||
|
||||
final ConfigFile globalConfig = ConfigManager.getConfigFile();
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package fr.neatmonster.nocheatplus.components.registry.exception;
|
||||
|
||||
/**
|
||||
* An item is not registered, although that is demanded in this context.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public class NotRegisteredException extends RegistryException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 6240601169826276653L;
|
||||
|
||||
public NotRegisteredException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NotRegisteredException(String message, Throwable cause,
|
||||
boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public NotRegisteredException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public NotRegisteredException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NotRegisteredException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
package fr.neatmonster.nocheatplus.permissions;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Combine permission information (registry): RegisteredPermission, policy
|
||||
* information (default fetching policy, extra flags), (the Bukkit permission is
|
||||
|
@ -16,9 +13,6 @@ public class PermissionInfo extends PermissionPolicy {
|
|||
// TODO: Consider IPermissionPolicy with read only access.
|
||||
|
||||
private final RegisteredPermission registeredPermission;
|
||||
/** Copy on write, primary thread write access only. */
|
||||
// TODO: Implement or remove.
|
||||
private RegisteredPermission[] preferUpdateOther = null;
|
||||
|
||||
/**
|
||||
* Minimal constructor.
|
||||
|
@ -43,37 +37,4 @@ public class PermissionInfo extends PermissionPolicy {
|
|||
return registeredPermission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy on write for primary thread only access.
|
||||
*
|
||||
* @param otherPermissions
|
||||
*/
|
||||
public void preferUpdateOther(final RegisteredPermission... otherPermissions) {
|
||||
final Set<RegisteredPermission> newEntries = new LinkedHashSet<RegisteredPermission>();
|
||||
if (preferUpdateOther != null) {
|
||||
for (int i = 0; i < preferUpdateOther.length; i++) {
|
||||
newEntries.add(preferUpdateOther[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < otherPermissions.length; i++) {
|
||||
newEntries.add(otherPermissions[i]);
|
||||
}
|
||||
preferUpdateOther = newEntries.toArray(new RegisteredPermission[newEntries.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internally stored array with other permissions that are preferred
|
||||
* to be updated when the permission represented by this PermissionInfo is
|
||||
* to be updated. Note that the permission itself is not automatically
|
||||
* included, but external calls might add it. Aiming at requesting
|
||||
* permission updates from another thread than the primary server thread.
|
||||
* <br>
|
||||
* Thread-safe read.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public RegisteredPermission[] preferUpdateOther() {
|
||||
return this.preferUpdateOther;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,12 +2,15 @@ package fr.neatmonster.nocheatplus.permissions;
|
|||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import fr.neatmonster.nocheatplus.components.registry.exception.AlreadyRegisteredException;
|
||||
import fr.neatmonster.nocheatplus.components.registry.exception.NotRegisteredException;
|
||||
import fr.neatmonster.nocheatplus.utilities.ds.map.HashMapLOW;
|
||||
|
||||
/**
|
||||
|
@ -28,6 +31,16 @@ public class PermissionRegistry {
|
|||
/** No need to map to the strings in an extra step here. */
|
||||
private final HashMapLOW<String, PermissionInfo> infosString = new HashMapLOW<String, PermissionInfo>(lock, 100);
|
||||
|
||||
// TODO: Might do lazy tasks for all player data regularly.
|
||||
/**
|
||||
* All registered permissions that are meant to be kept updated for players.
|
||||
* Guarantees are not to actually keep them updated, but might lazily update
|
||||
* them with world changing and logging on.
|
||||
*/
|
||||
private final LinkedHashSet<RegisteredPermission> preferKeepUpdated = new LinkedHashSet<RegisteredPermission>();
|
||||
private RegisteredPermission[] preferKeepUpdatedWorld = new RegisteredPermission[0];
|
||||
private RegisteredPermission[] preferKeepUpdatedOffline = new RegisteredPermission[0];
|
||||
|
||||
/**
|
||||
*
|
||||
* @param nextId Next id to return with getId.
|
||||
|
@ -130,6 +143,7 @@ public class PermissionRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* (Primary thread only.)
|
||||
*
|
||||
* @param settings
|
||||
* @return All registered permissions for which the policy has changed (by
|
||||
|
@ -153,7 +167,72 @@ public class PermissionRegistry {
|
|||
// Still set in either case, in case we missed something (cheap).
|
||||
info.set(newPolicy);
|
||||
}
|
||||
arrangePreferKeepUpdated();
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* (Primary thread only.)
|
||||
*/
|
||||
public void arrangePreferKeepUpdated() {
|
||||
final List<RegisteredPermission> preferKeepUpdatedWorld = new LinkedList<RegisteredPermission>();
|
||||
final List<RegisteredPermission> preferKeepUpdatedOffline = new LinkedList<RegisteredPermission>();
|
||||
// (No permanent updating yet.)
|
||||
for (final RegisteredPermission registeredPermission : this.preferKeepUpdated) {
|
||||
final PermissionInfo info = infosInt.get(registeredPermission.getId());
|
||||
switch (info.fetchingPolicy()) {
|
||||
case FALSE:
|
||||
case TRUE:
|
||||
// Skip only these.
|
||||
continue;
|
||||
case ALWAYS:
|
||||
case INTERVAL:
|
||||
// Update as often as makes sense in this context.
|
||||
// TODO: Might later run lazy tasks permanently for online players.
|
||||
preferKeepUpdatedOffline.add(registeredPermission);
|
||||
preferKeepUpdatedWorld.add(registeredPermission);
|
||||
break;
|
||||
default:
|
||||
if (info.invalidationOffline()) {
|
||||
preferKeepUpdatedOffline.add(registeredPermission);
|
||||
}
|
||||
else if (info.invalidationWorld()) { // TODO: 'else' as long as world includes offline.
|
||||
preferKeepUpdatedOffline.add(registeredPermission); // TODO: as long as world includes offline.
|
||||
preferKeepUpdatedWorld.add(registeredPermission);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.preferKeepUpdatedWorld = preferKeepUpdatedWorld.toArray(new RegisteredPermission[preferKeepUpdatedWorld.size()]);
|
||||
this.preferKeepUpdatedOffline = preferKeepUpdatedOffline.toArray(new RegisteredPermission[preferKeepUpdatedOffline.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions will be sorted into areas, depending on the set policies.
|
||||
* However that will only happen with either calling
|
||||
* arrangePreferKeepUpdated() or updateSettings(PermissionSettings).
|
||||
*
|
||||
* @param registeredPermissions
|
||||
*/
|
||||
public void preferKeepUpdated(final RegisteredPermission... registeredPermissions) {
|
||||
for (final RegisteredPermission registeredPermission : registeredPermissions) {
|
||||
final PermissionInfo info = infosInt.get(registeredPermission.getId());
|
||||
if (info == null) {
|
||||
throw new NotRegisteredException("Id not registered: " + registeredPermission.getId());
|
||||
}
|
||||
if (info.getRegisteredPermission() != registeredPermission) {
|
||||
throw new AlreadyRegisteredException("RegisteredPermission instances should be identical.");
|
||||
}
|
||||
preferKeepUpdated.add(info.getRegisteredPermission()); // Add the already registered object.
|
||||
}
|
||||
}
|
||||
|
||||
public RegisteredPermission[] getPreferKeepUpdatedWorld() {
|
||||
return preferKeepUpdatedWorld;
|
||||
}
|
||||
|
||||
public RegisteredPermission[] getPreferKeepUpdatedOffline() {
|
||||
return preferKeepUpdatedOffline;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -558,7 +558,7 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
* May be null.
|
||||
*/
|
||||
public void requestLazyPermissionUpdate(final RegisteredPermission...registeredPermissions) {
|
||||
if (registeredPermissions == null) {
|
||||
if (registeredPermissions == null || registeredPermissions.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
|
@ -586,6 +586,7 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
|
||||
void onPlayerJoin(final long timeNow) {
|
||||
invalidateOffline();
|
||||
requestLazyPermissionUpdate(permissionRegistry.getPreferKeepUpdatedOffline());
|
||||
}
|
||||
|
||||
private void invalidateOffline() {
|
||||
|
@ -594,7 +595,14 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
while (it.hasNext()) {
|
||||
final PermissionNode node = it.next().getValue();
|
||||
final PermissionInfo info = node.getPermissionInfo();
|
||||
if (info.invalidationOffline() || info.invalidationWorld()) {
|
||||
if (info.invalidationOffline()
|
||||
/*
|
||||
* TODO: world based should only be invalidated with world
|
||||
* changing. Therefore store the last world info
|
||||
* (UUID/name?) in PlayerData and use on login for
|
||||
* comparison.
|
||||
*/
|
||||
|| info.invalidationWorld()) {
|
||||
// TODO: Really count leave as world change?
|
||||
node.invalidate();
|
||||
}
|
||||
|
@ -618,6 +626,7 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
node.invalidate();
|
||||
}
|
||||
}
|
||||
requestLazyPermissionUpdate(permissionRegistry.getPreferKeepUpdatedWorld());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.bukkit.scheduler.BukkitScheduler;
|
|||
import fr.neatmonster.nocheatplus.checks.blockbreak.BlockBreakListener;
|
||||
import fr.neatmonster.nocheatplus.checks.blockinteract.BlockInteractListener;
|
||||
import fr.neatmonster.nocheatplus.checks.blockplace.BlockPlaceListener;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.ChatConfig;
|
||||
import fr.neatmonster.nocheatplus.checks.chat.ChatListener;
|
||||
import fr.neatmonster.nocheatplus.checks.combined.CombinedData;
|
||||
import fr.neatmonster.nocheatplus.checks.combined.CombinedListener;
|
||||
|
@ -61,6 +62,7 @@ import fr.neatmonster.nocheatplus.checks.inventory.InventoryListener;
|
|||
import fr.neatmonster.nocheatplus.checks.moving.MovingListener;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace.TraceEntryPool;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.AuxMoving;
|
||||
import fr.neatmonster.nocheatplus.checks.net.NetConfig;
|
||||
import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
|
||||
import fr.neatmonster.nocheatplus.clients.ModUtil;
|
||||
import fr.neatmonster.nocheatplus.command.NoCheatPlusCommand;
|
||||
|
@ -967,6 +969,12 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
|
|||
command.setExecutor(commandHandler);
|
||||
// (CommandHandler is TabExecutor.)
|
||||
|
||||
// Tell the permission registry, which permissions should get updated.
|
||||
// TODO: confine by check enabled flags.
|
||||
permissionRegistry.preferKeepUpdated(NetConfig.getPreferKeepUpdatedPermissions());
|
||||
permissionRegistry.preferKeepUpdated(ChatConfig.getPreferKeepUpdatedPermissions());
|
||||
permissionRegistry.arrangePreferKeepUpdated();
|
||||
|
||||
////////////////////////////////
|
||||
// Tasks, post-rumble-logging
|
||||
////////////////////////////////
|
||||
|
@ -1049,6 +1057,10 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
|
|||
*/
|
||||
private void postEnable(final Player[] onlinePlayers) {
|
||||
logManager.info(Streams.INIT, "Post-enable running...");
|
||||
// Update permission registry internals for permissions preferred to be updated.
|
||||
// (By now checks should have noted what they want.)
|
||||
permissionRegistry.arrangePreferKeepUpdated();
|
||||
|
||||
final ConfigFile config = ConfigManager.getConfigFile();
|
||||
try {
|
||||
// Command protection feature.
|
||||
|
|
Loading…
Reference in New Issue