[BLEEDING][BREAKING] New implementation for exemption. (+)
* Store the per check type flags within PlayerData/PlayerCheckTypeTree. Access methods within PlayerData. * (More) thread-safe access, with the twist that alterations are mostly done within the thread-context (primary thread vs. asynchronous). (+) Mimic legacy behavior, by non-nested entries. However untouched nested entries are possible, creating ExemptionContext instances with other negative ids. (-) No ExemptionRegistry is implemented. This just aims at replacing the internals, without altering the (legacy) behavior.
This commit is contained in:
parent
eb41f4397a
commit
cec4a4d129
|
@ -0,0 +1,65 @@
|
|||
package fr.neatmonster.nocheatplus.hooks;
|
||||
|
||||
/**
|
||||
* Both hashCode and equals only compare the ids, so equals is not fit for
|
||||
* comparing side conditions - AWAIT a method like
|
||||
* isEquivalentTo(ExemptionContext) for that purpose, rather.
|
||||
* <hr>
|
||||
* Note that some ids like 0 and -1 are reserved. The registry will only deal
|
||||
* positive ids.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public class ExemptionContext {
|
||||
|
||||
/** Id is -1. */
|
||||
public static final ExemptionContext LEGACY_NON_NESTED = new ExemptionContext(-1);
|
||||
/** Id is 0. */
|
||||
public static final ExemptionContext ANONYMOUS_NESTED = new ExemptionContext(0);
|
||||
|
||||
///////////////
|
||||
// Instance
|
||||
///////////////
|
||||
|
||||
/*
|
||||
*
|
||||
* TODO: How to use (one context = one thing, or one context contains multiple ids.
|
||||
* -> so contexts contain contexts (...).
|
||||
*/
|
||||
|
||||
private Integer id;
|
||||
|
||||
public ExemptionContext(final Integer id) {
|
||||
if (id == null) {
|
||||
throw new NullPointerException("The id must not be null.");
|
||||
}
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
else if (obj instanceof ExemptionContext) {
|
||||
return id.equals(((ExemptionContext) obj).getId());
|
||||
}
|
||||
else if (obj instanceof Integer) {
|
||||
return id.equals((Integer) obj);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package fr.neatmonster.nocheatplus.hooks;
|
||||
|
||||
/**
|
||||
* Register contexts, get ids >= 0 for new contexts.
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public class ExemptionRegistry {
|
||||
|
||||
// TODO: ...
|
||||
|
||||
}
|
|
@ -14,11 +14,6 @@
|
|||
*/
|
||||
package fr.neatmonster.nocheatplus.hooks;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
@ -26,10 +21,20 @@ import org.bukkit.entity.Player;
|
|||
|
||||
import fr.neatmonster.nocheatplus.NCPAPIProvider;
|
||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.utilities.CheckTypeUtil;
|
||||
import fr.neatmonster.nocheatplus.players.DataManager;
|
||||
import fr.neatmonster.nocheatplus.players.PlayerData;
|
||||
|
||||
/**
|
||||
* API for exempting players of checks, checked before calculations are done.
|
||||
* <hr>
|
||||
* Refactoring stage: Route to DataManager/PlayerData, which will somehow
|
||||
* attempt to mimic legacy behavior. Do note that checking for meta data and
|
||||
* other specialties are not handled within DataManager/PlayerData yet.
|
||||
* <hr>
|
||||
* Except clear and accessing settings, it will be "safe" to access methods
|
||||
* asynchronously to the server primary thread, however isExempted always
|
||||
* reflects a non-synchronized state of all thread contexts, while exempt and
|
||||
* unexempt will be local to the thread-context (primary thread, asynchronous).
|
||||
*
|
||||
* @author asofold
|
||||
*/
|
||||
|
@ -37,16 +42,6 @@ public class NCPExemptionManager {
|
|||
|
||||
private static ExemptionSettings settings = new ExemptionSettings();
|
||||
|
||||
/**
|
||||
* A map associating a check type with the unique ids of its exempted
|
||||
* players.
|
||||
*/
|
||||
private static final Map<CheckType, Set<UUID>> exempted = new HashMap<CheckType, Set<UUID>>();
|
||||
|
||||
static {
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current settings.
|
||||
*
|
||||
|
@ -78,15 +73,7 @@ public class NCPExemptionManager {
|
|||
* Remove all exemptions.
|
||||
*/
|
||||
public static final void clear() {
|
||||
// Use put with a new map to keep entries to stay thread safe.
|
||||
for (final CheckType checkType : CheckType.values()) {
|
||||
if (CheckTypeUtil.needsSynchronization(checkType)) {
|
||||
exempted.put(checkType, Collections.synchronizedSet(new HashSet<UUID>()));
|
||||
}
|
||||
else {
|
||||
exempted.put(checkType, new HashSet<UUID>());
|
||||
}
|
||||
}
|
||||
DataManager.clearAllExemptions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,9 +96,11 @@ public class NCPExemptionManager {
|
|||
* The check type.
|
||||
*/
|
||||
public static final void exemptPermanently(final UUID id, final CheckType checkType) {
|
||||
for (final CheckType refType : CheckTypeUtil.getWithDescendants(checkType)) {
|
||||
exempted.get(refType).add(id);
|
||||
final PlayerData data = DataManager.getPlayerData(id);
|
||||
if (data != null) {
|
||||
data.exempt(checkType);
|
||||
}
|
||||
// TODO: else throw ?
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,7 +141,8 @@ public class NCPExemptionManager {
|
|||
* @return If the entity is exempted from checks right now.
|
||||
*/
|
||||
public static final boolean isExempted(final UUID id, final CheckType checkType) {
|
||||
return exempted.get(checkType).contains(id);
|
||||
final PlayerData data = DataManager.getPlayerData(id);
|
||||
return data != null && data.isExempted(checkType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,8 +201,9 @@ public class NCPExemptionManager {
|
|||
* The check type.
|
||||
*/
|
||||
public static final void unexempt(final UUID id, final CheckType checkType) {
|
||||
for (final CheckType refType : CheckTypeUtil.getWithDescendants(checkType)) {
|
||||
exempted.get(refType).remove(id);
|
||||
final PlayerData data = DataManager.getPlayerData(id);
|
||||
if (data != null) {
|
||||
data.unexempt(checkType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1036,4 +1036,14 @@ public class DataManager implements INeedConfig, ComponentRegistry<IRemoveData>,
|
|||
instance.frequentPlayerTasks.addAsynchronous(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static void clearAllExemptions() {
|
||||
final Iterator<Entry<UUID, PlayerData>> it = instance.playerData.iterator();
|
||||
while (it.hasNext()) {
|
||||
it.next().getValue().clearAllExemptions();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,370 @@
|
|||
package fr.neatmonster.nocheatplus.players;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.components.data.checktype.CheckTypeTree;
|
||||
import fr.neatmonster.nocheatplus.components.data.checktype.CheckTypeTree.CheckTypeTreeNode;
|
||||
import fr.neatmonster.nocheatplus.hooks.ExemptionContext;
|
||||
import fr.neatmonster.nocheatplus.players.PlayerCheckTypeTree.PlayerCheckTypeTreeNode;
|
||||
|
||||
/**
|
||||
* <hr>
|
||||
* Exemption and unexemption are handled within two contexts: primary thread and
|
||||
* asynchronous. Exemption testing may account for both states (without
|
||||
* synchronization).
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public class PlayerCheckTypeTree extends CheckTypeTree<PlayerCheckTypeTreeNode>{
|
||||
|
||||
/**
|
||||
* <hr>
|
||||
* <ul>
|
||||
* <li>Nodes are not meant to be passed to the outside (!).</li>
|
||||
* <li>Locking for 'asynchronous' access has to be done externally.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
static class PlayerCheckTypeTreeNode extends CheckTypeTreeNode<PlayerCheckTypeTreeNode> {
|
||||
|
||||
/**
|
||||
* Explicitly exempted by API call (cumulative flag), excludes checking for
|
||||
* meta data and the like.
|
||||
*/
|
||||
private boolean exemptedPrimaryThread = false;
|
||||
/**
|
||||
* Explicitly exempted by API call (cumulative flag), excludes checking for
|
||||
* meta data and the like.
|
||||
*/
|
||||
private boolean exemptedAsynchronous = false;
|
||||
|
||||
/**
|
||||
* Exemption contexts that apply here. Lazily allocate.
|
||||
*/
|
||||
/*
|
||||
* TODO: Consider a ParallelList (access independently without need for
|
||||
* mergePrimaryThread, or extend DualList by appropriate methods).
|
||||
*/
|
||||
private List<ExemptionContext> exemptionsPrimaryThread = null;
|
||||
private List<ExemptionContext> exemptionsAsynchronous = null;
|
||||
|
||||
PlayerCheckTypeTreeNode(final CheckType checkType,
|
||||
final PlayerCheckTypeTreeNode parent,
|
||||
final CheckTypeTreeNodeFactory<PlayerCheckTypeTreeNode> factory) {
|
||||
super(checkType, parent, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for exemption by explicit API call. Excludes checking for meta
|
||||
* data or other API.
|
||||
* <hr>
|
||||
* No locking involved, checking flags for primary thread and
|
||||
* asynchronous access.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isExempted() {
|
||||
// Typically this should boil down to thread-local use, as it is CheckType-bound.
|
||||
return exemptedPrimaryThread || exemptedAsynchronous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call under lock.
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
void exemptAsynchronous(final ExemptionContext context) {
|
||||
exemptedAsynchronous = true;
|
||||
if (exemptionsAsynchronous == null) {
|
||||
exemptionsAsynchronous = new LinkedList<ExemptionContext>();
|
||||
}
|
||||
exemptionsAsynchronous.add(0, context);
|
||||
}
|
||||
|
||||
void exemptPrimaryThread(final ExemptionContext context) {
|
||||
exemptedPrimaryThread = true;
|
||||
if (exemptionsPrimaryThread == null) {
|
||||
exemptionsPrimaryThread = new LinkedList<ExemptionContext>();
|
||||
}
|
||||
exemptionsPrimaryThread.add(0, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call under lock.
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
void unexemptAsynchronous(final ExemptionContext context) {
|
||||
if (exemptionsAsynchronous != null) {
|
||||
exemptionsAsynchronous.remove(context);
|
||||
if (exemptionsAsynchronous.isEmpty()) {
|
||||
// TODO: Have a counter to delay resetting?
|
||||
exemptedAsynchronous = false;
|
||||
exemptionsAsynchronous = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unexemptPrimaryThread(final ExemptionContext context) {
|
||||
if (exemptionsPrimaryThread != null) {
|
||||
exemptionsPrimaryThread.remove(context);
|
||||
if (exemptionsPrimaryThread.isEmpty()) {
|
||||
// TODO: Have a counter to delay resetting?
|
||||
exemptedPrimaryThread = false;
|
||||
exemptionsPrimaryThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call under lock.
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
void unexemptAllAsynchronous(final ExemptionContext context) {
|
||||
if (exemptionsAsynchronous != null) {
|
||||
exemptionsAsynchronous.removeAll(Collections.singleton(context));
|
||||
if (exemptionsAsynchronous.isEmpty()) {
|
||||
// TODO: Have a counter to delay resetting?
|
||||
exemptedAsynchronous = false;
|
||||
exemptionsAsynchronous = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unexemptAllPrimaryThread(final ExemptionContext context) {
|
||||
if (exemptionsPrimaryThread != null) {
|
||||
exemptionsPrimaryThread.removeAll(Collections.singleton(context));
|
||||
if (exemptionsPrimaryThread.isEmpty()) {
|
||||
// TODO: Have a counter to delay resetting?
|
||||
exemptedPrimaryThread = false;
|
||||
exemptionsPrimaryThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Must call under lock.
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
boolean isExemptedAsynchronous(ExemptionContext context) {
|
||||
return exemptionsAsynchronous != null && exemptionsAsynchronous.contains(context);
|
||||
}
|
||||
|
||||
boolean isExemptedPrimaryThread(final ExemptionContext context) {
|
||||
return exemptionsPrimaryThread != null && exemptionsPrimaryThread.contains(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary thread only, must call under lock.
|
||||
*/
|
||||
public void clearAllExemptions() {
|
||||
exemptedAsynchronous = false;
|
||||
exemptedPrimaryThread = false;
|
||||
exemptionsAsynchronous = null;
|
||||
exemptionsPrimaryThread = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////
|
||||
// Instance
|
||||
////////////////
|
||||
|
||||
private final Lock lock;
|
||||
|
||||
public PlayerCheckTypeTree(final Lock lock) {
|
||||
super();
|
||||
this.lock = lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlayerCheckTypeTreeNode newNode(CheckType checkType,
|
||||
PlayerCheckTypeTreeNode parent,
|
||||
CheckTypeTreeNodeFactory<PlayerCheckTypeTreeNode> factory) {
|
||||
return new PlayerCheckTypeTreeNode(checkType, parent, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* <hr>
|
||||
* Thread-safe read, not synchronized.
|
||||
*
|
||||
* @param checkType
|
||||
* @return
|
||||
*/
|
||||
public boolean isExempted(final CheckType checkType) {
|
||||
return getNode(checkType).isExempted(); // Fast read.
|
||||
}
|
||||
|
||||
public void exempt(final CheckType checkType, final ExemptionContext context) {
|
||||
final PlayerCheckTypeTreeNode node = getNode(checkType);
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("Invalid check type.");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
exemptPrimaryThread(node, context);
|
||||
}
|
||||
else {
|
||||
exemptAsynchronous(node, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void exemptPrimaryThread(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.exemptPrimaryThread(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void exemptAsynchronous(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
lock.lock();
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.exemptAsynchronous(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public void unexempt(final CheckType checkType, final ExemptionContext context) {
|
||||
final PlayerCheckTypeTreeNode node = getNode(checkType);
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("Invalid check type.");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
unexemptPrimaryThread(node, context);
|
||||
}
|
||||
else {
|
||||
unexemptAsynchronous(node, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void unexemptPrimaryThread(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.unexemptPrimaryThread(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void unexemptAsynchronous(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
lock.lock();
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.unexemptAsynchronous(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public void unexemptAll(final CheckType checkType, final ExemptionContext context) {
|
||||
final PlayerCheckTypeTreeNode node = getNode(checkType);
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("Invalid check type.");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
unexemptAllPrimaryThread(node, context);
|
||||
}
|
||||
else {
|
||||
unexemptAllAsynchronous(node, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void unexemptAllPrimaryThread(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.unexemptAllPrimaryThread(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void unexemptAllAsynchronous(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
lock.lock();
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.unexemptAllAsynchronous(context);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public boolean isExempted(final CheckType checkType, final ExemptionContext context) {
|
||||
final PlayerCheckTypeTreeNode node = getNode(checkType);
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("Invalid check type.");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
return isExemptedPrimaryThread(node, context);
|
||||
}
|
||||
else {
|
||||
return isExemptedAsynchronous(node, context);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExemptedPrimaryThread(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
return node.isExemptedPrimaryThread(context);
|
||||
}
|
||||
|
||||
private boolean isExemptedAsynchronous(final PlayerCheckTypeTreeNode node, final ExemptionContext context) {
|
||||
final boolean res;
|
||||
lock.lock();
|
||||
res = node.isExemptedPrimaryThread(context);
|
||||
lock.unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call from the primary thread only.
|
||||
*
|
||||
*/
|
||||
public void clearAllExemptions() {
|
||||
clearAllExemptions(CheckType.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call from the primary thread only.
|
||||
*
|
||||
* @param checkType
|
||||
*/
|
||||
public void clearAllExemptions(final CheckType checkType) {
|
||||
final PlayerCheckTypeTreeNode node = getNode(checkType);
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("Invalid check type.");
|
||||
}
|
||||
lock.lock();
|
||||
visitWithDescendants(node, new Visitor<PlayerCheckTypeTreeNode>() {
|
||||
@Override
|
||||
public boolean visit(final PlayerCheckTypeTreeNode node) {
|
||||
node.clearAllExemptions();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -21,15 +21,19 @@ import java.util.Iterator;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
|
||||
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
|
||||
import fr.neatmonster.nocheatplus.components.data.ICanHandleTimeRunningBackwards;
|
||||
import fr.neatmonster.nocheatplus.components.data.IData;
|
||||
import fr.neatmonster.nocheatplus.hooks.ExemptionContext;
|
||||
import fr.neatmonster.nocheatplus.permissions.PermissionInfo;
|
||||
import fr.neatmonster.nocheatplus.permissions.PermissionNode;
|
||||
import fr.neatmonster.nocheatplus.permissions.PermissionPolicy.FetchingPolicy;
|
||||
|
@ -107,17 +111,21 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
// Instance //
|
||||
//////////////
|
||||
|
||||
// TODO: Use the same lock for permissions stuff ?
|
||||
/** Per player lock. */
|
||||
/*
|
||||
* TODO: Impact of using this everywhere is uncertain. For exemptions and
|
||||
* permissions it'll be ok, because nodes get created once for most, but for
|
||||
* permission updates (merge primary thread) and the like, it'll not be as
|
||||
* certain.
|
||||
*/
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
/** Not sure this is the future of extra properties. */
|
||||
private Set<String> tags = null;
|
||||
|
||||
/*
|
||||
* TODO: Consider updating the UUID for stuff like
|
||||
* "exempt player/name on next login". This also implies the addition of a
|
||||
* method to force-postpone data removal, as well as configuration for how
|
||||
* exactly to apply/timeout, plus new syntax for 'ncp exempt' (flags/side
|
||||
* conditions like +login/...).
|
||||
* TODO: Concept for updating names for UUIDs -> + OfflinePlayerData,
|
||||
* uncertain when/how to access.
|
||||
*/
|
||||
/** Unique id of the player. */
|
||||
final UUID playerId;
|
||||
|
@ -138,16 +146,19 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
private final PermissionRegistry permissionRegistry;
|
||||
|
||||
/** Permission cache. */
|
||||
private final HashMapLOW<Integer, PermissionNode> permissions = new HashMapLOW<Integer, PermissionNode>(35);
|
||||
private final HashMapLOW<Integer, PermissionNode> permissions = new HashMapLOW<Integer, PermissionNode>(lock, 35);
|
||||
|
||||
private boolean requestUpdateInventory = false;
|
||||
private boolean requestPlayerSetBack = false;
|
||||
|
||||
private boolean frequentPlayerTaskShouldBeScheduled = false;
|
||||
/** Actually queried ones. */
|
||||
private final DualSet<RegisteredPermission> updatePermissions = new DualSet<RegisteredPermission>();
|
||||
private final DualSet<RegisteredPermission> updatePermissions = new DualSet<RegisteredPermission>(lock);
|
||||
/** Possibly needed in future. */
|
||||
private final DualSet<RegisteredPermission> updatePermissionsLazy = new DualSet<RegisteredPermission>();
|
||||
private final DualSet<RegisteredPermission> updatePermissionsLazy = new DualSet<RegisteredPermission>(lock);
|
||||
|
||||
/** TODO: Soon to add minimized offline data, so these kind of things don't impact as much. */
|
||||
private final PlayerCheckTypeTree checkTypeTree = new PlayerCheckTypeTree(lock);
|
||||
|
||||
/** Unregister the tasks once 0 count is reached. */
|
||||
private short frequentTaskDelayUnregister = 0;
|
||||
|
@ -656,4 +667,118 @@ public class PlayerData implements IData, ICanHandleTimeRunningBackwards {
|
|||
updatePermissionsLazy.clearPrimaryThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic legacy behavior (non-nested) - exempt including descendants
|
||||
* recursively. Note that contexts other than
|
||||
* ExemptionContext.LEGACY_NON_NESTED will not be touched.
|
||||
*
|
||||
* @param checkType
|
||||
*/
|
||||
public void exempt(final CheckType checkType) {
|
||||
checkTypeTree.exempt(checkType, ExemptionContext.LEGACY_NON_NESTED);
|
||||
// TODO: Handlers?
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimic legacy behavior (non-nested) - unexempt including descendants
|
||||
* recursively. Note that contexts other than
|
||||
* ExemptionContext.LEGACY_NON_NESTED will not be touched.
|
||||
* <hr>
|
||||
* Primary thread and asynchronous access are separated and yield different
|
||||
* results, it's imperative to always unexempt properly for asyncrhonous
|
||||
* thread contexts, as isExempted reflects a mixture of both.
|
||||
*
|
||||
* @param checkType
|
||||
*/
|
||||
public void unexempt(final CheckType checkType) {
|
||||
checkTypeTree.unexemptAll(checkType, ExemptionContext.LEGACY_NON_NESTED);
|
||||
// TODO: Handlers?
|
||||
}
|
||||
|
||||
/**
|
||||
* Exempt with reference to the given context with descendants recursively.
|
||||
* <br>
|
||||
* Note that multiple calls to exempt demand multiple calls to
|
||||
* unexempt(CheckType, ExemptionContext).
|
||||
* <hr>
|
||||
* Primary thread and asynchronous access are separated and yield different
|
||||
* results, it's imperative to always unexempt properly for asyncrhonous
|
||||
* thread contexts, as isExempted reflects a mixture of both.
|
||||
*
|
||||
* @param checkType
|
||||
* @param context
|
||||
*/
|
||||
public void exempt(final CheckType checkType, final ExemptionContext context) {
|
||||
checkTypeTree.exempt(checkType, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unexempt once, including descendants recursively. <br>
|
||||
* Note that for multiple calls to exempt with one context, multiple calls
|
||||
* to unexempt with that context may be necessary to fully unexempt, or call
|
||||
* unexemptAll for the context.
|
||||
* <hr>
|
||||
* ExemptionContext.LEGACY_NON_NESTED is not automatically calling
|
||||
* unexemptAll as is done with the legacy signature unexempt(CheckType).
|
||||
* <hr>
|
||||
* Primary thread and asynchronous access are separated and yield different
|
||||
* results, it's imperative to always unexempt properly for asyncrhonous
|
||||
* thread contexts, as isExempted reflects a mixture of both.
|
||||
*
|
||||
* @param checkType
|
||||
* @param context
|
||||
*/
|
||||
public void unexempt(final CheckType checkType, final ExemptionContext context) {
|
||||
checkTypeTree.unexempt(checkType, context);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove all (potentially nested) entries context for the given checkType
|
||||
* and descendants recursively.
|
||||
* <hr>
|
||||
* Primary thread and asynchronous access are separated and yield different
|
||||
* results, it's imperative to always unexempt properly for asyncrhonous
|
||||
* thread contexts, as isExempted reflects a mixture of both.
|
||||
*
|
||||
* @param checkType
|
||||
* @param context
|
||||
*/
|
||||
public void unexemptAll(final CheckType checkType, final ExemptionContext context) {
|
||||
checkTypeTree.unexemptAll(checkType, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for exemption.
|
||||
* <hr>
|
||||
* Thread-safe read (not synchronized).
|
||||
*
|
||||
* @param checkType
|
||||
* @return
|
||||
*/
|
||||
public boolean isExempted(final CheckType checkType) {
|
||||
return checkTypeTree.isExempted(checkType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all exemptions, for all thread contexts.
|
||||
* <hr>
|
||||
* Call from the primary thread only.
|
||||
*/
|
||||
public void clearAllExemptions() {
|
||||
checkTypeTree.clearAllExemptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all exemptions for the given checkType and descendants recursively,
|
||||
* for all thread contexts.
|
||||
* <hr>
|
||||
* Call from the primary thread only.
|
||||
*
|
||||
* @param checkType
|
||||
*/
|
||||
public void clearAllExemptions(final CheckType checkType) {
|
||||
checkTypeTree.clearAllExemptions(checkType);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -181,26 +181,25 @@ public class CheckUtils {
|
|||
* information.).
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: Once thread-safe read has been implemented, check the fastest
|
||||
* thing first (likely exemption).
|
||||
*/
|
||||
|
||||
final RegisteredPermission permission = checkType.getPermission();
|
||||
if (permission != null) {
|
||||
// Check permission policy/cache regardless of the thread context.
|
||||
if (pData.hasPermission(permission, player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO: Refine this error message +- put in place where it needs to be.
|
||||
if (!isPrimaryThread && !CheckTypeUtil.needsSynchronization(checkType)) {
|
||||
// Checking for exemption can cause harm now.
|
||||
/*
|
||||
* Checking for exemption can't cause harm anymore, however even
|
||||
* fetching data or configuration might still lead to everything
|
||||
* exploding.
|
||||
*/
|
||||
improperAPIAccess(checkType);
|
||||
}
|
||||
// TODO: New exemption implementation (thread-safe read).
|
||||
// TODO: Maybe a solution: force sync into primary thread a) each time b) once with lazy force set to use copy on write [for the player or global?].
|
||||
return NCPExemptionManager.isExempted(player, checkType, isPrimaryThread);
|
||||
// Exemption check.
|
||||
if (NCPExemptionManager.isExempted(player, checkType, isPrimaryThread)) {
|
||||
return true;
|
||||
}
|
||||
// Check permission policy/cache regardless of the thread context.
|
||||
final RegisteredPermission permission = checkType.getPermission();
|
||||
if (permission != null && pData.hasPermission(permission, player)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue