Add locking against changes to generic registrations.

Let default implementation use registration nodes.
This commit is contained in:
asofold 2016-06-22 22:32:51 +02:00
parent 97129c13d6
commit c9efd7b76c
9 changed files with 249 additions and 95 deletions

View File

@ -40,7 +40,7 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
*/
public class Critical extends Check {
private AuxMoving auxMoving = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(AuxMoving.class);
private final AuxMoving auxMoving = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(AuxMoving.class);
/**
* Instantiates a new critical check.

View File

@ -55,6 +55,7 @@ import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeEnchant;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.compat.IBridgeCrossPlugin;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.stats.Counters;
@ -110,7 +111,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
private final int idCancelDead = counters.registerKey("canceldead");
// Assume it to stay the same all time.
private final IBridgeCrossPlugin crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(IBridgeCrossPlugin.class);
private final IGenericInstanceHandle<IBridgeCrossPlugin> crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IBridgeCrossPlugin.class);
public FightListener() {
super(CheckType.FIGHT);
@ -489,7 +490,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
final Player damagedPlayer = damaged instanceof Player ? (Player) damaged : null;
final FightData damagedData = damagedPlayer == null ? null : FightData.getData(damagedPlayer);
final boolean damagedIsDead = damaged.isDead();
final boolean damagedIsFake = !crossPlugin.isNativeEntity(damaged);
final boolean damagedIsFake = !crossPlugin.getHandle().isNativeEntity(damaged);
if (damagedPlayer != null && !damagedIsDead) {
// God mode check.
// (Do not test the savage.)
@ -588,7 +589,7 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
attackerData.lastExplosionDamageTick = -1;
attackerData.lastExplosionEntityId = Integer.MAX_VALUE;
}
else if (handleNormalDamage(player, !crossPlugin.isNativePlayer(player),
else if (handleNormalDamage(player, !crossPlugin.getHandle().isNativePlayer(player),
damaged, damagedIsFake,
BridgeHealth.getOriginalDamage(event), BridgeHealth.getFinalDamage(event),
tick, attackerData)) {

View File

@ -23,6 +23,7 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.net.NetData;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.compat.IBridgeCrossPlugin;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.TickTask;
@ -31,6 +32,8 @@ import fr.neatmonster.nocheatplus.utilities.TickTask;
*/
public class GodMode extends Check {
private final IGenericInstanceHandle<IBridgeCrossPlugin> crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IBridgeCrossPlugin.class);
/**
* Instantiates a new god mode check.
*/
@ -195,7 +198,7 @@ public class GodMode extends Check {
// TODO: Is this still relevant ?
// First check if the player is really dead (e.g. another plugin could have just fired an artificial event).
if (BridgeHealth.getHealth(player) <= 0.0 && player.isDead()
&& NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(IBridgeCrossPlugin.class).isNativeEntity(player)) {
&& crossPlugin.getHandle().isNativeEntity(player)) {
try {
// Schedule a task to be executed in roughly 1.5 seconds.
// TODO: Get plugin otherwise !?

View File

@ -15,6 +15,7 @@
package fr.neatmonster.nocheatplus.components.registry;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.exception.RegistrationLockedException;
/**
* A registry for unique instances of any class type.<br>
@ -32,17 +33,20 @@ public interface GenericInstanceRegistry {
* be aligned to the actual class.
*
* @param instance
* @throws RegistrationLockedException
* If the registration of the class of instance is locked.
*/
public <T> T registerGenericInstance(T instance);
/**
* Register an instance under for a super-class.
*
* @todo The registry implementation might specify if overriding is allowed.
* @param registerAs
* @param instance
* @return The previously registered instance. If none was registered, null
* is returned.
* @throws RegistrationLockedException
* If the registration of registerFor is locked.
*/
public <T, TI extends T> T registerGenericInstance(Class<T> registerFor, TI instance);
@ -61,6 +65,8 @@ public interface GenericInstanceRegistry {
* @param registeredFor
* @return The previously registered instance. If none was registered, null
* is returned.
* @throws RegistrationLockedException
* If the registration of registerFor is locked.
*/
public <T> T unregisterGenericInstance(Class<T> registeredFor);

View File

@ -15,13 +15,14 @@
package fr.neatmonster.nocheatplus.components.registry.event;
/**
* Rather an internal interface.
* Allow to unregister listeners, should also disable internally created handles
* if they are this listener. Rather an internal interface.
*
* @author asofold
*
*/
public interface IUnregisterGenericInstanceListener {
public interface IUnregisterGenericInstanceRegistryListener {
public <T> void unregisterGenericInstanceListener(Class<T> registeredFor, IGenericInstanceHandle<T> listener);
public <T> void unregisterGenericInstanceRegistryListener(Class<T> registeredFor, IGenericInstanceRegistryListener<T> listener);
}

View File

@ -0,0 +1,37 @@
package fr.neatmonster.nocheatplus.components.registry.exception;
/**
* A registration item has been locked versus changes, but was attempted to be
* changed.
*
* @author asofold
*
*/
public class RegistrationLockedException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = -7278363049512687206L;
public RegistrationLockedException() {
super();
}
public RegistrationLockedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public RegistrationLockedException(String message, Throwable cause) {
super(message, cause);
}
public RegistrationLockedException(String message) {
super(message);
}
public RegistrationLockedException(Throwable cause) {
super(cause);
}
}

View File

@ -935,11 +935,15 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
}
// Register some generic stuff.
// Counters: debugging purposes, maybe integrated for statistics later.
// (Deny change some.)
registerGenericInstance(new Counters());
genericInstanceRegistry.denyChangeExistingRegistration(Counters.class);
registerGenericInstance(new WRPT());
registerGenericInstance(new Random(System.currentTimeMillis() ^ ((long) this.hashCode() * (long) listenerManager.hashCode() * (long) logManager.hashCode())));
genericInstanceRegistry.denyChangeExistingRegistration(WRPT.class);
registerGenericInstance(new TraceEntryPool(1000)); // Random number.
genericInstanceRegistry.denyChangeExistingRegistration(TraceEntryPool.class);
// (Allow override others.)
registerGenericInstance(new Random(System.currentTimeMillis() ^ ((long) this.hashCode() * (long) listenerManager.hashCode() * (long) logManager.hashCode())));
addComponent(new BridgeCrossPlugin());
// Initialize MCAccess.

View File

@ -14,37 +14,152 @@
*/
package fr.neatmonster.nocheatplus.components.registry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import fr.neatmonster.nocheatplus.components.registry.event.GenericInstanceHandle.ReferenceCountHandle;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceRegistryListener;
import fr.neatmonster.nocheatplus.components.registry.event.IUnregisterGenericInstanceListener;
import fr.neatmonster.nocheatplus.components.registry.event.IUnregisterGenericInstanceRegistryListener;
import fr.neatmonster.nocheatplus.components.registry.exception.RegistrationLockedException;
import fr.neatmonster.nocheatplus.logging.details.IGetStreamId;
import fr.neatmonster.nocheatplus.logging.details.ILogString;
public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry, IUnregisterGenericInstanceListener {
public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry, IUnregisterGenericInstanceRegistryListener {
// TODO: Test cases.
/** Storage for generic instances registration. */
private final Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
/**
* Hold registration information for a class, as well as some convenience
* functionality.
*
* @author asofold
*
*/
public static class Registration<T> {
/** Listeners for registry events. */
private final Map<Class<?>, Collection<IGenericInstanceRegistryListener<?>>> listeners = new HashMap<Class<?>, Collection<IGenericInstanceRegistryListener<?>>>();
private static final long DENY_OVERRIDE_INSTANCE = 0x01;
private static final long DENY_REMOVE_INSTANCE = 0x02;
/** Owned handles by the class they have been registered for. */
private final Map<Class<?>, IGenericInstanceHandle<?>> uniqueHandles = new LinkedHashMap<Class<?>, IGenericInstanceHandle<?>>();
// TODO: unique handles + use
/** Handles created within this class, that have to be detached. */
private final Set<IGenericInstanceHandle<?>> ownedHandles = new LinkedHashSet<IGenericInstanceHandle<?>>();
private final GenericInstanceRegistry registry;
private final IUnregisterGenericInstanceRegistryListener unregister;
private final Class<T> registeredFor;
private T instance = null;
/** Always kept registered, thus the reference count is ignored. */
private ReferenceCountHandle<T> uniqueHandle = null;
private long accessFlags = 0L;
private final List<IGenericInstanceRegistryListener<T>> listeners = new LinkedList<IGenericInstanceRegistryListener<T>>();
public Registration(Class<T> registeredFor, T instance,
GenericInstanceRegistry registry, IUnregisterGenericInstanceRegistryListener unregister) {
this.registry = registry;
this.unregister = unregister;
this.registeredFor = registeredFor;
this.instance = instance;
}
public void denyOverrideInstance() {
accessFlags |= DENY_OVERRIDE_INSTANCE;
}
public void denyRemoveInstance() {
accessFlags |= DENY_REMOVE_INSTANCE;
}
/**
* Call for unregistering this instance officially. Listeners and
* handles may be kept,
*
* @return The previously registered instance.
*/
public T unregisterInstance() {
if ((accessFlags & DENY_REMOVE_INSTANCE) != 0) {
throw new RegistrationLockedException();
}
T oldInstance = this.instance;
this.instance = null;
if (!listeners.isEmpty()) {
for (IGenericInstanceRegistryListener<T> listener : listeners) {
((IGenericInstanceRegistryListener<T>) listener).onGenericInstanceRemove(registeredFor, oldInstance);
}
}
return oldInstance;
}
/**
* Call on register.
*
* @param instance
* The previously registered instance.
* @return
*/
public T registerInstance(T instance) {
if ((accessFlags & DENY_OVERRIDE_INSTANCE) != 0) {
throw new RegistrationLockedException();
}
T oldInstance = this.instance;
this.instance = instance;
if (!listeners.isEmpty()) {
if (oldInstance == null) {
for (IGenericInstanceRegistryListener<T> listener : listeners) {
listener.onGenericInstanceOverride(registeredFor, instance, oldInstance);
}
}
else {
for (IGenericInstanceRegistryListener<T> listener : listeners) {
listener.onGenericInstanceRegister(registeredFor, instance);
}
}
}
return oldInstance;
}
public IGenericInstanceHandle<T> getHandle() {
if (uniqueHandle == null) {
uniqueHandle = new ReferenceCountHandle<T>(registeredFor, registry, unregister);
this.listeners.add(uniqueHandle);
}
return uniqueHandle.getNewHandle();
}
public void unregisterListener(IGenericInstanceRegistryListener<T> listener) {
IGenericInstanceHandle<T> disable = null;
if (listener == uniqueHandle) {
disable = uniqueHandle;
uniqueHandle = null;
}
this.listeners.remove(listener);
if (disable != null) {
disable.disableHandle();
}
}
public T getInstance() {
return (T) instance;
}
public boolean canBeRemoved() {
return instance == null && uniqueHandle == null && listeners.isEmpty();
}
public void clear() {
instance = null;
if (uniqueHandle != null) {
uniqueHandle.disableHandle();
uniqueHandle = null;
}
listeners.clear();
}
}
private final Map<Class<?>, Registration<?>> registrations = new LinkedHashMap<Class<?>, Registration<?>>();
private ILogString logger = null;
@ -58,19 +173,22 @@ public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry,
this.logPrefix = logPrefix;
}
@Override
public <T> void unregisterGenericInstanceListener(Class<T> registeredFor, IGenericInstanceHandle<T> listener) {
Collection<IGenericInstanceRegistryListener<?>> registered = listeners.get(registeredFor);
if (registered != null) {
registered.remove(listener);
if (registered.isEmpty()) {
listeners.remove(registeredFor);
}
@SuppressWarnings("unchecked")
private <T> Registration<T> getRegistration(Class<T> registeredFor, boolean create) {
Registration<T> registration = (Registration<T>) registrations.get(registeredFor);
if (registration == null && create) {
// Create empty.
registration = new Registration<T>(registeredFor, null, this, this);
this.registrations.put(registeredFor, registration);
}
if ((listener instanceof ReferenceCountHandle<?>) && ownedHandles.contains(listener)) {
ownedHandles.remove(listener);
uniqueHandles.remove(registeredFor);
((IGenericInstanceHandle<?>) listener).disableHandle();
return registration;
}
@Override
public <T> void unregisterGenericInstanceRegistryListener(Class<T> registeredFor, IGenericInstanceRegistryListener<T> listener) {
Registration<T> registration = getRegistration(registeredFor, false);
if (registration != null) {
registration.unregisterListener(listener);
}
}
@ -80,24 +198,11 @@ public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry,
return registerGenericInstance((Class<T>) instance.getClass(), instance);
}
@SuppressWarnings("unchecked")
@Override
public <T, TI extends T> T registerGenericInstance(Class<T> registerFor, TI instance) {
T registered = getGenericInstance(registerFor);
final boolean had = instances.containsKey(registerFor);
instances.put(registerFor, instance);
Collection<IGenericInstanceRegistryListener<?>> registeredListeners = listeners.get(registerFor);
if (registeredListeners != null) {
for (IGenericInstanceRegistryListener<?> rawListener : registeredListeners) {
if (had) {
((IGenericInstanceRegistryListener<T>) rawListener).onGenericInstanceOverride(registerFor, instance, registered);
}
else {
((IGenericInstanceRegistryListener<T>) rawListener).onGenericInstanceRegister(registerFor, instance);
}
}
}
if (had) {
Registration<T> registration = getRegistration(registerFor, true);
T registered = registration.registerInstance(instance);
if (registered != null) {
logRegistryEvent("Registered (override) for " + registerFor.getName() + ": " + instance.getClass().getName());
}
else {
@ -106,64 +211,56 @@ public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry,
return registered;
}
@SuppressWarnings("unchecked")
@Override
public <T> T getGenericInstance(Class<T> registeredFor) {
return (T) instances.get(registeredFor);
Registration<T> registration = getRegistration(registeredFor, false);
return registration == null ? null : registration.getInstance();
}
@SuppressWarnings("unchecked")
@Override
public <T> T unregisterGenericInstance(Class<T> registeredFor) {
T registered = getGenericInstance(registeredFor); // Convenience.
final boolean had = instances.containsKey(registeredFor);
instances.remove(registeredFor);
Collection<IGenericInstanceRegistryListener<?>> registeredListeners = listeners.get(registeredFor);
if (registeredListeners != null) {
for (IGenericInstanceRegistryListener<?> rawListener : registeredListeners) {
((IGenericInstanceRegistryListener<T>) rawListener).onGenericInstanceRemove(registeredFor, registered);
}
}
if (had) {
Registration<T> registration = getRegistration(registeredFor, false);
T registered = registration == null ? null : registration.unregisterInstance();
if (registered != null) {
logRegistryEvent("Unregister, remove mapping for: " + registeredFor.getName());
}
else {
logRegistryEvent("Unregister, no mapping present for: " + registeredFor.getName());
}
// Repeat getting for removal test.
if (registrations.containsKey(registeredFor) && getRegistration(registeredFor, false).canBeRemoved()) {
registrations.remove(registeredFor);
}
return registered;
}
@Override
public <T> IGenericInstanceHandle<T> getGenericInstanceHandle(Class<T> registeredFor) {
@SuppressWarnings("unchecked")
ReferenceCountHandle<T> handle = (ReferenceCountHandle<T>) uniqueHandles.get(registeredFor);
if (handle == null) {
handle = new ReferenceCountHandle<T>(registeredFor, this, this);
ownedHandles.add(handle);
uniqueHandles.put(registeredFor, handle);
Collection<IGenericInstanceRegistryListener<?>> registered = listeners.get(registeredFor);
if (registered == null) {
registered = new HashSet<IGenericInstanceRegistryListener<?>>();
listeners.put(registeredFor, registered);
}
registered.add((IGenericInstanceRegistryListener<?>) handle);
}
// else: no need to register.
return handle.getNewHandle();
return getRegistration(registeredFor, true).getHandle();
}
public void clear() {
instances.clear();
listeners.clear();
// TODO: consider fire unregister or add a removal method ?
// Force detach all handles.
for (IGenericInstanceHandle<?> handle : new ArrayList<IGenericInstanceHandle<?>>(ownedHandles)) {
handle.disableHandle();
for (final Registration<?> registration : registrations.values()) {
registration.clear();
}
ownedHandles.clear();
registrations.clear();
logRegistryEvent("Registry cleared.");
}
/**
* Convenience method to lock a registration vs. changing.
*
* @param registeredFor
*/
public void denyChangeExistingRegistration(Class<?> registeredFor) {
Registration<?> registration = this.getRegistration(registeredFor, false);
if (registration != null) {
registration.denyOverrideInstance();
registration.denyRemoveInstance();
}
}
protected void logRegistryEvent(String message) {
if (logger != null) {
logger.info(selectStream.getStreamId(), logPrefix == null ? message : logPrefix + message);

View File

@ -69,7 +69,8 @@ public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListene
/**
* Allow fetching PrarentDelegate instances, increasing reference count with
* each returned one. Really unregister only with reaching a count of zero
* on disableHandle. This way only one instance needs to be updated.
* on disableHandle. This way only one instance needs to be updated. This
* doesn't self-register as listener.
*
* @author asofold
*
@ -80,7 +81,7 @@ public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListene
private int references = 0;
public ReferenceCountHandle(Class<T> registeredFor, GenericInstanceRegistry registry,
IUnregisterGenericInstanceListener unregister) {
IUnregisterGenericInstanceRegistryListener unregister) {
super(registeredFor, registry, unregister);
}
@ -102,10 +103,14 @@ public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListene
return new ParentDelegateHandle<T>(getRegisteredFor(), getRegistry(), this);
}
public int getNumberOfReferences() {
return references;
}
}
private GenericInstanceRegistry registry;
private IUnregisterGenericInstanceListener unregister;
private IUnregisterGenericInstanceRegistryListener unregister;
private Class<T> registeredFor;
private T handle = null;
private boolean initialized = false;
@ -119,7 +124,7 @@ public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListene
* @param registry
* @param unregister
*/
public GenericInstanceHandle(Class<T> registeredFor, GenericInstanceRegistry registry, IUnregisterGenericInstanceListener unregister) {
public GenericInstanceHandle(Class<T> registeredFor, GenericInstanceRegistry registry, IUnregisterGenericInstanceRegistryListener unregister) {
this.registry = registry;
this.unregister = unregister;
this.registeredFor = registeredFor;
@ -169,7 +174,7 @@ public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListene
registeredFor = null;
registry = null;
if (unregister != null) {
unregister.unregisterGenericInstanceListener(registeredFor, this);
unregister.unregisterGenericInstanceRegistryListener(registeredFor, this);
}
unregister = null;
}
@ -183,7 +188,7 @@ public class GenericInstanceHandle<T> implements IGenericInstanceRegistryListene
return registry;
}
public IUnregisterGenericInstanceListener getUnregister() {
public IUnregisterGenericInstanceRegistryListener getUnregister() {
return unregister;
}