[BLEEDING][BREAKING][BROKEN] Continue data registry + API. (+)

Likely incomplete/broken somewhere.

Implement/extend/use/fix new data caches and factories.
(+) Fixes related to recent commits (e.g. log listener exceptions
properly, fight.wrongturn).

Missing:
* Debug logging (registry), consider a registry log file.
* Proper naming/tags for listeners.
* Consistency: ICheckData should probably be used with
removeData(CheckType)? Registration is arbitrary though.
* Consistency: clearData() vs clearData(CheckType.ALL) - should check
type related data be ICheckData only ?
* Data expiration stages and PlayerOfflineData - impact on memory...
* (...)

Further:
* WorldData inheritance issue: implement passing on changes to children.
(Current line of thought: rather extend IWorldDataManager to allow
change default+inherited only.)
* Shrink exposed API - uncertain: rather have a registration context
object or expose individual methods for factory registration and
grouping types?
* (...)
* Planned breakage: Project + package organization redone: move stuff
where it is best for having an API (components -> split to top level or
name it api, utilities ... parts belong into API, and the like...,
possibly split project further: commons, api(+-bukkit), core/checks,
plugin-bukkit).
This commit is contained in:
asofold 2018-03-02 09:47:05 +01:00
parent 350908cb47
commit 82d6f94230
75 changed files with 3507 additions and 681 deletions

View File

@ -14,6 +14,7 @@
*/
package fr.neatmonster.nocheatplus.utilities;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class Misc {
@ -36,4 +37,11 @@ public class Misc {
return res;
}
public static <T> void putFirst(T item, List<T> list) {
if (list.contains(item)) {
list.remove(item);
}
list.add(0, item);
}
}

View File

@ -705,6 +705,8 @@ public class HashMapLOW <K, V> {
* Get an iterator reflecting this 'stage of resetting'. During iteration,
* entries may get removed or added, values changed. Concurrent modification
* will not let the iteration fail.
* <hr>
* This operation does not use locking.
*
* @return
*/

View File

@ -0,0 +1,100 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.utilities.ds.map;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
/**
* Mapping Class<T> to <? extends T> for arbitrary T, aiming at simple generic
* caches.
* <hr/>
* Until as specialized implementation is present, see {@link HashMapLOW} for
* reference.
*
* @author asofold
*
*/
public class InstanceMapLOW {
/*
* TODO: Optimized implementation possible (performance/JIT)? Buckets and
* entries could support the type relation.
*/
private final HashMapLOW<Class<?>, Object> map;
public InstanceMapLOW(final Lock lock, int initialSize) {
map = new HashMapLOW<Class<?>, Object>(lock, 12);
}
@SuppressWarnings("unchecked")
public <T, I extends T> T put(final Class<T> key, final I value) {
return (T) map.put(key, value);
}
@SuppressWarnings("unchecked")
public <T, I extends T> T putIfAbsent(final Class<T> key, final I value) {
return (T) map.putIfAbsent(key, value);
}
@SuppressWarnings("unchecked")
public <T> T get(Class<T> key) {
return (T) map.get(key);
}
@SuppressWarnings("unchecked")
public <T> T getLocked(Class<T> key) {
return (T) map.getLocked(key);
}
public boolean containsKey(final Class<?> key) {
return map.containsKey(key);
}
public boolean containsKeyLocked(final Class<?> key) {
return map.containsKeyLocked(key);
}
public Collection<Class<?>> getKeys() {
return map.getKeys();
}
@SuppressWarnings("unchecked")
public <T> T remove(final Class<T> key) {
return (T) map.remove(key);
}
/**
* (Note that all contained keys should be different classes.)
*
* @param keys
*/
public void remove(final Collection<Class<?>> keys) {
map.remove(keys);
}
public boolean isEmpty() {
return map.isEmpty();
}
public int size() {
return map.size();
}
public void clear() {
map.clear();
}
}

View File

@ -65,7 +65,8 @@ public class FastConsume extends Check implements Listener, INotifyReload {
// TODO: Do this kind of thing via registries later on.
//ConfigManager.setForAllConfigs(ConfPaths.INVENTORY_INSTANTEAT_CHECK, false);
NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager().overrideCheckActivation(
this.type, AlmostBoolean.NO, OverrideType.PERMANENT, true);
CheckType.INVENTORY_INSTANTEAT, AlmostBoolean.NO,
OverrideType.PERMANENT, true);
StaticLog.logInfo("Inventory checks: FastConsume is available, disabled InstantEat.");
}

View File

@ -201,7 +201,7 @@ public enum CheckType {
}
private String guessConfigPathRoot() {
return name().toLowerCase().replace('_', '.') + ".";
return "checks." + name().toLowerCase().replace('_', '.') + ".";
}
public CheckTypeType getType() {

View File

@ -14,6 +14,7 @@
*/
package fr.neatmonster.nocheatplus.checks.access;
import fr.neatmonster.nocheatplus.components.config.ICheckConfig;
import fr.neatmonster.nocheatplus.worlds.IWorldData;
/**

View File

@ -14,6 +14,8 @@
*/
package fr.neatmonster.nocheatplus.checks.access;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
/**
* Abstract implementation to do nothing.
*

View File

@ -24,6 +24,8 @@ import fr.neatmonster.nocheatplus.actions.ParameterHolder;
*/
public interface IViolationInfo extends ParameterHolder {
// TODO: Move to components or to the appropriate API location (next iteration(s)).
/**
* Get the violation level just added by this violation.
*

View File

@ -19,6 +19,8 @@ import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
import fr.neatmonster.nocheatplus.checks.access.ACheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnReload;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstance;
import fr.neatmonster.nocheatplus.stats.Timings;
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
@ -26,7 +28,7 @@ import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
/**
* Player specific data for the block break checks.
*/
public class BlockBreakData extends ACheckData {
public class BlockBreakData extends ACheckData implements IDataOnReload {
// Violation levels.
public double directionVL;
@ -118,4 +120,10 @@ public class BlockBreakData extends ACheckData {
}
}
@Override
public boolean dataOnReload(IGetGenericInstance dataAccess) {
// Remove on reload for now.
return true;
}
}

View File

@ -41,13 +41,19 @@ import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the block break checks.
@ -82,8 +88,36 @@ public class BlockBreakListener extends CheckListener {
/** For temporary use: LocUtil.clone before passing deeply, call setWorld(null) after use. */
private final Location useLoc = new Location(null, 0, 0, 0);
@SuppressWarnings("unchecked")
public BlockBreakListener(){
super(CheckType.BLOCKBREAK);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
// Register config and data.
api.register(api.newRegistrationContext()
// BlockBreakConfig
.registerConfigWorld(BlockBreakConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, BlockBreakConfig>() {
@Override
public BlockBreakConfig getNewInstance(WorldFactoryArgument arg) {
return new BlockBreakConfig(arg.worldData);
}
})
.registerConfigTypesPlayer(CheckType.BLOCKBREAK, true)
.context() //
// BlockBreakData
.registerDataPlayer(BlockBreakData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, BlockBreakData>() {
@Override
public BlockBreakData getNewInstance(
PlayerFactoryArgument arg) {
return new BlockBreakData(
arg.playerData.getGenericInstance(BlockBreakConfig.class));
}
})
// (Complete data removal for now.)
.addToGroups(CheckType.BLOCKBREAK, true, IData.class, ICheckData.class)
.context() //
);
}
/**

View File

@ -37,14 +37,20 @@ import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.InventoryUtil;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the block interact checks.
@ -99,8 +105,33 @@ public class BlockInteractListener extends CheckListener {
private final int idInteractLookFlyingFirst = counters.registerKey("block.interact.look.flying.first");
private final int idInteractLookFlyingOther = counters.registerKey("block.interact.look.flying.other");
@SuppressWarnings("unchecked")
public BlockInteractListener() {
super(CheckType.BLOCKINTERACT);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext() //
// BlockInteractConfig
.registerConfigWorld(BlockInteractConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, BlockInteractConfig>() {
@Override
public BlockInteractConfig getNewInstance(WorldFactoryArgument arg) {
return new BlockInteractConfig(arg.worldData);
}
})
.registerConfigTypesPlayer(CheckType.BLOCKINTERACT, true)
.context() //
// BlockinteractData
.registerDataPlayer(BlockInteractData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, BlockInteractData>() {
@Override
public BlockInteractData getNewInstance(
PlayerFactoryArgument arg) {
return new BlockInteractData();
}
})
.addToGroups(CheckType.BLOCKINTERACT, true, IData.class, ICheckData.class)
.context() //
);
}
/**

View File

@ -47,14 +47,20 @@ import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
import fr.neatmonster.nocheatplus.checks.net.model.DataPacketFlying;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.InventoryUtil;
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the block place checks.
@ -115,8 +121,33 @@ public class BlockPlaceListener extends CheckListener {
private final Class<?> blockMultiPlaceEvent = ReflectionUtil.getClass("org.bukkit.event.block.BlockMultiPlaceEvent");
private final boolean hasGetReplacedState = ReflectionUtil.getMethodNoArgs(BlockPlaceEvent.class, "getReplacedState", BlockState.class) != null;
@SuppressWarnings("unchecked")
public BlockPlaceListener() {
super(CheckType.BLOCKPLACE);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext()
// BlockPlaceConfig
.registerConfigWorld(BlockPlaceConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, BlockPlaceConfig>() {
@Override
public BlockPlaceConfig getNewInstance(WorldFactoryArgument arg) {
return new BlockPlaceConfig(arg.worldData);
}
})
.registerConfigTypesPlayer()
.context() //
// BlockPlaceData
.registerDataPlayer(BlockPlaceData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, BlockPlaceData>() {
@Override
public BlockPlaceData getNewInstance(
PlayerFactoryArgument arg) {
return new BlockPlaceData();
}
})
.addToGroups(CheckType.BLOCKPLACE, true, IData.class, ICheckData.class)
.context() //
);
}
/**

View File

@ -34,6 +34,10 @@ import fr.neatmonster.nocheatplus.checks.moving.MovingConfig;
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
import fr.neatmonster.nocheatplus.command.CommandUtil;
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.feature.INotifyReload;
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
import fr.neatmonster.nocheatplus.config.ConfPaths;
@ -42,8 +46,10 @@ import fr.neatmonster.nocheatplus.config.ConfigManager;
import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
import fr.neatmonster.nocheatplus.utilities.ds.prefixtree.SimpleCharPrefixTree;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the chat checks.
@ -86,11 +92,33 @@ public class ChatListener extends CheckListener implements INotifyReload, JoinLe
/** Set world to null after use, primary thread only. */
private final Location useLoc = new Location(null, 0, 0, 0);
@SuppressWarnings("unchecked")
public ChatListener() {
super(CheckType.CHAT);
ConfigFile config = ConfigManager.getConfigFile();
initFilters(config);
// (text inits in constructor.)
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext()
.registerConfigWorld(ChatConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, ChatConfig>() {
@Override
public ChatConfig getNewInstance(WorldFactoryArgument arg) {
return new ChatConfig(arg.worldData);
}
})
.registerConfigTypesPlayer()
.context() //
.registerDataPlayer(ChatData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, ChatData>() {
@Override
public ChatData getNewInstance(PlayerFactoryArgument arg) {
return new ChatData();
}
})
.addToGroups(CheckType.CHAT, true, IData.class, ICheckData.class)
.context() //
);
}
private void initFilters(ConfigFile config) {

View File

@ -14,13 +14,15 @@
*/
package fr.neatmonster.nocheatplus.checks.combined;
import java.util.Collection;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.ACheckData;
import fr.neatmonster.nocheatplus.checks.access.IRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.utilities.PenaltyTime;
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
public class CombinedData extends ACheckData implements IRemoveSubCheckData {
public class CombinedData extends ACheckData implements IDataOnRemoveSubCheckData {
// VLs
public double bedLeaveVL = 0;
@ -54,27 +56,32 @@ public class CombinedData extends ACheckData implements IRemoveSubCheckData {
public long lastMoveTime;
@Override
public boolean removeSubCheckData(final CheckType checkType) {
switch(checkType) {
// TODO: case COMBINED:
case COMBINED_IMPROBABLE:
improbableVL = 0;
improbableCount.clear(System.currentTimeMillis()); // TODO: Document there, which to use.
return true;
case COMBINED_YAWRATE:
yawFreq.clear(System.currentTimeMillis()); // TODO: Document there, which to use.
return true;
case COMBINED_BEDLEAVE:
bedLeaveVL = 0;
wasInBed = false; // wasInBed is probably better kept?
return true;
case COMBINED_MUNCHHAUSEN:
munchHausenVL = 0;
return true;
default:
return false;
public boolean dataOnRemoveSubCheckData(Collection<CheckType> checkTypes) {
for (final CheckType checkType : checkTypes)
{
switch(checkType) {
// TODO: case COMBINED:
case COMBINED_IMPROBABLE:
improbableVL = 0;
improbableCount.clear(System.currentTimeMillis()); // TODO: Document there, which to use.
break;
case COMBINED_YAWRATE:
yawFreq.clear(System.currentTimeMillis()); // TODO: Document there, which to use.
break;
case COMBINED_BEDLEAVE:
bedLeaveVL = 0;
wasInBed = false; // wasInBed is probably better kept?
break;
case COMBINED_MUNCHHAUSEN:
munchHausenVL = 0;
break;
case COMBINED:
return true;
default:
break;
}
}
return false;
}
}

View File

@ -28,10 +28,16 @@ import org.bukkit.event.player.PlayerToggleSprintEvent;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckListener;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Class to combine some things, make available for other checks, or just because they don't fit into another section.<br>
@ -50,8 +56,34 @@ public class CombinedListener extends CheckListener {
private final Counters counters = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstance(Counters.class);
private final int idFakeInvulnerable = counters.registerKey("fakeinvulnerable");
@SuppressWarnings("unchecked")
public CombinedListener(){
super(CheckType.COMBINED);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext()
// CombinedConfig
.registerConfigWorld(CombinedConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, CombinedConfig>() {
@Override
public CombinedConfig getNewInstance(WorldFactoryArgument arg) {
return new CombinedConfig(arg.worldData);
}
})
.registerConfigTypesPlayer()
.context() //
// CombinedData
.registerDataPlayer(CombinedData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, CombinedData>() {
@Override
public CombinedData getNewInstance(
PlayerFactoryArgument arg) {
return new CombinedData();
}
})
.addToGroups(CheckType.MOVING, false, IData.class, ICheckData.class)
.removeSubCheckData(CheckType.COMBINED, true)
.context() //
);
}
/**

View File

@ -14,18 +14,24 @@
*/
package fr.neatmonster.nocheatplus.checks.fight;
import java.util.Collection;
import java.util.LinkedList;
import org.bukkit.World;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.ACheckData;
import fr.neatmonster.nocheatplus.checks.access.IRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldChange;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.utilities.PenaltyTime;
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
/**
* Player specific data for the fight checks.
*/
public class FightData extends ACheckData implements IRemoveSubCheckData {
public class FightData extends ACheckData implements IDataOnRemoveSubCheckData, IDataOnWorldChange {
// Violation levels.
public double angleVL;
@ -115,61 +121,70 @@ public class FightData extends ACheckData implements IRemoveSubCheckData {
}
@Override
public boolean removeSubCheckData(final CheckType checkType) {
switch(checkType) {
// TODO: case FIGHT: ...
case FIGHT_DIRECTION:
directionVL = 0;
return true;
case FIGHT_REACH:
reachVL = 0;
reachMod = 1.0;
return true;
case FIGHT_ANGLE:
angleVL = 0;
angleHits.clear();
return true;
case FIGHT_SPEED:
speedVL = 0;
speedBuckets.clear(System.currentTimeMillis());
speedShortTermCount = 0;
speedShortTermTick = 0;
return true;
case FIGHT_FASTHEAL:
fastHealVL = 0;
fastHealRefTime = 0;
fastHealBuffer = 0;
regainHealthTime = 0;
return true;
case FIGHT_GODMODE:
godModeVL = 0;
godModeBuffer = 0;
godModeAcc = 0;
godModeLastTime = 0;
godModeLastAge = 0;
lastNoDamageTicks = 0; // Not sure here, possibly a shared thing.
// godModeHealth / ...
return true;
case FIGHT_CRITICAL:
criticalVL = 0;
return true;
case FIGHT_NOSWING:
noSwingVL = 0;
// Not reset time, for leniency rather.
return true;
case FIGHT_SELFHIT:
selfHitVL.clear(System.currentTimeMillis());
return true;
default:
return false;
public boolean dataOnRemoveSubCheckData(
final Collection<CheckType> checkTypes) {
for (final CheckType checkType : checkTypes) {
switch(checkType) {
// TODO: case FIGHT: ...
case FIGHT_DIRECTION:
directionVL = 0;
break;
case FIGHT_REACH:
reachVL = 0;
reachMod = 1.0;
break;
case FIGHT_ANGLE:
angleVL = 0;
angleHits.clear();
break;
case FIGHT_SPEED:
speedVL = 0;
speedBuckets.clear(System.currentTimeMillis());
speedShortTermCount = 0;
speedShortTermTick = 0;
break;
case FIGHT_FASTHEAL:
fastHealVL = 0;
fastHealRefTime = 0;
fastHealBuffer = 0;
regainHealthTime = 0;
break;
case FIGHT_GODMODE:
godModeVL = 0;
godModeBuffer = 0;
godModeAcc = 0;
godModeLastTime = 0;
godModeLastAge = 0;
lastNoDamageTicks = 0; // Not sure here, possibly a shared thing.
// godModeHealth / ...
break;
case FIGHT_CRITICAL:
criticalVL = 0;
break;
case FIGHT_NOSWING:
noSwingVL = 0;
// Not reset time, for leniency rather.
break;
case FIGHT_SELFHIT:
selfHitVL.clear(System.currentTimeMillis());
break;
case FIGHT:
return true;
default:
break;
}
}
return false;
}
public void onWorldChange() {
@Override
public boolean dataOnWorldChange(Player player, IPlayerData pData,
World previousWorld, World newWorld) {
angleHits.clear();
lastAttackedX = Double.MAX_VALUE;
lastAttackTick = 0;
lastWorld = "";
return false;
}
}

View File

@ -30,7 +30,6 @@ import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.player.PlayerAnimationEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
@ -55,17 +54,23 @@ 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.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.build.BuildParameters;
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
import fr.neatmonster.nocheatplus.utilities.location.TrigUtil;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the fight checks.<br>
@ -119,8 +124,33 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// Assume it to stay the same all time.
private final IGenericInstanceHandle<IBridgeCrossPlugin> crossPlugin = NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IBridgeCrossPlugin.class);
@SuppressWarnings("unchecked")
public FightListener() {
super(CheckType.FIGHT);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext()
// FightConfig
.registerConfigWorld(FightConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, FightConfig>() {
@Override
public FightConfig getNewInstance(WorldFactoryArgument arg) {
return new FightConfig(arg.worldData);
}
})
.registerConfigTypesPlayer()
.context() //
// FightData
.registerDataPlayer(FightData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, FightData>() {
@Override
public FightData getNewInstance(PlayerFactoryArgument arg) {
return new FightData(arg.playerData.getGenericInstance(FightConfig.class));
}
})
.addToGroups(CheckType.FIGHT, false, IData.class, ICheckData.class)
.removeSubCheckData(CheckType.FIGHT, true)
.context() //
);
}
/**
@ -281,8 +311,8 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
// TODO: Add as real check.
// TODO: Add something on packet level already.
if (pData.isCheckActive(CheckType.FIGHT_WRONGTURN, damagedPlayer)
&& wrongTurn.check(damagedPlayer, loc, data, cc)) {
if (pData.isCheckActive(CheckType.FIGHT_WRONGTURN, player)
&& wrongTurn.check(player, loc, data, cc)) {
cancelled = true;
}
@ -830,12 +860,6 @@ public class FightListener extends CheckListener implements JoinLeaveListener{
data.angleHits.clear();
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerChangedWorld(final PlayerChangedWorldEvent event) {
DataManager.getGenericInstance(event.getPlayer(),
FightData.class).onWorldChange();
}
@EventHandler(ignoreCancelled = false, priority = EventPriority.MONITOR)
public void onItemHeld(final PlayerItemHeldEvent event) {
final Player player = event.getPlayer();

View File

@ -22,10 +22,10 @@ import org.bukkit.util.Vector;
import fr.neatmonster.nocheatplus.actions.ActionList;
import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.ICheckConfig;
import fr.neatmonster.nocheatplus.checks.access.ICheckData;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueHandle;
import fr.neatmonster.nocheatplus.checks.net.FlyingQueueLookBlockChecker;
import fr.neatmonster.nocheatplus.components.config.ICheckConfig;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.utilities.collision.CollideRayVsAABB;
import fr.neatmonster.nocheatplus.utilities.collision.ICollideRayVsAABB;

View File

@ -24,7 +24,7 @@ import fr.neatmonster.nocheatplus.checks.access.ACheckConfig;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.worlds.WorldData;
import fr.neatmonster.nocheatplus.worlds.IWorldData;
/**
* Configurations specific for the "inventory" checks. Every world gets one of
@ -70,7 +70,7 @@ public class InventoryConfig extends ACheckConfig {
* @param data
* the data
*/
public InventoryConfig(final WorldData worldData) {
public InventoryConfig(final IWorldData worldData) {
super(worldData);
final ConfigFile data = worldData.getRawConfiguration();
dropLimit = data.getInt(ConfPaths.INVENTORY_DROP_LIMIT);

View File

@ -52,13 +52,19 @@ import fr.neatmonster.nocheatplus.checks.combined.Improbable;
import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
import fr.neatmonster.nocheatplus.compat.BridgeHealth;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.entity.IEntityAccessVehicle;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.feature.JoinLeaveListener;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.InventoryUtil;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the inventory checks.
@ -94,8 +100,34 @@ public class InventoryListener extends CheckListener implements JoinLeaveListen
private final IGenericInstanceHandle<IEntityAccessVehicle> handleVehicles =
NCPAPIProvider.getNoCheatPlusAPI().getGenericInstanceHandle(IEntityAccessVehicle.class);
@SuppressWarnings("unchecked")
public InventoryListener() {
super(CheckType.INVENTORY);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext()
// InventoryConfig
.registerConfigWorld(InventoryConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, InventoryConfig>() {
@Override
public InventoryConfig getNewInstance(
WorldFactoryArgument arg) {
return new InventoryConfig(arg.worldData);
}
})
.registerConfigTypesPlayer()
.context() //
// InventoryData
.registerDataPlayer(InventoryData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, InventoryData>() {
@Override
public InventoryData getNewInstance(
PlayerFactoryArgument arg) {
return new InventoryData();
}
})
.addToGroups(CheckType.INVENTORY, true, IData.class, ICheckData.class)
.context() //
);
}
/**

View File

@ -18,13 +18,13 @@ import java.util.Collection;
import java.util.concurrent.Callable;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.ACheckData;
import fr.neatmonster.nocheatplus.checks.access.IRemoveSubCheckData;
import fr.neatmonster.nocheatplus.checks.moving.location.setback.DefaultSetBackStorage;
import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace;
import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace.TraceEntryPool;
@ -41,9 +41,13 @@ import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry;
import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeReference;
import fr.neatmonster.nocheatplus.components.data.IDataOnReload;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload;
import fr.neatmonster.nocheatplus.components.entity.IEntityAccessDimensions;
import fr.neatmonster.nocheatplus.components.location.IGetPosition;
import fr.neatmonster.nocheatplus.components.location.IPositionWithLook;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstance;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.TickTask;
@ -58,33 +62,7 @@ import fr.neatmonster.nocheatplus.workaround.IWorkaroundRegistry.WorkaroundSet;
/**
* Player specific data for the moving checks.
*/
public class MovingData extends ACheckData implements IRemoveSubCheckData {
/*
* TODO: Handle world unload and other by registration (PlayerDataManager) -
* might just implement the interfaces and auto-handle at registration.
*/
// /**
// * Clear data related to the given world.
// * @param world The world that gets unloaded.
// */
// public static void onWorldUnload(final World world) {
// // TODO: Register with check (interfaces or just an event listener).
// final String worldName = world.getName();
// for (final MovingData data : playersMap.values()) {
// data.onWorldUnload(worldName);
// }
// }
//
// public static void onReload() {
// // TODO: Register with check (interfaces or just an event listener).
// final MovingConfig globalCc = MovingConfig.getConfig((String) null);
// final int tick = TickTask.getTick();
// for (final MovingData data : playersMap.values()) {
// data.adjustOnReload(globalCc, tick);
// }
// }
public class MovingData extends ACheckData implements IDataOnRemoveSubCheckData, IDataOnReload, IDataOnWorldUnload {
// Check specific.
@ -324,59 +302,6 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
}
@Override
public boolean removeSubCheckData(final CheckType checkType) {
// TODO: LocationTrace stays (leniency for other players!).
// TODO: Likely more fields left to change.
switch (checkType) {
/*
* TODO: case MOVING: // Remove all in-place (future: data might
* stay as long as the player is online).
*/
case MOVING_SURVIVALFLY:
survivalFlyVL = 0;
clearFlyData();
resetSetBack(); // TODO: Not sure this is really best for compatibility.
// TODO: other?
return true;
case MOVING_CREATIVEFLY:
creativeFlyVL = 0;
clearFlyData();
resetSetBack(); // TODO: Not sure this is really best for compatibility.
// TODO: other?
return true;
case MOVING_NOFALL:
noFallVL = 0;
clearNoFallData();
return true;
case MOVING_MOREPACKETS:
morePacketsVL = 0;
clearPlayerMorePacketsData();
morePacketsSetback = null;
morePacketsSetBackResetTime = 0;
return true;
case MOVING_PASSABLE:
passableVL = 0;
return true;
case MOVING_VEHICLE:
vehicleEnvelopeVL = 0;
vehicleMorePacketsVL = 0;
clearVehicleData();
return true;
case MOVING_VEHICLE_ENVELOPE:
vehicleEnvelopeVL = 0;
vehicleMoves.invalidate();
vehicleSetBacks.invalidateAll(); // Also invalidates morepackets set back.
return true;
case MOVING_VEHICLE_MOREPACKETS:
vehicleMorePacketsVL = 0;
clearVehicleMorePacketsData();
return true;
default:
return false;
}
}
/**
* Clear fly and more packets check data for both vehicles and players.
*/
@ -522,26 +447,6 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
vehicleMoves.invalidate();
}
/**
* Clean up data related to worlds with the given name (not case-sensitive).
* @param worldName
*/
public void onWorldUnload(final String worldName) {
// TODO: Unlink world references.
if (teleported != null && worldName.equalsIgnoreCase(teleported.getWorld().getName())) {
resetTeleported();
}
if (setBack != null && worldName.equalsIgnoreCase(setBack.getWorld().getName())) {
clearFlyData();
}
if (morePacketsSetback != null && worldName.equalsIgnoreCase(morePacketsSetback.getWorld().getName())) {
clearPlayerMorePacketsData();
clearNoFallData(); // just in case.
}
// (Assume vehicle data needn't really reset here.)
vehicleSetBacks.resetByWorldName(worldName);
}
/**
* Invalidate all past player moves data and set last position if not null.
*
@ -1243,13 +1148,6 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
iead.getWidth(player) / 2.0, Math.max(player.getEyeHeight(), iead.getHeight(player)));
}
/**
* Adjust to new parameters.
*/
protected void adjustOnReload(final MovingConfig cc, final int tick) {
trace.adjustSettings(cc.traceMaxAge, cc.traceMaxSize, tick);
}
/**
* Test if velocity has affected the in-air jumping phase. Keeps set until
* reset on-ground or otherwise. Use clearActiveVerVel to force end velocity
@ -1382,4 +1280,91 @@ public class MovingData extends ACheckData implements IRemoveSubCheckData {
verVel.removeLeadingQueuedVerticalVelocityByFlag(flag);
}
@Override
public boolean dataOnRemoveSubCheckData(Collection<CheckType> checkTypes) {
// TODO: Detect if it is ok to remove data.
// TODO: LocationTrace stays (leniency for other players!).
// TODO: Likely more fields left to change.
for (final CheckType checkType : checkTypes) {
switch (checkType) {
/*
* TODO: case MOVING: // Remove all in-place (future: data might
* stay as long as the player is online).
*/
case MOVING_SURVIVALFLY:
survivalFlyVL = 0;
clearFlyData(); // TODO: ...
resetSetBack(); // TODO: Not sure this is really best for compatibility.
// TODO: other?
break;
case MOVING_CREATIVEFLY:
creativeFlyVL = 0;
clearFlyData(); // TODO: ...
resetSetBack(); // TODO: Not sure this is really best for compatibility.
// TODO: other?
break;
case MOVING_NOFALL:
noFallVL = 0;
clearNoFallData();
break;
case MOVING_MOREPACKETS:
morePacketsVL = 0;
clearPlayerMorePacketsData();
morePacketsSetback = null;
morePacketsSetBackResetTime = 0;
break;
case MOVING_PASSABLE:
passableVL = 0;
break;
case MOVING_VEHICLE:
vehicleEnvelopeVL = 0;
vehicleMorePacketsVL = 0;
clearVehicleData();
break;
case MOVING_VEHICLE_ENVELOPE:
vehicleEnvelopeVL = 0;
vehicleMoves.invalidate();
vehicleSetBacks.invalidateAll(); // Also invalidates morepackets set back.
break;
case MOVING_VEHICLE_MOREPACKETS:
vehicleMorePacketsVL = 0;
clearVehicleMorePacketsData();
break;
case MOVING:
clearMostMovingCheckData(); // Just in case.
return true;
default:
break;
}
}
return false;
}
@Override
public boolean dataOnWorldUnload(final World world,
final IGetGenericInstance dataAccess) {
// TODO: Unlink world references.
final String worldName = world.getName();
if (teleported != null && worldName.equalsIgnoreCase(teleported.getWorld().getName())) {
resetTeleported();
}
if (setBack != null && worldName.equalsIgnoreCase(setBack.getWorld().getName())) {
clearFlyData();
}
if (morePacketsSetback != null && worldName.equalsIgnoreCase(morePacketsSetback.getWorld().getName())) {
clearPlayerMorePacketsData();
clearNoFallData(); // just in case.
}
// (Assume vehicle data needn't really reset here.)
vehicleSetBacks.resetByWorldName(worldName);
return false;
}
@Override
public boolean dataOnReload(final IGetGenericInstance dataAccess) {
final MovingConfig cc = dataAccess.getGenericInstance(MovingConfig.class);
trace.adjustSettings(cc.traceMaxAge, cc.traceMaxSize, TickTask.getTick());
return false;
}
}

View File

@ -48,7 +48,6 @@ import org.bukkit.event.player.PlayerToggleFlightEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.event.player.PlayerToggleSprintEvent;
import org.bukkit.event.player.PlayerVelocityEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.potion.PotionEffectType;
@ -90,10 +89,13 @@ import fr.neatmonster.nocheatplus.compat.MCAccess;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.BlockChangeEntry;
import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker.Direction;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.location.SimplePositionWithLook;
import fr.neatmonster.nocheatplus.components.modifier.IAttributeAccess;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.feature.IHaveCheckType;
import fr.neatmonster.nocheatplus.components.registry.feature.INeedConfig;
import fr.neatmonster.nocheatplus.components.registry.feature.IRemoveData;
@ -106,6 +108,7 @@ import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.logging.debug.DebugUtil;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
import fr.neatmonster.nocheatplus.utilities.PotionUtil;
@ -115,9 +118,9 @@ import fr.neatmonster.nocheatplus.utilities.build.BuildParameters;
import fr.neatmonster.nocheatplus.utilities.location.LocUtil;
import fr.neatmonster.nocheatplus.utilities.location.PlayerLocation;
import fr.neatmonster.nocheatplus.utilities.location.TrigUtil;
import fr.neatmonster.nocheatplus.utilities.map.BlockCache;
import fr.neatmonster.nocheatplus.utilities.map.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.map.MapUtil;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Central location to listen to events that are relevant for the moving checks.
@ -204,10 +207,12 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
private final int idMoveEvent = counters.registerKey("event.player.move");
@SuppressWarnings("unchecked")
public MovingListener() {
super(CheckType.MOVING);
// Register vehicleChecks.
NCPAPIProvider.getNoCheatPlusAPI().addComponent(vehicleChecks);
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.addComponent(vehicleChecks);
blockChangeTracker = NCPAPIProvider.getNoCheatPlusAPI().getBlockChangeTracker();
if (Bridge1_9.hasEntityToggleGlideEvent()) {
queuedComponents.add(new Listener() {
@ -219,6 +224,35 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
}
});
}
// Register config and data.
// TODO: Should register before creating Check instances ?
api.register(api.newRegistrationContext()
// MovingConfig
.registerConfigWorld(MovingConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, MovingConfig>() {
@Override
public MovingConfig getNewInstance(
final WorldFactoryArgument arg) {
return new MovingConfig(arg.worldData);
}
})
.registerConfigTypesPlayer(CheckType.MOVING, true)
.context() //
// MovingData
.registerDataPlayer(MovingData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, MovingData>() {
@Override
public MovingData getNewInstance(
final PlayerFactoryArgument arg) {
return new MovingData(arg.worldData.getGenericInstance(
MovingConfig.class), arg.playerData);
}
})
.addToGroups(CheckType.MOVING, false, IData.class, ICheckData.class)
.removeSubCheckData(CheckType.MOVING, true)
.context() //
);
}
/**
@ -2463,12 +2497,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldunload(final WorldUnloadEvent event) {
// TODO: Consider removing the world-related data anyway (even if the event is cancelled).
MovingData.onWorldUnload(event.getWorld());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerToggleSneak(final PlayerToggleSneakEvent event) {
survivalFly.setReallySneaking(event.getPlayer(), event.isSneaking());
@ -2696,6 +2724,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
@Override
public CheckType getCheckType() {
// TODO: this is for the hover check only...
// TODO: ugly.
return CheckType.MOVING_SURVIVALFLY;
}
@ -2717,7 +2746,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
public void onReload() {
aux.clear();
hoverTicksStep = Math.max(1, ConfigManager.getConfigFile().getInt(ConfPaths.MOVING_SURVIVALFLY_HOVER_STEP));
MovingData.onReload();
}
/**

View File

@ -16,8 +16,16 @@ package fr.neatmonster.nocheatplus.checks.net;
import java.util.List;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Static method utility for networking related stuff.
@ -163,4 +171,31 @@ public class NetStatic {
return Math.max(0.0, violation);
}
@SuppressWarnings("unchecked")
public static void registerTypes() {
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
api.register(api.newRegistrationContext()
// NetConfig
.registerConfigWorld(NetConfig.class)
.factory(new IFactoryOne<WorldFactoryArgument, NetConfig>() {
@Override
public NetConfig getNewInstance(WorldFactoryArgument arg) {
return new NetConfig(arg.worldData);
}
})
.registerConfigTypesPlayer()
.context() //
// NetData
.registerDataPlayer(NetData.class)
.factory(new IFactoryOne<PlayerFactoryArgument, NetData>() {
@Override
public NetData getNewInstance(PlayerFactoryArgument arg) {
return new NetData(arg.playerData.getGenericInstance(NetConfig.class));
}
})
.addToGroups(CheckType.NET, true, IData.class, ICheckData.class)
.context() //
);
}
}

View File

@ -23,7 +23,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.command.BaseCommand;
import fr.neatmonster.nocheatplus.command.NoCheatPlusCommand.NCPReloadEvent;
import fr.neatmonster.nocheatplus.components.registry.feature.INotifyReload;
@ -71,22 +70,14 @@ public class ReloadCommand extends BaseCommand {
// Do the actual reload.
ConfigManager.cleanup();
// (Magic/TODO)
ConfigManager.init(access,
(WorldDataManager) NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager());
final WorldDataManager worldDataManager = (WorldDataManager) NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager();
ConfigManager.init(access, worldDataManager);
if (logManager instanceof INotifyReload) { // TODO: This is a band-aid.
((INotifyReload) logManager).onReload();
}
// Remove all cached configs.
DataManager.clearConfigs(); // There you have to add XConfig.clear() form now on.
// Remove some checks data.
// TODO: Better concept (INotifyReload).
for (final CheckType checkType : new CheckType[]{
CheckType.BLOCKBREAK, CheckType.FIGHT,
}){
DataManager.clearData(checkType);
}
// Remove all cached configs from data.
NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager().removeCachedConfigs();
// Reset debug flags to default (temp, heavy).
DataManager.restoreDefaultDebugFlags();

View File

@ -66,28 +66,28 @@ public class RemovePlayerCommand extends BaseCommand {
if (player != null) playerName = player.getName();
ViolationHistory hist = ViolationHistory.getHistory(playerName, false);
boolean histRemoved = false;
boolean somethingFound = false;
if (hist != null){
histRemoved = hist.remove(checkType);
somethingFound = hist.remove(checkType);
if (checkType == CheckType.ALL){
histRemoved = true;
somethingFound = true;
ViolationHistory.removeHistory(playerName);
}
}
if (DataManager.removeExecutionHistory(checkType, playerName)) histRemoved = true;
if (DataManager.removeExecutionHistory(checkType, playerName)) {
somethingFound = true;
}
final boolean dataRemoved = DataManager.removeData(playerName, checkType);
if (DataManager.removeData(playerName, checkType)) {
somethingFound = true;
}
if (dataRemoved || histRemoved){
String which;
if (dataRemoved && histRemoved) which = "data and history";
else if (dataRemoved) which = "data";
else which = "history";
sender.sendMessage(TAG + "Removed " + which + " (" + checkType + "): " + playerName);
if (somethingFound){
sender.sendMessage(TAG + "Issued history and data removal (" + checkType + "): " + playerName);
}
else
sender.sendMessage(TAG + "Nothing found (" + checkType + ", exact spelling): " + playerName);
sender.sendMessage(TAG + "Nothing found (" + checkType + "): " + playerName + " (spelled correctly?)");
return true;
}

View File

@ -22,6 +22,7 @@ import fr.neatmonster.nocheatplus.compat.blocks.changetracker.BlockChangeTracker
import fr.neatmonster.nocheatplus.components.registry.ComponentRegistry;
import fr.neatmonster.nocheatplus.components.registry.ComponentRegistryProvider;
import fr.neatmonster.nocheatplus.components.registry.GenericInstanceRegistry;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.event.mini.EventRegistryBukkit;
import fr.neatmonster.nocheatplus.logging.LogManager;
import fr.neatmonster.nocheatplus.permissions.PermissionRegistry;
@ -216,4 +217,20 @@ public interface NoCheatPlusAPI extends ComponentRegistry<Object>, ComponentRegi
*/
public IPlayerDataManager getPlayerDataManager();
/**
* Get a new registration context instance for registration with
* {@link NoCheatPlusAPI#register(RegistrationContext)}.
*
* @return
*/
public RegistrationContext newRegistrationContext();
/**
* Do use {@link NoCheatPlusAPI#newRegistrationContext()} for future
* compatibility.
*
* @param context
*/
public void register(RegistrationContext context);
}

View File

@ -12,14 +12,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.checks.access;
package fr.neatmonster.nocheatplus.components.config;
/**
* TODO: Keep / Remove.
*
* @author asofold
*/
public interface ICheckConfig {
public interface ICheckConfig extends IConfig {
}

View File

@ -0,0 +1,25 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.config;
/**
* Empty interface for grouping configurations.
*
* @author asofold
*
*/
public interface IConfig {
}

View File

@ -32,6 +32,8 @@ package fr.neatmonster.nocheatplus.components.data;
*/
public interface ICanHandleTimeRunningBackwards {
// TODO: IDataOn ? Applicable data access (IGetGenericInstance) as argument?
/**
* Adjust to system time having run backwards (just "a second ago").
*/

View File

@ -12,9 +12,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.checks.access;
import fr.neatmonster.nocheatplus.components.data.IData;
package fr.neatmonster.nocheatplus.components.data;
/**
* This is for future purposes. Might remove...<br>
@ -27,6 +25,6 @@ import fr.neatmonster.nocheatplus.components.data.IData;
* @author asofold
* @TODO Keep/Remove
*/
public interface ICheckData extends IData{
public interface ICheckData extends IData {
}

View File

@ -0,0 +1,40 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.data;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.players.IPlayerData;
/**
* Player join: : Data storage specific listener for explicit registration with
* the appropriate registry.
*
* @author asofold
*
*/
public interface IDataOnJoin {
/**
* Called with a player join event.
*
* @param player
* @param pData
* @return Return true to remove the data instance from the cache in
* question, false otherwise.
*/
public boolean dataOnJoin(Player player, IPlayerData pData);
}

View File

@ -0,0 +1,42 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.data;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.players.IPlayerData;
/**
* Player leave (quit / kick): Data storage specific listener for explicit
* registration with the appropriate registry.
*
* @author asofold
*
*/
public interface IDataOnLeave {
/**
* Called with player quit or kick.
* <hr>
* (TODO / subject to fix:) Might get called twice (kick + quit).
*
* @param player
* @param pData
* @return Return true to remove the data instance from the cache in
* question, false otherwise.
*/
public boolean dataOnLeave(Player player, IPlayerData pData);
}

View File

@ -0,0 +1,38 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.data;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstance;
/**
* Configuration reload: Data storage specific listener for explicit
* registration with the appropriate registry.
*
* @author asofold
*
*/
public interface IDataOnReload {
/**
* Called after the configuration has been reloaded.
*
* @param dataAccess
* Applicable data access point for data / configs.
* @return Return true to remove the data instance from the cache in
* question, false otherwise.
*/
public boolean dataOnReload(IGetGenericInstance dataAccess);
}

View File

@ -12,29 +12,29 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.checks.access;
package fr.neatmonster.nocheatplus.components.data;
import java.util.Collection;
import fr.neatmonster.nocheatplus.checks.CheckType;
/**
* Extension for CheckData, enabling disable spell checking removal of sub check
* data.
* Check type specific data removal: Data storage specific listener for explicit
* registration with the appropriate registry.
*
* @author asofold
*
*/
public interface IRemoveSubCheckData {
public interface IDataOnRemoveSubCheckData {
/**
* Remove the sub check data of the given CheckType.
* Pinpoint per check type data removal (with the option to keep the
* containing data instance).
*
* @param checkType
* @return True, if the sub check type has been contained <i>and the
* implementation is capable of removing it in general.</i> False,
* if the implementation is not capable of removing that type of
* data, or if the check type doesn't qualify for a sub check at
* all. If false is returned, the entire check group data (or super
* check data) might get removed, in order to ensure data removal.
* @param checkTypes
* @return Return true to remove the data instance from the cache in
* question, false otherwise.
*/
public boolean removeSubCheckData(CheckType checkType);
public boolean dataOnRemoveSubCheckData(final Collection<CheckType> checkTypes);
}

View File

@ -0,0 +1,44 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.data;
import org.bukkit.World;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.players.IPlayerData;
/**
* Player changed world: Data storage specific listener for explicit
* registration with the appropriate registry.
*
* @author asofold
*
*/
public interface IDataOnWorldChange {
/**
* Called with player having changed the world.
*
* @param player
* @param pData
* @param previousWorld
* @param newWorld
* @return Return true to remove the data instance from the cache in
* question, false otherwise.
*/
public boolean dataOnWorldChange(Player player, IPlayerData pData,
World previousWorld, World newWorld);
}

View File

@ -0,0 +1,40 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.data;
import org.bukkit.World;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstance;
/**
* World unload: Data storage specific listener for explicit registration with
* the appropriate registry.
*
* @author asofold
*
*/
public interface IDataOnWorldUnload {
/**
* Called with a world unload event.
*
* @param world
* @return Return true to remove the data instance from the cache in
* question, false otherwise.
*/
public boolean dataOnWorldUnload(World world,
final IGetGenericInstance dataAccess);
}

View File

@ -52,19 +52,27 @@ public abstract class BaseCheckNode<N extends BaseCheckNode<N>> extends CheckTyp
// TODO: GENERIC (ConfigFlagAccess/Fetch based) Override / update / reset.
protected void configFlagOverride(final ConfigFile rawConfiguration,
final AlmostBoolean active, final OverrideType overrideType,
final boolean overrideChildren, final IConfigFlagAccess<N> access) {
/**
* Override state.
*
* @param active
* @param overrideType
* @param overrideChildren
* @param access
*/
protected void override(final AlmostBoolean active,
final OverrideType overrideType, final boolean overrideChildren,
final IConfigFlagAccess<N> access) {
@SuppressWarnings("unchecked")
final boolean applicable = access.getConfigState((N) this).setValue(active, overrideType);
if (applicable) {
// The flag is updated here.
configFlagUpdate(rawConfiguration, false, access);
update(false, access);
}
if (overrideChildren) {
// Always override children, as there can be arbitrary sequences of overrides.
for (final N child : getChildren()) {
child.configFlagOverride(rawConfiguration, active, overrideType,
child.override(active, overrideType,
true, access);
}
}
@ -72,14 +80,20 @@ public abstract class BaseCheckNode<N extends BaseCheckNode<N>> extends CheckTyp
// Updates child nodes recursively, provided they depend on their parent.
for (final N child : getChildren()) {
if (access.getConfigState(child).getValue() == AlmostBoolean.MAYBE) {
child.configFlagUpdate(rawConfiguration, false, access);
child.update(false, access);
}
}
}
}
protected void configFlagUpdate(final ConfigFile rawConfiguration,
final boolean forceUpdateChildren, final IConfigFlagAccess<N> access) {
/**
* Update in place according to tree state.
*
* @param forceUpdateChildren
* @param access
*/
protected void update(final boolean forceUpdateChildren,
final IConfigFlagAccess<N> access) {
@SuppressWarnings("unchecked")
final N thisNode = (N) this; // TODO
final boolean previousActive = access.getState(thisNode);
@ -89,19 +103,7 @@ public abstract class BaseCheckNode<N extends BaseCheckNode<N>> extends CheckTyp
access.setState(thisNode, newActive.decide());
}
else {
// Only fetch from configuration, if override level allows so.
if (rawConfiguration != null && configActivation.allowsOverrideBy(
OverrideType.SPECIFIC)) {
final String configPath = access.getConfigPath(thisNode);
if (configPath != null) {
// TODO: Contract? Either config is null, or path must exist.
newActive = rawConfiguration.getAlmostBoolean(configPath,
AlmostBoolean.MAYBE);
configActivation.setValue(newActive, configOverrideType);
}
}
// If not set by configuration, fetch from parent.
// Fetch from parent.
if (newActive == AlmostBoolean.MAYBE) {
N parent = getParent();
if (parent == null) {
@ -123,11 +125,63 @@ public abstract class BaseCheckNode<N extends BaseCheckNode<N>> extends CheckTyp
// Only update, if the state depends on the parent.
if (forceUpdateChildren
|| access.getConfigState(node).getValue() == AlmostBoolean.MAYBE) {
node.configFlagUpdate(rawConfiguration, forceUpdateChildren, access);
node.update(forceUpdateChildren, access);
}
}
}
}
/**
* Update towards the given rawConfiguration instance.
*
* @param rawConfiguration
* @param forceUpdateChildren
* @param access
*/
protected void update(final ConfigFile rawConfiguration,
final boolean forceUpdateChildren, final IConfigFlagAccess<N> access) {
@SuppressWarnings("unchecked")
final N thisNode = (N) this; // TODO
final AlmostBooleanWithOverride configActivation = access.getConfigState(thisNode);
// First attempt to override by config.
if (rawConfiguration != null) {
if (configActivation.allowsOverrideBy(
OverrideType.SPECIFIC)) {
// TODO: SPECIFIC for inherited !?
final String configPath = access.getConfigPath(thisNode);
final AlmostBoolean setValue;
if (configPath == null) {
setValue = AlmostBoolean.MAYBE;
}
else {
// TODO: Contract? Either config is null, or path must exist.
setValue = rawConfiguration.getAlmostBoolean(configPath, AlmostBoolean.MAYBE);
}
configActivation.setValue(setValue, configOverrideType);
}
}
// TODO: else -> set to MAYBE ?
final boolean oldState = access.getState(thisNode);
update(false, access); // Update in-place.
final boolean changed = oldState ^ access.getState(thisNode);
if (forceUpdateChildren) {
// Update children with configuration (forced).
for (final N node : this.getChildren()) {
node.update(rawConfiguration, forceUpdateChildren, access);
}
}
else if (changed) {
// Update children just for the changed parent.
for (final N node : this.getChildren()) {
// Only update, if the state depends on the parent.
if (access.getConfigState(node).getValue() == AlmostBoolean.MAYBE) {
node.update(false, access);
}
}
}
}
}

View File

@ -78,16 +78,26 @@ public abstract class CheckNodeWithDebug<N extends CheckNodeWithDebug<N>> extend
// TODO: @Override
@SuppressWarnings("unchecked")
protected void overrideDebug(final ConfigFile rawConfiguration,
final AlmostBoolean active, final OverrideType overrideType,
protected void overrideDebug(
final AlmostBoolean active,
final OverrideType overrideType,
final boolean overrideChildren) {
configFlagOverride(rawConfiguration, active, overrideType, overrideChildren, accessDebug);
override(active, overrideType, overrideChildren, accessDebug);
}
// TODO: @Override
@SuppressWarnings("unchecked")
protected void updateDebug(final ConfigFile rawConfiguration, final boolean forceUpdateChildren) {
configFlagUpdate(rawConfiguration, forceUpdateChildren, accessDebug);
protected void updateDebug(
final ConfigFile rawConfiguration,
final boolean forceUpdateChildren) {
update(rawConfiguration, forceUpdateChildren, accessDebug);
}
// TODO: @Override
@SuppressWarnings("unchecked")
protected void updateDebug(
final boolean forceUpdateChildren) {
update(forceUpdateChildren, accessDebug);
}
// TODO: resetDebug(...)

View File

@ -160,7 +160,7 @@ public class DefaultGenericInstanceRegistry implements GenericInstanceRegistry,
* Only call if uniqueHandle is null and under lock.
*/
private void updateUniqueHandle() {
if (uniqueHandle.isDisabled()) {
if (uniqueHandle != null && uniqueHandle.isDisabled()) {
unregisterListener(uniqueHandle);
}
if (uniqueHandle == null) {

View File

@ -12,12 +12,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry;
package fr.neatmonster.nocheatplus.components.registry.factory;
import java.util.concurrent.locks.Lock;
import fr.neatmonster.nocheatplus.components.concurrent.IPrimaryThreadContextTester;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.utilities.ds.map.HashMapLOW;
/**
@ -31,7 +30,7 @@ import fr.neatmonster.nocheatplus.utilities.ds.map.HashMapLOW;
* @param <A>
* Argument type for IFactoryOne instances.
*/
public class FactoryOneRegistry<A> {
public class FactoryOneRegistry<A> implements IFactoryOneRegistry<A> {
private final Lock lock;
private final IPrimaryThreadContextTester primaryThreadContextTester;
@ -45,13 +44,8 @@ public class FactoryOneRegistry<A> {
factories = new HashMapLOW<Class<?>, IFactoryOne<A,?>>(lock, 30);
}
/**
* Note that types are automatically sorted into registered groups.
*
* @param registerFor
* @param factory
*/
public <I, T extends I> void registerFactory(final Class<T> registerFor,
@Override
public <T> void registerFactory(final Class<T> registerFor,
final IFactoryOne<A, T> factory) {
if (!primaryThreadContextTester.isPrimaryThread()) {
outsideThreadContext("register factory");
@ -65,16 +59,7 @@ public class FactoryOneRegistry<A> {
throw new IllegalStateException("Can't call off the primary thread context: " + tag);
}
/**
* Fetch a new instance from a registered factory.
*
* @param registeredFor
* @param arg
* @return
* @throws A
* RuntimeException (might get changed to a registry type of
* exception), in case a factory throws something-
*/
@Override
public <T> T getNewInstance(final Class<T> registeredFor, final A arg) {
@SuppressWarnings("unchecked")
final IFactoryOne<A, T> factory = (IFactoryOne<A, T>) factories.get(registeredFor);

View File

@ -0,0 +1,40 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry.factory;
public interface IFactoryOneRegistry<A> {
/**
* Register a factory.
*
* @param registerFor
* @param factory
*/
public <T> void registerFactory(final Class<T> registerFor,
final IFactoryOne<A, T> factory);
/**
* Fetch a new instance from a registered factory.
*
* @param registeredFor
* @param arg
* @return
* @throws RuntimeException
* (might get changed to a registry type of exception), in case
* a factory throws something-
*/
public <T> T getNewInstance(final Class<T> registeredFor, final A arg);
}

View File

@ -0,0 +1,49 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry.factory;
import fr.neatmonster.nocheatplus.components.registry.meta.IRichTypeSetRegistry;
/**
* Combine an IFactoryOneRegistry with TypeSetRegistries (general and per check
* type). TypeSetRegistries are kept "synchronized", in terms of registered
* groups (not items).
*
* @author asofold
*
*/
public interface IRichFactoryRegistry<A> extends IRichTypeSetRegistry, IFactoryOneRegistry<A> {
/**
* Register a factory, see:
* {@link IFactoryOneRegistry#registerFactory(Class, IFactoryOne)}
* <hr/>
* The type the factory is registered for might automatically be added to
* all groups which have been set to auto grouping:
* {@link #createAutoGroup(Class)}
* <hr/>
*/
@Override
public <T> void registerFactory(final Class<T> registerFor,
final IFactoryOne<A, T> factory);
/**
* Create the group, and automatically add factory return types to this group
* @param groupType
*/
public <G> void createAutoGroup(Class<G> groupType);
}

View File

@ -0,0 +1,128 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry.factory;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.registry.meta.RichTypeSetRegistry;
import fr.neatmonster.nocheatplus.utilities.CheckTypeUtil;
import fr.neatmonster.nocheatplus.utilities.CheckUtils;
/**
* Thread safe "read" factory registry with additional convenience
* functionality. (TODO: Registration might not be thread-safe.)
* <hr>
* Thread safety further depends on the registered factories for fetching new
* instances.
*
* @author asofold
*
* @param <A>
* Factory argument type.
*/
public class RichFactoryRegistry<A> extends RichTypeSetRegistry implements IRichFactoryRegistry<A> {
public static class CheckRemovalSpec {
public final Collection<Class<?>> completeRemoval = new LinkedHashSet<Class<?>>();
public final Collection<Class<? extends IDataOnRemoveSubCheckData>> subCheckRemoval = new LinkedHashSet<Class<? extends IDataOnRemoveSubCheckData>>();
public final Collection<CheckType> checkTypes;;
public CheckRemovalSpec(final CheckType checkType,
final boolean withDescendantCheckTypes,
final IRichFactoryRegistry<?> factoryRegistry
) {
this(withDescendantCheckTypes
? CheckTypeUtil.getWithDescendants(checkType)
: Arrays.asList(checkType), factoryRegistry);
}
public CheckRemovalSpec(final Collection<CheckType> checkTypes,
final IRichFactoryRegistry<?> factoryRegistry) {
this.checkTypes = checkTypes;
for (final CheckType refType : checkTypes) {
for (final Class<? extends IData> type : factoryRegistry.getGroupedTypes(
IData.class, refType)) {
completeRemoval.add(type);
}
for (final Class<? extends IDataOnRemoveSubCheckData> type : factoryRegistry.getGroupedTypes(
IDataOnRemoveSubCheckData.class, refType)) {
subCheckRemoval.add(type);
}
}
if (checkTypes.contains(CheckType.ALL)) {
for (final Class<? extends IData> type : factoryRegistry.getGroupedTypes(
IData.class)) {
completeRemoval.add(type);
}
for (final Class<? extends IDataOnRemoveSubCheckData> type : factoryRegistry.getGroupedTypes(
IDataOnRemoveSubCheckData.class)) {
subCheckRemoval.add(type);
}
}
}
}
private final Lock lock;
private final FactoryOneRegistry<A> factoryRegistry;
@SuppressWarnings("unchecked")
private Set<Class<?>> autoGroups = Collections.EMPTY_SET;
public RichFactoryRegistry(final Lock lock) {
super(lock);
this.lock = lock;
factoryRegistry = new FactoryOneRegistry<A>(
lock, CheckUtils.primaryServerThreadContextTester);
}
@Override
public <T> T getNewInstance(final Class<T> registeredFor, final A arg) {
return factoryRegistry.getNewInstance(registeredFor, arg);
}
@SuppressWarnings("unchecked")
@Override
public <T> void registerFactory(final Class<T> registerFor,
final IFactoryOne<A, T> factory) {
lock.lock();
factoryRegistry.registerFactory(registerFor, factory);
for (final Class<?> groupType: autoGroups) {
if (groupType.isAssignableFrom(registerFor)) {
addToGroups(registerFor, (Class<? super T>) groupType);
}
}
lock.unlock();
}
@Override
public <G> void createAutoGroup(final Class<G> groupType) {
lock.lock();
createGroup(groupType);
final Set<Class<?>> autoGroups = new LinkedHashSet<Class<?>>(this.autoGroups);
autoGroups.add(groupType);
this.autoGroups = autoGroups;
lock.unlock();
}
}

View File

@ -0,0 +1,151 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry.meta;
import java.util.Collection;
import fr.neatmonster.nocheatplus.checks.CheckType;
/**
* Combine a general with per check type TypeSetRegistries. TypeSetRegistries
* are kept "synchronized", in terms of registered groups (not items).
*
* @author asofold
*
*/
public interface IRichTypeSetRegistry {
// TODO: package name?
/**
* Get all data types extending 'registeredFor', that have been explicitly
* registered for that group.
*
* @param registeredFor
* @return
*/
public <T> Collection<Class<? extends T>> getGroupedTypes(
Class<T> groupType);
/**
* Get all data types extending 'registeredFor', that have been explicitly
* registered for that group and for that check type. No inheritance logic
* is applied, nor will CheckType.ALL return all data types (use
* {@link #getGroupedTypes(Class)} instead).
*
* @param registeredFor
* @return
*/
public <T> Collection<Class<? extends T>> getGroupedTypes(
Class<T> groupType, CheckType checkType);
// TODO: getFactoryTypes -> registered factory return types ?
/**
* Register types for a group type.
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
* @param itemType
* @param groupTypes
*/
public <I> void addToGroups(Class<I> itemType,
Class<? super I>... groupTypes);
/**
* Register the itemType for all applicable group types that already have
* been registered.
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
* @param itemType
*/
public void addToExistingGroups(final Class<?> itemType);
/**
* Register an item type for group types for for a check type (and general). Types get added
* both to the general grouped types and those specific to the given check
* type.
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
* @param checkType
* @param itemType
* @param groupTypes
*/
public <I> void addToGroups(CheckType checkType,
Class<I> itemType, Class<? super I>... groupTypes);
/**
* Register the itemType for all applicable group types that already have
* been registered, both for the general registry and check type specific
* (no inheritance logic).
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
*
* @param checkType
* @param itemType
*/
public <I> void addToExistingGroups(CheckType checkType,
Class<I> itemType);
/**
* Register an item type for group types for for all given check types (and
* general). Data types get added both to the general grouped types and
* those specific to the given check types. Suggested use is with
* {@link fr.neatmonster.nocheatplus.utilities.CheckTypeUtil}.
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
* @param checkTypes
* @param itemType
* @param groupTypes
*/
public <I> void addToGroups(Collection<CheckType> checkTypes,
Class<I> itemType, Class<? super I>... groupTypes);
/**
* Register the itemType for all applicable group types that already have
* been registered, both for the general registry and check type specific
* (no inheritance logic).Suggested use is with
* {@link fr.neatmonster.nocheatplus.utilities.CheckTypeUtil}.
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
*
* @param checkType
* @param itemType
*/
public <I> void addToExistingGroups(Collection<CheckType> checkTypes,
Class<I> itemType);
/**
* Register the group type both for the general registry and for all check
* types.
* <hr>
* Groups are always present for the general registry, as well as for all
* check type specific registries. Item type registration remains specific.
*
* @param groupType
*/
public <G> void createGroup(Class<G> groupType);
}

View File

@ -0,0 +1,150 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry.meta;
import java.util.Collection;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.utilities.ds.map.HashMapLOW;
public class RichTypeSetRegistry implements IRichTypeSetRegistry {
private final Lock lock;
/**
* Grouped types to have a faster way of iterating data types stored in the
* PlayerData cache.
*/
private final TypeSetRegistry groupedTypes;
/**
* Additional attachment of grouped types to check types - needs to be
* registered explicitly.
*/
private final HashMapLOW<CheckType, TypeSetRegistry> groupedTypesByCheckType;
public RichTypeSetRegistry(final Lock lock) {
this.lock = lock;
groupedTypes = new TypeSetRegistry(lock);
groupedTypesByCheckType = new HashMapLOW<CheckType,
TypeSetRegistry>(lock, 35);
}
@Override
public <T> Collection<Class<? extends T>> getGroupedTypes(final Class<T> groupType) {
return groupedTypes.getGroupedTypes(groupType);
}
@Override
@SuppressWarnings("unchecked")
public <T> Collection<Class<? extends T>> getGroupedTypes(final Class<T> groupType,
final CheckType checkType) {
final TypeSetRegistry reg = groupedTypesByCheckType.get(checkType);
return (reg == null
? (Collection<Class<? extends T>>) Collections.EMPTY_SET
: reg.getGroupedTypes(groupType));
}
@Override
public <I> void addToGroups(final Class<I> itemType,
final Class<? super I>... groupTypes) {
lock.lock();
for (final Class<? super I> groupType : groupTypes) {
createGroup(groupType);
}
groupedTypes.addToGroups(itemType, groupTypes);
lock.unlock();
}
@Override
public <I> void addToGroups(CheckType checkType, Class<I> itemType,
Class<? super I>... groupTypes) {
lock.lock();
for (final Class<? super I> groupType : groupTypes) {
createGroup(groupType);
}
TypeSetRegistry reg = groupedTypesByCheckType.get(checkType);
if (reg == null) {
reg = new TypeSetRegistry(lock);
updateRegistry(reg);
groupedTypesByCheckType.put(checkType, reg);
}
reg.addToGroups(itemType, groupTypes);
groupedTypes.addToGroups(itemType, groupTypes);
lock.unlock();
}
@Override
public void addToExistingGroups(Class<?> itemType) {
groupedTypes.addToExistingGroups(itemType);
}
@Override
public <I> void addToExistingGroups(final CheckType checkType,
final Class<I> itemType) {
lock.lock();
TypeSetRegistry reg = groupedTypesByCheckType.get(checkType);
if (reg == null) {
reg = new TypeSetRegistry(lock);
updateRegistry(reg);
groupedTypesByCheckType.put(checkType, reg);
}
reg.addToExistingGroups(itemType);
groupedTypes.addToExistingGroups(itemType);
lock.unlock();
}
/**
* Update the given registry to contain all group types of the general
* registry.
*
* @param reg
*/
private void updateRegistry(final TypeSetRegistry reg) {
reg.updateGroupTypes(groupedTypes);
}
@Override
public <G> void createGroup(Class<G> groupType) {
groupedTypes.createGroup(groupType);
for (Entry<CheckType, TypeSetRegistry> entry : groupedTypesByCheckType.iterable()) {
entry.getValue().createGroup(groupType);
}
}
@Override
public <I> void addToGroups(final Collection<CheckType> checkTypes,
final Class<I> itemType, final Class<? super I>... groupTypes) {
lock.lock();
for (final CheckType checkType : checkTypes) {
addToGroups(checkType, itemType, groupTypes);
}
lock.unlock();
}
@Override
public <I> void addToExistingGroups(final Collection<CheckType> checkTypes,
final Class<I> itemType) {
lock.lock();
for (final CheckType checkType : checkTypes) {
addToExistingGroups(checkType, itemType);
}
lock.unlock();
}
}

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.components.registry;
package fr.neatmonster.nocheatplus.components.registry.meta;
import java.util.Collection;
import java.util.Collections;
@ -69,6 +69,10 @@ public class TypeSetRegistry {
return group;
}
void createGroup(TypeSetRegistry extReg) {
extReg.createGroup(this.groupType);
}
}
////////////////////////77
@ -110,6 +114,19 @@ public class TypeSetRegistry {
lock.unlock();
}
/**
* Create if not existent.
*
* @param itemTypes
*/
public <G> void createGroup(final Class<G> groupType) {
lock.lock();
if (!groupedTypes.containsKey(groupType)) {
newGroupNode(groupType);
}
lock.unlock();
}
private <G> GroupNode<G> newGroupNode(Class<G> groupType) {
final GroupNode<G> node = new GroupNode<G>(groupType);
groupedTypes.put(groupType, node);
@ -147,4 +164,19 @@ public class TypeSetRegistry {
return group == null ? Collections.EMPTY_LIST : group.getItems();
}
/**
* Ensure all groups exist (items are not copied).
*
* @param refReg
*/
public void updateGroupTypes(TypeSetRegistry refReg) {
lock.lock(); // Ensure no interruption - iterator is not using lock, thus no dead locks.
for (final Entry<Class<?>, GroupNode<?>> entry : refReg.groupedTypes.iterable()) {
if (!this.groupedTypes.containsKey(entry.getKey())) {
entry.getValue().createGroup(this);
}
}
lock.unlock();
}
}

View File

@ -681,5 +681,14 @@ public class RegistrationOrder {
&& (afterTag == null ? other.getAfterTag() == null : afterTag.equals(other.getAfterTag()));
}
@Override
public String toString() {
return "RegistrationOrder(p=" + basePriority
+ (tag == null ? "" : " t='" + tag + "'")
+ (beforeTag == null ? "" : " bt='" + beforeTag + "'")
+ (afterTag == null ? "" : "at='" + afterTag + "'")
+ ")";
}
}

View File

@ -0,0 +1,7 @@
package fr.neatmonster.nocheatplus.components.registry.setup;
public interface IDoRegister {
public void doRegister();
}

View File

@ -0,0 +1,174 @@
package fr.neatmonster.nocheatplus.components.registry.setup;
import java.util.LinkedList;
import java.util.List;
import fr.neatmonster.nocheatplus.components.config.IConfig;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.setup.config.RegisterConfigWorld;
import fr.neatmonster.nocheatplus.components.registry.setup.data.RegisterDataPlayer;
import fr.neatmonster.nocheatplus.components.registry.setup.data.RegisterDataWorld;
import fr.neatmonster.nocheatplus.components.registry.setup.instance.RegisterInstancePlayer;
import fr.neatmonster.nocheatplus.components.registry.setup.instance.RegisterInstanceWorld;
/**
* Convenience: set up a check or similar with NCP registries.
* <hr/>
* For now, this is only a registry helper for internal registration. Later on,
* this will aggregate components, dependencies and activation conditions,
* enabling automatic registration and unregistering. Multiple contexts
* registering the same types could lead to inconsistencies, if one gets
* unregistered - the planned dependency mechanism is meant to mend this (if not
* already registered, register the dependency first, then the component using
* it).
* <hr/>
* <ul>
* <li>Register factories and type relations for instances, config, data.</li>
* <li>(When to register, dependencies, IActivation.)</li>
* <li>(Register further components, such as listeners.)</li>
* <li>(When to unregister.)</li>
* <li>(More precise definition what/how to unregister.)</li>
* </ul>
* <hr/>
*
* @author asofold
*
*/
public class RegistrationContext implements IDoRegister {
/*
* / TODO: ILockable <-> tie to instantiation by global registry, tie sub
* objects to this.
*/
// TODO: Base some things rather on ICheckConfig, ICheckData ?
/**
* General type of registration, in terms of how often / when it's tried to
* get registered.
* <hr/>
*
* @author asofold
*
*/
public static enum RegistrationType {
/** Try to register directly, one time only. */
ONCE;
}
/**
* Removal type, in terms of a rough description for when to unregister the
* contained components.
*
* @author asofold
*
*/
public static enum RemovalType {
/**
* Never remove registration. Still might get removed/dented with the
* plugin getting disabled.
*/
NEVER;
}
//////////////////////////////
// Instance
//////////////////////////////
// TODO: Put a global registry in control of instantiation, add id(s) / tags.
private final List<IDoRegister> registerItems = new LinkedList<IDoRegister>();
//////////////////////////////
// Getter
//////////////////////////////
public RegistrationType registrationType() {
return RegistrationType.ONCE;
}
public RemovalType removalType() {
return RemovalType.NEVER;
}
/**
* Create a new (attached) per world instance registration object.
*
* @return
*/
public <T extends IConfig> RegisterInstanceWorld<T> registerInstanceWorld(Class<T> type) {
RegisterInstanceWorld<T> item = new RegisterInstanceWorld<T>(this, type);
registerItems.add(item);
return item;
}
/**
* Create a new (attached) per world config registration object. The config
* types are registered with the IPlayerDataManager too (no further grouping
* , no factory).
*
* @return
*/
public <T extends IConfig> RegisterConfigWorld<T> registerConfigWorld(Class<T> configType) {
RegisterConfigWorld<T> item = new RegisterConfigWorld<T>(this, configType);
registerItems.add(item);
return item;
}
/**
* Create a new (attached) per world data registration object. The data
* types are registered with the IPlayerDataManager too (no further grouping
* , no factory).
*
* @return
*/
public <T extends IData> RegisterDataWorld<T> registerDataWorld(Class<T> dataType) {
RegisterDataWorld<T> item = new RegisterDataWorld<T>(this, dataType);
registerItems.add(item);
return item;
}
/**
* Create a new (attached) per instance data registration object.
*
* @return
*/
public <T extends IData> RegisterInstancePlayer<T> registerInstancePlayer(Class<T> type) {
RegisterInstancePlayer<T> item = new RegisterInstancePlayer<T>(this, type);
registerItems.add(item);
return item;
}
/**
* Create a new (attached) per player data registration object.
*
* @return
*/
public <T extends IData> RegisterDataPlayer<T> registerDataPlayer(Class<T> dataType) {
RegisterDataPlayer<T> item = new RegisterDataPlayer<T>(this, dataType);
registerItems.add(item);
return item;
}
//////////////////////////////
// Setter
//////////////////////////////
//////////////////////////////
// Other functionality.
//////////////////////////////
@Override
public void doRegister() {
// TODO: ILockable, ...
// TODO: Exception handling.
for (IDoRegister item : registerItems) {
item.doRegister();
}
// TODO: (Capability to roll back?)
}
}

View File

@ -0,0 +1,64 @@
package fr.neatmonster.nocheatplus.components.registry.setup.config;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.config.IConfig;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.components.registry.setup.instance.RegisterInstanceWorld;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* World config types are automatically registered as config types with the
* IPlayerDataManager - no further grouping is done.
*
* @author asofold
*
* @param <T>
*/
public class RegisterConfigWorld<T extends IConfig> extends RegisterInstanceWorld<T> {
public RegisterConfigWorld(RegistrationContext registrationContext,
Class<T> type) {
super(registrationContext, type);
}
@Override
public RegisterConfigWorld<T> factory(
IFactoryOne<WorldFactoryArgument, T> factory) {
super.factory(factory);
return this;
}
@Override
public RegisterConfigWorld<T> registerConfigTypesPlayer() {
registerConfigTypesPlayer = true;
return this;
}
@Override
public RegisterConfigWorld<T> addToGroups(
final CheckType checkType, final boolean withDescendantCheckTypes,
final Class<? super T>... groupTypes) {
super.addToGroups(checkType, withDescendantCheckTypes, groupTypes);
return this;
}
@Override
public RegisterConfigWorld<T> registerConfigTypesPlayer(
CheckType checkType, boolean withDescendantCheckTypes) {
super.registerConfigTypesPlayer(checkType, withDescendantCheckTypes);
return this;
}
@Override
public void doRegister() {
super.doRegister();
if (!registerConfigTypesPlayer) {
registerConfigTypesPlayer(NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager());
}
}
}

View File

@ -0,0 +1,46 @@
package fr.neatmonster.nocheatplus.components.registry.setup.data;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.components.registry.setup.instance.RegisterInstancePlayer;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
public class RegisterDataPlayer<T extends IData> extends RegisterInstancePlayer<T> {
public RegisterDataPlayer(RegistrationContext registrationContext,
Class<T> type) {
super(registrationContext, type);
}
@Override
public RegisterDataPlayer<T> factory(
IFactoryOne<PlayerFactoryArgument, T> factory) {
super.factory(factory);
return this;
}
@Override
public RegisterDataPlayer<T> addToGroups(
final CheckType checkType, final boolean withDescendantCheckTypes,
final Class<? super T>... groupTypes) {
super.addToGroups(checkType, withDescendantCheckTypes, groupTypes);
return this;
}
@Override
public RegisterDataPlayer<T> registerDataTypesPlayer(
CheckType checkType, boolean withDescendantCheckTypes) {
super.registerDataTypesPlayer(checkType, withDescendantCheckTypes);
return this;
}
@Override
public RegisterDataPlayer<T> removeSubCheckData(
CheckType checkType, boolean withDescendantCheckTypes) {
super.removeSubCheckData(checkType, withDescendantCheckTypes);
return this;
}
}

View File

@ -0,0 +1,63 @@
package fr.neatmonster.nocheatplus.components.registry.setup.data;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.components.registry.setup.instance.RegisterInstanceWorld;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
/**
* Per world data types are automatically registered as (data) types with the
* IPlayerDataManager - no further grouping is done.
*
* @author asofold
*
* @param <T>
*/
public class RegisterDataWorld<T extends IData> extends RegisterInstanceWorld<T> {
public RegisterDataWorld(RegistrationContext registrationContext,
Class<T> type) {
super(registrationContext, type);
}
@Override
public RegisterDataWorld<T> factory(
IFactoryOne<WorldFactoryArgument, T> factory) {
super.factory(factory);
return this;
}
@Override
public RegisterDataWorld<T> addToGroups(
final CheckType checkType, final boolean withDescendantCheckTypes,
final Class<? super T>... groupTypes) {
super.addToGroups(checkType, withDescendantCheckTypes, groupTypes);
return this;
}
@Override
public RegisterDataWorld<T> registerDataTypesPlayer(
CheckType checkType, boolean withDescendantCheckTypes) {
super.registerDataTypesPlayer(checkType, withDescendantCheckTypes);
return this;
}
@Override
public RegisterDataWorld<T> removeSubCheckData(
CheckType checkType, boolean withDescendantCheckTypes) {
super.removeSubCheckData(checkType, withDescendantCheckTypes);
return this;
}
@Override
public void doRegister() {
super.doRegister();
if (!registerDataTypesPlayer) {
registerDataTypesPlayer(NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager());
}
}
}

View File

@ -0,0 +1,329 @@
package fr.neatmonster.nocheatplus.components.registry.setup.instance;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.config.ICheckConfig;
import fr.neatmonster.nocheatplus.components.config.IConfig;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.factory.IRichFactoryRegistry;
import fr.neatmonster.nocheatplus.components.registry.setup.IDoRegister;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.players.IPlayerDataManager;
import fr.neatmonster.nocheatplus.utilities.CheckTypeUtil;
/**
* Base class.
*
* @author asofold
*
* @param <T>
* Instance type.
* @param <A>
* Argument type for IFactoryOne.
*/
public abstract class RegisterInstance<T, A> implements IDoRegister {
/**
* Allow registering with multiple registries.
*
* @author asofold
*
*/
protected static interface IDoRegisterWithRegistry {
void doRegister(IRichFactoryRegistry<?> factoryRegistry);
}
////////////////////
// Instance
////////////////////
protected final RegistrationContext registrationContext;
protected final IRichFactoryRegistry<A> factoryRegistry;
protected final Class<T> type;
protected IFactoryOne<A, T> factory = null;
protected boolean registerConfigTypesPlayer = false;
protected boolean registerDataTypesPlayer = false;
/**
* Types that may be registered as types for players as well, for the case
* this is a per world type (with or without factory).
*/
protected final List<IDoRegisterWithRegistry> genericDataItems = new LinkedList<IDoRegisterWithRegistry>();
/**
* Types that may be registered as types for players as well, for the case
* this is a per world type (with or without factory).
*/
protected final List<IDoRegisterWithRegistry> genericConfigItems = new LinkedList<IDoRegisterWithRegistry>();
/** Standard items. */
protected final List<IDoRegister> items = new LinkedList<IDoRegister>();
public RegisterInstance(RegistrationContext registrationContext, Class<T> type,
IRichFactoryRegistry<A> factoryRegistry) {
this.registrationContext = registrationContext;
this.factoryRegistry = factoryRegistry;
this.type = type;
}
//////////////////////////////
// Abstract
//////////////////////////////
//////////////////////////////
// Setter (chaining).
//////////////////////////////
/**
*
* @param factory
* @return This instance for chaining.
*/
public RegisterInstance<T,A> factory(IFactoryOne<A, T> factory) {
this.factory = factory;
return this;
}
/**
* This does not check for data/config types, to apply with
* registreXYTypesAB.
*
* @param checkType
* @param withDescendantCheckTypes
* @param groupTypes
* @return
*/
public RegisterInstance<T,A> addToGroups(final CheckType checkType,
final boolean withDescendantCheckTypes,
final Class<? super T>... groupTypes) {
items.add(new IDoRegister() {
@Override
public void doRegister() {
if (withDescendantCheckTypes) {
factoryRegistry.addToGroups(
CheckTypeUtil.getWithDescendants(checkType),
type, groupTypes);
}
else {
factoryRegistry.addToGroups(checkType, type, groupTypes);
}
}
});
return this;
}
/**
* Register standard config types with the player registry as well (aiming
* at per world configuration types).
* <hr>
* <ul>
* <li>IConfig</li>
* <li>ICheckConfig</li>
* </ul>
*
* @return
*/
public RegisterInstance<T,A> registerConfigTypesPlayer() {
registerConfigTypesPlayer = true;
return this;
}
/**
* Register standard config types with the player registry as well (aiming
* at per world configuration types).
* <hr>
* <ul>
* <li>IConfig</li>
* <li>ICheckConfig</li>
* </ul>
*
* @param checkType
* @param withDescendantCheckTypes
* @return
*/
public RegisterInstance<T,A> registerConfigTypesPlayer(
final CheckType checkType,
final boolean withDescendantCheckTypes) {
registerConfigTypesPlayer();
final Collection<CheckType> checkTypes = withDescendantCheckTypes ? CheckTypeUtil.getWithDescendants(checkType)
: Arrays.asList(checkType);
if (IConfig.class.isAssignableFrom(type)) {
genericConfigItems.add(new IDoRegisterWithRegistry() {
@SuppressWarnings("unchecked")
@Override
public void doRegister(IRichFactoryRegistry<?> factoryRegistry) {
factoryRegistry.addToGroups(checkTypes,
(Class<? extends IConfig>) type,
IConfig.class);
}
});
}
if (ICheckConfig.class.isAssignableFrom(type)) {
genericConfigItems.add(new IDoRegisterWithRegistry() {
@SuppressWarnings("unchecked")
@Override
public void doRegister(IRichFactoryRegistry<?> factoryRegistry) {
factoryRegistry.addToGroups(checkTypes,
(Class<? extends ICheckConfig>) type,
ICheckConfig.class);
}
});
}
return this;
}
/**
* Register standard data types with the player registry as well (aiming at
* per world data).
* <hr>
* <ul>
* <li>IData</li>
* <li>ICheckData</li>
* </ul>
*
* @return
*/
public RegisterInstance<T,A> registerDataTypesPlayer() {
registerDataTypesPlayer = true;
return this;
}
/**
* Register standard data types with the player registry as well (aiming at
* per world data).
* <hr>
* <ul>
* <li>IData</li>
* <li>ICheckData</li>
* </ul>
*
* @param checkType
* @param withDescendantCheckTypes
* @return
*/
public RegisterInstance<T,A> registerDataTypesPlayer(
final CheckType checkType,
final boolean withDescendantCheckTypes) {
registerDataTypesPlayer();
final Collection<CheckType> checkTypes = withDescendantCheckTypes ? CheckTypeUtil.getWithDescendants(checkType)
: Arrays.asList(checkType);
if (IData.class.isAssignableFrom(type)) {
genericConfigItems.add(new IDoRegisterWithRegistry() {
@SuppressWarnings("unchecked")
@Override
public void doRegister(IRichFactoryRegistry<?> factoryRegistry) {
factoryRegistry.addToGroups(checkTypes,
(Class<? extends IData>) type,
IData.class);
}
});
}
if (ICheckData.class.isAssignableFrom(type)) {
genericConfigItems.add(new IDoRegisterWithRegistry() {
@SuppressWarnings("unchecked")
@Override
public void doRegister(IRichFactoryRegistry<?> factoryRegistry) {
factoryRegistry.addToGroups(checkTypes,
(Class<? extends ICheckData>) type,
ICheckData.class);
}
});
}
return this;
}
/**
* Register for sub check removal.
*
* @param checkType
* @param withDescendantCheckTypes
* @return
*/
public RegisterInstance<T,A> removeSubCheckData(
final CheckType checkType,
final boolean withDescendantCheckTypes) {
if (!IDataOnRemoveSubCheckData.class.isAssignableFrom(type)) {
throw new UnsupportedOperationException();
}
final Collection<CheckType> checkTypes = withDescendantCheckTypes ? CheckTypeUtil.getWithDescendants(checkType)
: Arrays.asList(checkType);
items.add(new IDoRegister() {
@SuppressWarnings("unchecked")
@Override
public void doRegister() {
factoryRegistry.addToGroups(checkTypes,
(Class<? extends IDataOnRemoveSubCheckData>) type,
IDataOnRemoveSubCheckData.class);
}
});
return this;
}
//////////////////////////////
// Other functionality.
//////////////////////////////
public RegistrationContext registrationContext() {
return registrationContext;
}
public RegistrationContext context() {
return registrationContext();
}
@Override
public void doRegister() {
if (factory != null) {
factoryRegistry.registerFactory(type, factory);
}
for (final IDoRegister item : items) {
item.doRegister();
}
for (final IDoRegisterWithRegistry item : genericDataItems) {
item.doRegister(factoryRegistry);
}
final IPlayerDataManager pdMan = NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager();
if (registerConfigTypesPlayer) {
registerConfigTypesPlayer(pdMan);
}
if (registerDataTypesPlayer) {
registerDataTypesPlayer(pdMan);
}
}
@SuppressWarnings("unchecked")
protected void registerConfigTypesPlayer(final IPlayerDataManager pdMan) {
if (IConfig.class.isAssignableFrom(type)) {
pdMan.addToGroups((Class<? extends IConfig>) type, IConfig.class);
}
if (ICheckConfig.class.isAssignableFrom(type)) {
pdMan.addToGroups((Class<? extends ICheckConfig>) type, ICheckConfig.class);
}
for (final IDoRegisterWithRegistry item : genericConfigItems) {
item.doRegister(pdMan);
}
}
@SuppressWarnings("unchecked")
protected void registerDataTypesPlayer(final IPlayerDataManager pdMan) {
if (IData.class.isAssignableFrom(type)) {
pdMan.addToGroups((Class<? extends IData>) type, IData.class);
}
if (ICheckData.class.isAssignableFrom(type)) {
pdMan.addToGroups((Class<? extends ICheckData>) type, ICheckData.class);
}
for (final IDoRegisterWithRegistry item : genericDataItems) {
item.doRegister(pdMan);
}
}
}

View File

@ -0,0 +1,15 @@
package fr.neatmonster.nocheatplus.components.registry.setup.instance;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.players.PlayerFactoryArgument;
public class RegisterInstancePlayer<T> extends RegisterInstance<T, PlayerFactoryArgument> {
public RegisterInstancePlayer(RegistrationContext registrationContext,
Class<T> type) {
super(registrationContext, type,
NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager());
}
}

View File

@ -0,0 +1,15 @@
package fr.neatmonster.nocheatplus.components.registry.setup.instance;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.worlds.WorldFactoryArgument;
public class RegisterInstanceWorld<T> extends RegisterInstance<T, WorldFactoryArgument> {
public RegisterInstanceWorld(RegistrationContext registrationContext,
Class<T> type) {
super(registrationContext, type,
NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager());
}
}

View File

@ -14,21 +14,21 @@
*/
package fr.neatmonster.nocheatplus.event.mini;
import java.lang.reflect.Method;
import java.util.Collection;
import org.bukkit.Bukkit;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.Plugin;
import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName;
import java.lang.reflect.Method;
import java.util.Collection;
import org.bukkit.Bukkit;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.EventExecutor;
import org.bukkit.plugin.Plugin;
import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder;
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
@ -68,8 +68,8 @@ public class EventRegistryBukkit extends MultiListenerRegistry<Event, EventPrior
*/
protected static class CancellableNodeBukkit<E> extends MiniListenerNode<E, EventPriority> {
public CancellableNodeBukkit(EventPriority basePriority) {
super(basePriority);
public CancellableNodeBukkit(Class<E> baseType, EventPriority basePriority) {
super(baseType, basePriority);
}
// TODO: Future java: E extends Cancellable ?
@ -95,9 +95,9 @@ public class EventRegistryBukkit extends MultiListenerRegistry<Event, EventPrior
if (Cancellable.class.isAssignableFrom(eventClass)) {
// TODO: Check if order is right (eventClass extends Cancellable).
// TODO: Future java (see above) ?
return new CancellableNodeBukkit<E>(basePriority);
return new CancellableNodeBukkit<E>(eventClass, basePriority);
} else {
return new MiniListenerNode<E, EventPriority>(basePriority);
return new MiniListenerNode<E, EventPriority>(eventClass, basePriority);
}
}
};

View File

@ -14,16 +14,19 @@
*/
package fr.neatmonster.nocheatplus.event.mini;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import fr.neatmonster.nocheatplus.components.registry.order.IGetRegistrationOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.AbstractRegistrationOrderSort;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.logging.Streams;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName;
import fr.neatmonster.nocheatplus.components.registry.order.IGetRegistrationOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.AbstractRegistrationOrderSort;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
/**
@ -78,13 +81,15 @@ public class MiniListenerNode<E, P> {
@SuppressWarnings("unchecked")
protected ListenerEntry<E>[] sortedListeners = new ListenerEntry[0];
protected final Class<E> baseType;
/**
* Stored for the case of exceptions.
*/
protected final P basePriority;
public MiniListenerNode(P basePriority) {
public MiniListenerNode(Class<E> baseType, P basePriority) {
this.baseType = baseType;
this.basePriority = basePriority;
}
@ -159,12 +164,37 @@ public class MiniListenerNode<E, P> {
}
}
private void logListenerException(final ListenerEntry<E> entry, final int index, final int length, final E event, final Throwable t) {
// Log long part once, to keep spam slightly down.
StaticLog.logOnce(Streams.STATUS, Level.SEVERE,
"Listener exception: event=" + event.getClass().getName() + " priority=" + this.basePriority + " index=" + index + " n=" + length
// TODO: Add id information to compare to registry log (later).
, StringUtil.throwableToString(t));
private void logListenerException(final ListenerEntry<E> entry,
final int index, final int length,
final E event, final Throwable t) {
// Log long part once, to keep spam slightly down.
// TODO: Add more info (ORDER with tags, class/wrapped class).
final StringBuilder builder = new StringBuilder(1024);
builder.append(" Details:");
builder.append(" listenerType=" + entry.listener.getClass().getName());
builder.append(" listenerOrder=" + entry.order);
builder.append(" listenerIndex=" + index + "/" + length);
if (entry.listener instanceof ComponentWithName) {
builder.append(" listenerName=");
builder.append(((ComponentWithName) entry.listener).getComponentName());
}
builder.append("\n exception:\n");
builder.append(StringUtil.throwableToString(t));
final Set<Throwable> done = new HashSet<Throwable>();
done.add(t);
Throwable cause = t.getCause();
while (cause != null && !done.contains(cause)) {
done.add(cause);
builder.append("\n caused by:\n");
builder.append(StringUtil.throwableToString(cause));
cause = t.getCause();
}
// TODO: Add id information to compare to registry log (later).
StaticLog.logOnce(Streams.STATUS, Level.SEVERE,
"Listener exception: baseType=" + baseType.getName()
+ " basePriority=" + this.basePriority
+ " eventType=" + event.getClass().getName(),
builder.toString());
}
}

View File

@ -77,7 +77,7 @@ public abstract class MiniListenerRegistry<EB, P> {
protected NodeFactory<EB, P> nodeFactory = new NodeFactory<EB, P>() {
@Override
public <E extends EB> MiniListenerNode<E, P> newNode(Class<E> eventClass, P basePriority) {
return new MiniListenerNode<E, P>(basePriority);
return new MiniListenerNode<E, P>(eventClass, basePriority);
}
};

View File

@ -14,17 +14,21 @@
*/
package fr.neatmonster.nocheatplus.event.mini;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterEventsWithOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterMethodWithOrder;
import fr.neatmonster.nocheatplus.logging.Streams;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Level;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterEventsWithOrder;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterMethodWithOrder;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.logging.Streams;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
/**
* Support for registering multiple event-handler methods at once.<br>
@ -52,7 +56,74 @@ import fr.neatmonster.nocheatplus.logging.Streams;
* @param <P>
* Priority class, e.g. EventPriority for Bukkit.
*/
public abstract class MultiListenerRegistry<EB, P> extends MiniListenerRegistry<EB, P> {
public abstract class MultiListenerRegistry<EB, P> extends MiniListenerRegistry<EB, P> {
protected class AutoListener<E> implements MiniListenerWithOrder<E>, ComponentWithName {
private final Class<E> eventClass;
private final Object listener;
private final Method method;
private final RegistrationOrder order;
private final P basePriority;
private AutoListener(final Class<E> eventClass,
final Object listener, final Method method,
final RegistrationOrder order, final P basePriority) {
this.eventClass = eventClass;
this.listener = listener;
this.method = method;
this.order = order;
this.basePriority = basePriority;
}
@Override
public void onEvent(final E event) {
try {
method.invoke(listener, event);
}
catch (InvocationTargetException e) {
onException(event, e);
}
catch (IllegalArgumentException e) {
onException(event, e);
}
catch (IllegalAccessException e) {
onException(event, e);
}
}
private void onException(final E event, final Throwable t) {
final StringBuilder builder = new StringBuilder(1024);
builder.append("Exception:\n");
builder.append(StringUtil.throwableToString(t));
Throwable cause = t.getCause();
while (cause != null) {
builder.append("caused by:\n");
builder.append(StringUtil.throwableToString(t.getCause()));
cause = cause.getCause();
}
StaticLog.logOnce(Level.SEVERE,
"Exception with " + getComponentName() + ", processing " + event.getClass().getName() + ": " + t.getClass().getSimpleName(),
builder.toString());
}
@Override
public RegistrationOrder getRegistrationOrder() {
/*
* Return the given instance of RegistrationOrder, assuming
* it'll be copied upon registration. Typically the registry
* can't and shouldn't distinguish if this comes from an
* external source anyway.
*/
return order;
}
@Override
public String getComponentName() {
return "AutoListener(" + listener.getClass().getName() +"." + method.getName() + "/" + eventClass.getName() + "/" + basePriority + ")";
}
}
/**
*
@ -75,7 +146,7 @@ public abstract class MultiListenerRegistry<EB, P> extends MiniListenerRegistry<
if (order == null) {
order = defaultOrder;
}
MiniListener<E> miniListener = getMiniListener(listener, method, order);
MiniListener<E> miniListener = getMiniListener(listener, method, order, basePriority);
if (listener == null) {
// TODO: Throw rather.
return null;
@ -91,36 +162,11 @@ public abstract class MultiListenerRegistry<EB, P> extends MiniListenerRegistry<
* @param method
* @return
*/
@SuppressWarnings("unchecked")
protected <E extends EB> MiniListener<E> getMiniListener(final Object listener,
final Method method, final RegistrationOrder order) {
@SuppressWarnings({ "unchecked", "unused" })
Class<E> eventClass = (Class<E>) method.getParameterTypes()[0];
MiniListener<E> miniListener = new MiniListenerWithOrder<E>() {
@Override
public void onEvent(E event) {
try {
method.invoke(listener, event);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
@Override
public RegistrationOrder getRegistrationOrder() {
/*
* Return the given instance of RegistrationOrder, assuming
* it'll be copied upon registration. Typically the registry
* can't and shouldn't distinguish if this comes from an
* external source anyway.
*/
return order;
}
};
return miniListener;
final Method method, final RegistrationOrder order, final P basePriority) {
return new AutoListener<E>((Class<E>) method.getParameterTypes()[0],
listener, method, order, basePriority);
}
protected boolean check_and_prepare_method(final Method method) {

View File

@ -14,13 +14,12 @@
*/
package fr.neatmonster.nocheatplus.players;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
@ -40,26 +39,7 @@ public class DataManager {
*/
// TODO: Should/can some data structures share the same lock?
private static PlayerDataManager instance = null;
/**
* Clear all cached CheckConfig instances.<br>
* This does not cleanup ConfigManager, i.e. stored yml-versions.
*/
public static void clearConfigs() {
// TODO: general DataManager thing, otherwise it's now a WorldDataManager thing.
final Set<CheckConfigFactory> factories = new LinkedHashSet<CheckConfigFactory>();
for (final CheckType checkType : CheckType.values()) {
final CheckConfigFactory factory = checkType.getConfigFactory();
if (factory != null) {
factories.add(factory);
}
}
for (final CheckConfigFactory factory : factories) {
factory.removeAllConfigs();
}
}
static PlayerDataManager instance = null;
/**
* Get the exact player name, stored internally.
@ -108,11 +88,12 @@ public class DataManager {
/**
* Remove data and history of all players for the given check type and sub
* checks.
* checks. Also removes check related data from the world manager.
*
* @param checkType
*/
public static void clearData(final CheckType checkType) {
NCPAPIProvider.getNoCheatPlusAPI().getWorldDataManager().clearData(checkType);
instance.clearData(checkType);
}
@ -147,7 +128,7 @@ public class DataManager {
* Exact player name.
* @param checkType
* Check type to remove data for, null is regarded as ALL.
* @return If any data was present.
* @return If any data was present (not strict).
*/
public static boolean removeData(final String playerName, CheckType checkType) {
return instance.removeData(playerName, checkType);
@ -273,4 +254,9 @@ public class DataManager {
return instance.getPlayerData(player).getGenericInstance(registeredFor);
}
static <T> T getFromFactory(final Class<T> registeredFor,
final PlayerFactoryArgument arg) {
return instance.getNewInstance(registeredFor, arg);
}
}

View File

@ -14,6 +14,7 @@
*/
package fr.neatmonster.nocheatplus.players;
import java.util.Collection;
import java.util.UUID;
import org.bukkit.entity.Player;
@ -22,6 +23,7 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.checktype.IBaseDataAccess;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstance;
import fr.neatmonster.nocheatplus.hooks.ExemptionContext;
@ -269,6 +271,23 @@ public interface IPlayerData extends IData, IBaseDataAccess, IGetGenericInstance
*/
public <T> void removeGenericInstance(Class<T> registeredFor);
/**
* Remove all generic instances from cache, which are contained in the given
* collection.
*
* @param types
*/
public void removeAllGenericInstances(Collection<Class<?>> types);
/**
* Call dataOnRemoveSubCheckData(...).
*
* @param subCheckRemoval
*/
public void removeSubCheckData(
Collection<Class<? extends IDataOnRemoveSubCheckData>> subCheckRemoval,
Collection<CheckType> checkTypes);
/**
* Check if notifications are turned off, this does not bypass permission
* checks.

View File

@ -20,18 +20,44 @@ import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.registry.ComponentRegistry;
import fr.neatmonster.nocheatplus.components.registry.factory.IRichFactoryRegistry;
import fr.neatmonster.nocheatplus.components.registry.feature.IRemoveData;
/**
* Player data specific operations.
* <hr>
* <hr/>
* ComponentRegistry:
* <li>Supported: IRemoveData</li>
* <hr/>
* <b>Preset groups and automatic registration.</b><br/>
* All of the types mentioned in the lists below are preset for grouping. <br/>
* However only those item types for which a factory gets registered here will
* be automatically put into existing groups. External types like configuration
* instances fetched from IWorldData need to be registered explicitly. <br/>
* <br/>
* Automatic data type grouping with call support (return value controls removal
* of the entire data object):
* <ul>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnReload}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnJoin}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnLeave}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnWorldChange}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData}</li></li>
* </ul>
* <br/>
* Automatic data type grouping for direct removal (API/reload/commands):
* <ul>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IData}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.config.IConfig}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.ICheckData}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.config.ICheckConfig}</li>
* </ul>
*
* @author asofold
*
*/
public interface IPlayerDataManager extends ComponentRegistry<IRemoveData> {
public interface IPlayerDataManager extends ComponentRegistry<IRemoveData>, IRichFactoryRegistry<PlayerFactoryArgument> {
// TODO: Complete (...)
@ -139,6 +165,12 @@ public interface IPlayerDataManager extends ComponentRegistry<IRemoveData> {
*
* @param checkType
*/
public void clearData(final CheckType checkType);
public void clearData(CheckType checkType);
/**
* Convenience method to remove cached types that implement IConfig for all
* players. Types need to be registered (player data factory or explicitly).
*/
public void removeCachedConfigs();
}

View File

@ -230,7 +230,12 @@ public class PlayerCheckTypeTree extends CheckTypeTree<PlayerCheckTypeTreeNode>{
*/
@SuppressWarnings("unchecked")
void updateDebug(ConfigFile rawConfiguration) {
configFlagUpdate(rawConfiguration, true, accessDebug);
update(rawConfiguration, true, accessDebug);
}
@SuppressWarnings("unchecked")
void updateDebug() {
update(true, accessDebug);
}
/**
@ -258,10 +263,10 @@ public class PlayerCheckTypeTree extends CheckTypeTree<PlayerCheckTypeTreeNode>{
}
@SuppressWarnings("unchecked")
void overrideDebug(final ConfigFile rawConfiguration,
void overrideDebug(
final CheckType checkType, final AlmostBoolean active,
final OverrideType overrideType, final boolean overrideChildren) {
configFlagOverride(rawConfiguration, active, overrideType, overrideChildren, accessDebug);
override(active, overrideType, overrideChildren, accessDebug);
}
}

View File

@ -18,6 +18,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@ -34,6 +35,13 @@ import fr.neatmonster.nocheatplus.checks.moving.util.MovingUtil;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.components.data.ICanHandleTimeRunningBackwards;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.data.IDataOnJoin;
import fr.neatmonster.nocheatplus.components.data.IDataOnLeave;
import fr.neatmonster.nocheatplus.components.data.IDataOnReload;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldChange;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload;
import fr.neatmonster.nocheatplus.hooks.ExemptionContext;
import fr.neatmonster.nocheatplus.hooks.NCPExemptionManager;
import fr.neatmonster.nocheatplus.permissions.PermissionInfo;
@ -45,6 +53,7 @@ import fr.neatmonster.nocheatplus.utilities.TickTask;
import fr.neatmonster.nocheatplus.utilities.ds.corw.DualSet;
import fr.neatmonster.nocheatplus.utilities.ds.count.ActionFrequency;
import fr.neatmonster.nocheatplus.utilities.ds.map.HashMapLOW;
import fr.neatmonster.nocheatplus.utilities.ds.map.InstanceMapLOW;
import fr.neatmonster.nocheatplus.worlds.IWorldData;
import fr.neatmonster.nocheatplus.worlds.WorldDataManager;
import fr.neatmonster.nocheatplus.worlds.WorldIdentifier;
@ -85,7 +94,7 @@ import fr.neatmonster.nocheatplus.worlds.WorldIdentifier;
* @author asofold
*
*/
public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
public class PlayerData implements IPlayerData {
// TODO: IPlayerData for the more official API.
@ -153,6 +162,8 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
/** Permission cache. */
private final HashMapLOW<Integer, PermissionNode> permissions = new HashMapLOW<Integer, PermissionNode>(lock, 35);
// TODO: a per entry typed variant (key - value relation)?
private final InstanceMapLOW dataCache = new InstanceMapLOW(lock, 24);
private boolean requestUpdateInventory = false;
private boolean requestPlayerSetBack = false;
@ -370,8 +381,8 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
}
}
@Override
public void handleTimeRanBackwards() {
public void handleTimeRanBackwards(final Collection<Class<? extends IData>> dataTypes) {
// Permissions.
final Iterator<Entry<Integer, PermissionNode>> it = permissions.iterator();
final long timeNow = System.currentTimeMillis();
while (it.hasNext()) {
@ -380,14 +391,20 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
case INTERVAL:
node.invalidate();
break;
case ONCE:
node.setState(node.getLastState(), timeNow);
break;
default:
// Ignore.
if (node.getLastFetch() > timeNow) {
node.setState(node.getLastState(), timeNow);
}
break;
}
}
// TODO: Register explicitly or not? (+ auto register?)...
for (final Class<? extends IData> type : dataTypes) {
final IData obj = dataCache.get(type);
if (obj != null && obj instanceof ICanHandleTimeRunningBackwards) {
((ICanHandleTimeRunningBackwards) obj).handleTimeRanBackwards();
}
}
}
void requestPermissionUpdatePrimaryThread(final RegisteredPermission registeredPermission) {
@ -421,7 +438,16 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
registerFrequentPlayerTaskAsynchronous();
}
void onPlayerLeave(final long timeNow) {
void onPlayerLeave(final Player player, final long timeNow,
Collection<Class<? extends IDataOnLeave>> types) {
// (Might collect to be removed types first.)
for (final Class<? extends IDataOnLeave> type : types) {
final IDataOnLeave instance = dataCache.get(type);
if (instance != null && instance.dataOnLeave(player, this)) {
dataCache.remove(type);
}
}
// (Somewhat reversed order of invalidation.)
invalidateOffline();
}
@ -430,12 +456,20 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
*
* @param world
* @param timeNow
* @param types
*/
void onPlayerJoin(final World world, final long timeNow,
final WorldDataManager worldDataManager) {
void onPlayerJoin(final Player player, final World world,
final long timeNow, final WorldDataManager worldDataManager,
final Collection<Class<? extends IDataOnJoin>> types) {
// Only update world if the data hasn't just been created.
updateCurrentWorld(world, worldDataManager);
invalidateOffline();
for (final Class<? extends IDataOnJoin> type : types) {
final IDataOnJoin instance = dataCache.get(type);
if (instance != null && instance.dataOnJoin(player, this)) {
dataCache.remove(type);
}
}
requestLazyPermissionUpdate(permissionRegistry.getPreferKeepUpdatedOffline());
lastJoinTime = timeNow;
}
@ -481,9 +515,12 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
*
* @param oldWorld
* @param newWorld
* @param types
*/
void onPlayerChangedWorld(final World oldWorld, final World newWorld,
final WorldDataManager worldDataManager) {
void onPlayerChangedWorld(final Player player,
final World oldWorld, final World newWorld,
final WorldDataManager worldDataManager,
final Collection<Class<? extends IDataOnWorldChange>> types) {
updateCurrentWorld(newWorld, worldDataManager);
// TODO: Double-invalidation (previous policy and target world policy)
final Iterator<Entry<Integer, PermissionNode>> it = permissions.iterator();
@ -496,18 +533,10 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
}
}
requestLazyPermissionUpdate(permissionRegistry.getPreferKeepUpdatedWorld());
}
/**
* Called with adjusting to the configuration (enable / config reload).
* @param changedPermissions
*/
public void adjustSettings(final Set<RegisteredPermission> changedPermissions) {
final Iterator<RegisteredPermission> it = changedPermissions.iterator();
while (it.hasNext()) {
final PermissionNode node = permissions.get(it.next().getId());
if (node != null) {
node.invalidate();
for (final Class<? extends IDataOnWorldChange> type : types) {
final IDataOnWorldChange instance = dataCache.get(type);
if (instance != null && instance.dataOnWorldChange(player, this, oldWorld, newWorld)) {
dataCache.remove(type);
}
}
}
@ -588,6 +617,7 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
permissions.clear(); // Might keep login-related permissions. Implement a 'retain-xy' or 'essential' flag?
updatePermissions.clearPrimaryThread();
updatePermissionsLazy.clearPrimaryThread();
dataCache.clear();
}
@Override
@ -733,7 +763,6 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
public void overrideDebug(final CheckType checkType, final AlmostBoolean active,
final OverrideType overrideType, final boolean overrideChildren) {
this.checkTypeTree.getNode(CheckType.ALL).overrideDebug(
getCurrentWorldDataSafe().getRawConfiguration(),
checkType, active, overrideType, overrideChildren);
}
@ -818,19 +847,112 @@ public class PlayerData implements IPlayerData, ICanHandleTimeRunningBackwards {
&& (frequentPlayerTaskShouldBeScheduled || isFrequentPlayerTaskScheduled());
}
/**
* Get a data/config instance (1.local cache, 2. player related factory, 3.
* world registry).
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getGenericInstance(Class<T> registeredFor) {
// TODO: 1. Check for cache (local).
// TODO: 2. Check for registered factory (local)
public <T> T getGenericInstance(final Class<T> registeredFor) {
// 1. Check for cache (local).
final Object res = dataCache.get(registeredFor);
if (res == null) {
/*
* TODO: Consider storing null and check containsKey(registeredFor)
* here. On the other hand it's not intended to query non existent
* data (just yet).
*/
return cacheMissGenericInstance(registeredFor);
}
else {
return (T) res;
}
}
private <T> T cacheMissGenericInstance(final Class<T> registeredFor) {
// 2. Check for registered factory (local)
// TODO: Might store PlayerDataManager here.
T res = DataManager.getFromFactory(registeredFor,
new PlayerFactoryArgument(this, getCurrentWorldDataSafe()));
if (res != null) {
return putDataCache(registeredFor, res);
}
// 3. Check proxy registry.
// TODO: Explicit registration for proxy registry needed (PlayerDataManager).
// TODO: Store these in the local cache too (override/replace on world change etc registered too).
return getCurrentWorldDataSafe().getGenericInstance(registeredFor);
res = getCurrentWorldDataSafe().getGenericInstance(registeredFor);
return res == null ? null : putDataCache(registeredFor, res);
}
private <T> T putDataCache(final Class<T> registeredFor, final T instance) {
final T previousInstance = (T) dataCache.putIfAbsent(registeredFor, instance); // Under lock.
return previousInstance == null ? instance : previousInstance;
}
/**
* Remove from cache.
*/
@Override
public <T> void removeGenericInstance(final Class<T> type) {
dataCache.remove(type);
}
@Override
public <T> void removeGenericInstance(Class<T> registeredFor) {
// TODO: implement
public void removeAllGenericInstances(final Collection<Class<?>> types) {
if (dataCache.isEmpty()) {
return;
}
dataCache.remove(types);
}
@Override
public void removeSubCheckData(
final Collection<Class<? extends IDataOnRemoveSubCheckData>> types,
final Collection<CheckType> checkTypes
) {
final Collection<Class<?>> removeTypes = new LinkedList<Class<?>>();
for (final Class<? extends IDataOnRemoveSubCheckData> type : types) {
final IDataOnRemoveSubCheckData impl = (IDataOnRemoveSubCheckData) dataCache.get(type);
if (impl != null) {
if (impl.dataOnRemoveSubCheckData(checkTypes)) {
removeTypes.add(type);
}
}
}
if (!removeTypes.isEmpty()) {
dataCache.remove(removeTypes);
}
}
/**
* Called with adjusting to the configuration (enable / config reload).
* @param changedPermissions
*/
public void adjustSettings(final Set<RegisteredPermission> changedPermissions) {
final Iterator<RegisteredPermission> it = changedPermissions.iterator();
while (it.hasNext()) {
final PermissionNode node = permissions.get(it.next().getId());
if (node != null) {
node.invalidate();
}
}
}
public void onWorldUnload(final World world,
final Collection<Class<? extends IDataOnWorldUnload>> types) {
for (final Class<? extends IDataOnWorldUnload> type : types) {
final IDataOnWorldUnload instance = dataCache.get(type);
if (instance != null && instance.dataOnWorldUnload(world, this)) {
dataCache.remove(type);
}
}
}
public void onReload(final Collection<Class<? extends IDataOnReload>> types) {
for (final Class<? extends IDataOnReload> type : types) {
final IDataOnReload instance = dataCache.get(type);
if (instance != null && instance.dataOnReload(this)) {
dataCache.remove(type);
}
}
}
}

View File

@ -15,8 +15,8 @@
package fr.neatmonster.nocheatplus.players;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@ -37,21 +37,31 @@ import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ViolationHistory;
import fr.neatmonster.nocheatplus.checks.access.ICheckData;
import fr.neatmonster.nocheatplus.checks.access.IRemoveSubCheckData;
import fr.neatmonster.nocheatplus.checks.combined.CombinedData;
import fr.neatmonster.nocheatplus.compat.BridgeMisc;
import fr.neatmonster.nocheatplus.compat.versions.BukkitVersion;
import fr.neatmonster.nocheatplus.compat.versions.GenericVersion;
import fr.neatmonster.nocheatplus.compat.versions.ServerVersion;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.config.ICheckConfig;
import fr.neatmonster.nocheatplus.components.config.IConfig;
import fr.neatmonster.nocheatplus.components.data.ICanHandleTimeRunningBackwards;
import fr.neatmonster.nocheatplus.components.registry.FactoryOneRegistry;
import fr.neatmonster.nocheatplus.components.registry.TypeSetRegistry;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.data.IDataOnJoin;
import fr.neatmonster.nocheatplus.components.data.IDataOnLeave;
import fr.neatmonster.nocheatplus.components.data.IDataOnReload;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldChange;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.factory.RichFactoryRegistry;
import fr.neatmonster.nocheatplus.components.registry.factory.RichFactoryRegistry.CheckRemovalSpec;
import fr.neatmonster.nocheatplus.components.registry.feature.ComponentWithName;
import fr.neatmonster.nocheatplus.components.registry.feature.ConsistencyChecker;
import fr.neatmonster.nocheatplus.components.registry.feature.IDisableListener;
@ -90,6 +100,7 @@ import fr.neatmonster.nocheatplus.worlds.WorldDataManager;
*
*/
// TODO: RegisterWithOrder still relevant ?
// TODO: Tag utility (common stuff).
@RegisterWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = "(^feature.*)", basePriority = "-80")
public class PlayerDataManager implements IPlayerDataManager, ComponentWithName, INeedConfig, ConsistencyChecker, IDisableListener {
@ -163,14 +174,7 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
private final Lock lock = new ReentrantLock();
// TODO: Consider same lock for some registry parts (deadlocking possibilities with exposed API).
private final FactoryOneRegistry<PlayerFactoryArgument> factoryRegistry = new FactoryOneRegistry<PlayerFactoryArgument>(
lock, CheckUtils.primaryServerThreadContextTester);
/**
* Grouped types to have a faster way of iterating data types stored in the
* PlayerData cache.
*/
private final TypeSetRegistry groupedTypes = new TypeSetRegistry(lock);
private final RichFactoryRegistry<PlayerFactoryArgument> factoryRegistry = new RichFactoryRegistry<PlayerFactoryArgument>(lock);
private final TickListener tickListener = new TickListener() {
private int delayRareTasks = 0;
@ -198,16 +202,19 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
* nocheatplus.system.data.player...). (RegistryTags for other?).
*/
new MiniListener<PlayerQuitEvent>() {
@Override
@EventHandler(priority = EventPriority.MONITOR)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", afterTag = ".*")
@Override
public void onEvent(final PlayerQuitEvent event) {
playerLeaves(event.getPlayer());
}
},
new MiniListener<PlayerKickEvent>() {
// TODO: ignoreCancelled !?
// TODO: afterTag !?
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", afterTag = ".*")
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", afterTag = "feature.*")
@Override
public void onEvent(final PlayerKickEvent event) {
playerLeaves(event.getPlayer());
}
@ -215,23 +222,34 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
new MiniListener<PlayerJoinEvent>() {
@EventHandler(priority = EventPriority.LOWEST)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = ".*")
@Override
public void onEvent(final PlayerJoinEvent event) {
playerJoins(event);
}
},
new MiniListener<PlayerChangedWorldEvent>() {
@EventHandler(priority = EventPriority.LOWEST)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", afterTag = ".*")
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = ".*")
@Override
public void onEvent(final PlayerChangedWorldEvent event) {
playerChangedWorld(event);
}
},
new MiniListener<WorldUnloadEvent>() {
@EventHandler(priority = EventPriority.LOWEST)
@RegisterMethodWithOrder(tag = "system.nocheatplus.datamanager", beforeTag = ".*")
@Override
public void onEvent(final WorldUnloadEvent event) {
onWorldUnload(event);
}
},
};
/**
* Sets the static instance reference.
* @param worldDataManager
*
* @param worldDataManager
* @param permissionRegistry
*/
public PlayerDataManager(final WorldDataManager worldDataManager, final PermissionRegistry permissionRegistry) {
DataManager.instance = this; // TODO: Let NoCheatPlus do this, DataManager returns an ILockable.
@ -248,8 +266,20 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
// Likely an older version without efficient mapping.
playerMap = new PlayerMap(true);
}
this.permissionRegistry = permissionRegistry;
this.permissionRegistry = permissionRegistry; // TODO: World specific.
this.worldDataManager = worldDataManager;
// (Call support.)
factoryRegistry.createAutoGroup(IDataOnReload.class);
factoryRegistry.createAutoGroup(IDataOnWorldUnload.class);
factoryRegistry.createAutoGroup(IDataOnJoin.class);
factoryRegistry.createAutoGroup(IDataOnLeave.class);
factoryRegistry.createAutoGroup(IDataOnWorldChange.class);
factoryRegistry.createAutoGroup(IDataOnRemoveSubCheckData.class);
// Data/config removal.
factoryRegistry.createAutoGroup(IData.class);
factoryRegistry.createAutoGroup(IConfig.class);
factoryRegistry.createAutoGroup(ICheckData.class);
factoryRegistry.createAutoGroup(ICheckConfig.class);
}
/**
@ -262,18 +292,18 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
return;
}
final long now = System.currentTimeMillis();
final Set<CheckDataFactory> factories = new LinkedHashSet<CheckDataFactory>();
final Set<Entry<UUID, Long>> entries = lastLogout.entrySet();
final Iterator<Entry<UUID, Long>> iterator = entries.iterator();
while (iterator.hasNext()) {
final Entry<UUID, Long> entry = iterator.next();
// TODO: Multi stage expiration.
final long ts = entry.getValue();
if (now - ts <= durExpireData) {
break;
}
final UUID playerId = entry.getKey();
// TODO: LEGACY handling: switch to UUIDs here for sure.
legacyPlayerDataExpirationRemovalByName(playerId, factories, deleteData);
legacyPlayerDataExpirationRemovalByName(playerId, deleteData);
bulkPlayerDataRemoval.add(playerId); // For bulk removal.
iterator.remove();
}
@ -284,7 +314,7 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
}
private final void legacyPlayerDataExpirationRemovalByName(final UUID playerId,
final Set<CheckDataFactory> factories, final boolean deleteData) {
final boolean deleteData) {
final String playerName = DataManager.getPlayerName(playerId);
if (playerName == null) {
// TODO: WARN
@ -292,15 +322,9 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
}
// TODO: Validity of name?
if (deleteData) {
factories.clear();
for (final CheckType type : CheckType.values()) {
final CheckDataFactory factory = type.getDataFactory();
if (factory != null) {
factories.add(factory);
}
}
for (final CheckDataFactory factory : factories) {
factory.removeData(playerName);
final PlayerData pData = playerData.get(playerId);
if (pData != null) {
pData.removeData(false); // TODO: staged ...
}
clearComponentData(CheckType.ALL, playerName);
}
@ -382,107 +406,70 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
}
/**
* Remove the player data for a given player and a given check type.
* CheckType.ALL and null will be interpreted as removing all data.<br>
* Remove data instances from the cache for a given player and a given check
* type. CheckType.ALL and null will be interpreted as removing all data.
* <hr/>
* Does not touch the execution history.
* <hr/>
*
* @param playerName
* Exact player name.
* @param checkType
* Check type to remove data for, null is regarded as ALL.
* @return If any data was present.
* @return If any data was present (not strict).
*/
public boolean removeData(final String playerName, CheckType checkType) {
final PlayerData pd = getPlayerData(playerName);
PlayerData pData = getPlayerData(playerName);
// TODO: Once working, use the most correct name from PlayerData.
final UUID playerId = pd == null ? getUUID(playerName) : pd.getPlayerId();
final UUID playerId = pData == null ? getUUID(playerName) : pData.getPlayerId();
if (pData == null && playerId != null) {
pData = playerData.get(playerId);
}
boolean somethingFound = pData != null || playerId != null;
// TODO: Method signature with UUID / (I)PlayerData ?
if (checkType == null) {
checkType = CheckType.ALL;
}
boolean had = false;
// Check extended registered components.
// TODO: "System data" might not be wise to erase for online players.
if (clearComponentData(checkType, playerName)) {
had = true;
}
/*
* TODO: "System data" might not be wise to erase for online players.
* ICheckData vs IData (...), except if registered for per check
* type removal.
*/
somethingFound |= clearComponentData(checkType, playerName);
// Collect factories.
final Set<CheckDataFactory> factories = new HashSet<CheckDataFactory>();
for (CheckType otherType : CheckTypeUtil.getWithDescendants(checkType)) {
final CheckDataFactory otherFactory = otherType.getDataFactory();
if (otherFactory != null) {
factories.add(otherFactory);
}
}
// Remove data.
for (final CheckDataFactory factory : factories) {
if (removeDataPrecisely(playerId, playerName, checkType, factory)) {
had = true;
}
}
// TODO: Multi stage removal, other API
// TODO: Maintain a shouldBeOnline flag for fast skipping?
if (pd != null && checkType == CheckType.ALL) {
// TODO: Fetch/use UUID early, and check validity of name.
if (playerId != null) {
bulkPlayerDataRemoval.add(playerId);
}
}
return had;
}
/**
* Attempt to only remove the data, relevant to the given CheckType.
*
* @param playerId
* @param playerName
* @param checkType
* @param factory
* @return If any data has been removed.
*/
private boolean removeDataPrecisely(final UUID playerId, final String playerName,
final CheckType checkType, final CheckDataFactory factory) {
// TODO: Use PlayerData if present.
final ICheckData data = factory.getDataIfPresent(playerId, playerName);
if (data == null) {
return false;
}
else {
// Attempt precise removal.
final boolean debug = data.getDebug();
String debugText = debug ? "[" + checkType + "] [" + playerName + "] Data removal: " : null;
boolean res = false;
if (data instanceof IRemoveSubCheckData
&& ((IRemoveSubCheckData) data).removeSubCheckData(checkType)) {
if (debug) {
debugText += "Removed (sub) check data, keeping the data object.";
if (pData != null) {
final CheckRemovalSpec removalSpec = new CheckRemovalSpec(checkType, true, this);
final boolean hasComplete = !removalSpec.completeRemoval.isEmpty();
final boolean hasSub = !removalSpec.subCheckRemoval.isEmpty();
if (hasComplete || hasSub) {
if (hasComplete) {
pData.removeAllGenericInstances(removalSpec.completeRemoval);
}
res = true;
}
else {
// Just remove.
if (factory.removeData(playerName) == null) {
// Is this even possible?
if (debug) {
debugText += "Could not remove data, despite present!";
}
if (hasSub) {
pData.removeSubCheckData(removalSpec.subCheckRemoval, removalSpec.checkTypes);
}
else {
if (debug) {
debugText += "Removed the entire data object.";
}
res = true;
// TODO: Remove the PlayerData instance, if necessary?
}
// TODO: Maintain a shouldBeOnline flag for fast skipping?
if (checkType == CheckType.ALL) {
// TODO: Fetch/use UUID early, and check validity of name.
if (playerId != null) {
bulkPlayerDataRemoval.add(playerId);
}
}
if (debug) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.TRACE_FILE, debugText);
if (pData.isDebugActive(checkType)) {
NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(
Streams.TRACE_FILE,
CheckUtils.getLogMessagePrefix(playerName, checkType)
+ "Removed data.");
}
return res;
}
return somethingFound;
}
/**
@ -534,7 +521,6 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
clearData(CheckType.ALL);
playerData.clear(); // Also clear for online players.
iRemoveData.clear();
DataManager.clearConfigs(); // TODO: Cleaning up the WorldDataManager is up to the WorldDataManager.
lastLogout.clear();
executionHistories.clear();
playerMap.clear();
@ -545,6 +531,13 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
}
}
public void onWorldUnload(final WorldUnloadEvent event) {
final Collection<Class<? extends IDataOnWorldUnload>> types = factoryRegistry.getGroupedTypes(IDataOnWorldUnload.class);
for (final Entry<UUID, PlayerData> entry : playerData.iterable()) {
entry.getValue().onWorldUnload(event.getWorld(), types);
}
}
/**
* Add mappings for player names variations.
* @param player
@ -575,7 +568,8 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
* TODO: Now we update the world already with getPlayerData, in case
* it's just been created...
*/
pData.onPlayerJoin(player.getWorld(), timeNow, worldDataManager);
final Collection<Class<? extends IDataOnJoin>> types = factoryRegistry.getGroupedTypes(IDataOnJoin.class);
pData.onPlayerJoin(player, player.getWorld(), timeNow, worldDataManager, types);
pData.getGenericInstance(CombinedData.class).lastJoinTime = timeNow;
}
@ -589,17 +583,22 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
lastLogout.put(playerId, timeNow);
final PlayerData pData = playerData.get(playerId);
if (pData != null) {
pData.onPlayerLeave(timeNow);
final Collection<Class<? extends IDataOnLeave>> types = factoryRegistry.getGroupedTypes(IDataOnLeave.class);
pData.onPlayerLeave(player, timeNow, types);
pData.getGenericInstance(CombinedData.class).lastLogoutTime = timeNow;
}
else {
// TODO: put lastLogoutTime to OfflinePlayerData ?
}
// TODO: put lastLogoutTime to PlayerData !
pData.getGenericInstance(CombinedData.class).lastLogoutTime = timeNow;
removeOnlinePlayer(player);
}
private void playerChangedWorld(final PlayerChangedWorldEvent event) {
final Player player = event.getPlayer();
final PlayerData pData = getPlayerData(player, true);
pData.onPlayerChangedWorld(event.getFrom(), player.getWorld(), worldDataManager);
final Collection<Class<? extends IDataOnWorldChange>> types = factoryRegistry.getGroupedTypes(IDataOnWorldChange.class);
pData.onPlayerChangedWorld(player, event.getFrom(), player.getWorld(),
worldDataManager, types);
}
/**
@ -633,6 +632,10 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
public void onReload() {
// present.
adjustSettings();
final Collection<Class<? extends IDataOnReload>> types = factoryRegistry.getGroupedTypes(IDataOnReload.class);
for (final Entry<UUID, PlayerData> entry : playerData.iterable()) {
entry.getValue().onReload(types);
}
}
@Override
@ -748,25 +751,13 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
* which implement this.
*/
public void handleSystemTimeRanBackwards() {
// TODO: WorldDataManager should have an extra method and be called before this.
// Collect data factories and clear execution history.
final Set<CheckDataFactory> factories = new HashSet<CheckDataFactory>();
for (final CheckType type : CheckTypeUtil.getWithDescendants(CheckType.ALL)) {
final Map<String, ExecutionHistory> map = executionHistories.get(type);
if (map != null) {
map.clear();
}
final CheckDataFactory factory = type.getDataFactory();
if (factory != null) {
factories.add(factory);
}
}
for (final CheckDataFactory factory : factories) {
if (factory instanceof ICanHandleTimeRunningBackwards) {
((ICanHandleTimeRunningBackwards) factory).handleTimeRanBackwards();
}
else {
factory.removeAllData();
}
}
for (final IRemoveData rmd : iRemoveData) {
if (rmd instanceof ICanHandleTimeRunningBackwards) {
@ -778,8 +769,10 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
}
ViolationHistory.clear(CheckType.ALL);
// PlayerData
// TODO: Register explicitly (adding IDataOnTimeRanBackwards)?
Collection<Class<? extends IData>> dataTypes = factoryRegistry.getGroupedTypes(IData.class);
for (final Entry<UUID, PlayerData> entry : playerData.iterable()){
entry.getValue().handleTimeRanBackwards();
entry.getValue().handleTimeRanBackwards(dataTypes);
}
}
@ -905,22 +898,22 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
@Override
public void clearData(final CheckType checkType) {
// TODO: WorldDataManager: clear player related data there (registered in PlayerDataManager!?).
final Set<CheckDataFactory> factories = new HashSet<CheckDataFactory>();
for (final CheckType type : CheckTypeUtil.getWithDescendants(checkType)) {
final Map<String, ExecutionHistory> map = executionHistories.get(type);
if (map != null) {
map.clear();
}
final CheckDataFactory factory = type.getDataFactory();
if (factory != null) {
factories.add(factory);
final CheckRemovalSpec removalSpec = new CheckRemovalSpec(checkType, true, this);
final boolean hasComplete = !removalSpec.completeRemoval.isEmpty();
final boolean hasSub = !removalSpec.subCheckRemoval.isEmpty();
if (hasComplete || hasSub) {
for (final Entry<UUID, PlayerData> entry : playerData.iterable()) {
final IPlayerData pData = entry.getValue();
if (hasComplete) {
pData.removeAllGenericInstances(removalSpec.completeRemoval);
}
if (hasSub) {
pData.removeSubCheckData(removalSpec.subCheckRemoval, removalSpec.checkTypes);
}
// TODO: Remove the PlayerData instance, if suitable?
}
}
for (final CheckDataFactory factory : factories) {
// TODO: Support precise removal ?
factory.removeAllData();
}
// TODO: IRemoveData - why register here at all ?
for (final IRemoveData rmd : iRemoveData) {
if (checkType == CheckType.ALL) {
// Not sure this is really good, though.
@ -933,7 +926,14 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
}
}
}
for (final CheckType type : removalSpec.checkTypes) {
final Map<String, ExecutionHistory> map = executionHistories.get(type);
if (map != null) {
map.clear();
}
}
ViolationHistory.clear(checkType);
// TODO: PlayerData removal should have other mechanisms (stages).
if (checkType == CheckType.ALL) {
bulkPlayerDataRemoval.addAll(playerData.getKeys());
doBulkPlayerDataRemoval(); // Only removes offline player data.
@ -989,4 +989,83 @@ public class PlayerDataManager implements IPlayerDataManager, ComponentWithName
return removed;
}
@Override
public <T> void registerFactory(final Class<T> registerFor,
final IFactoryOne<PlayerFactoryArgument, T> factory) {
factoryRegistry.registerFactory(registerFor, factory);
}
@Override
public <T> Collection<Class<? extends T>> getGroupedTypes(final Class<T> groupType) {
return factoryRegistry.getGroupedTypes(groupType);
}
@Override
public <T> Collection<Class<? extends T>> getGroupedTypes(final Class<T> groupType,
final CheckType checkType) {
return factoryRegistry.getGroupedTypes(groupType, checkType);
}
@Override
public <I> void addToGroups(final Class<I> itemType,
final Class<? super I>... groupTypes) {
factoryRegistry.addToGroups(itemType, groupTypes);
}
@Override
public <I> void addToGroups(CheckType checkType, Class<I> itemType,
Class<? super I>... groupTypes) {
factoryRegistry.addToGroups(checkType, itemType, groupTypes);
}
@Override
public void addToExistingGroups(Class<?> itemType) {
factoryRegistry.addToExistingGroups(itemType);
}
@Override
public <I> void addToExistingGroups(final CheckType checkType,
final Class<I> itemType) {
factoryRegistry.addToExistingGroups(checkType, itemType);
}
@Override
public <G> void createGroup(final Class<G> groupType) {
factoryRegistry.createGroup(groupType);
}
@Override
public <G> void createAutoGroup(final Class<G> groupType) {
factoryRegistry.createAutoGroup(groupType);
}
@Override
public <I> void addToGroups(final Collection<CheckType> checkTypes,
final Class<I> itemType, final Class<? super I>... groupTypes) {
factoryRegistry.addToGroups(checkTypes, itemType, groupTypes);
}
@Override
public <I> void addToExistingGroups(final Collection<CheckType> checkTypes,
final Class<I> itemType) {
factoryRegistry.addToExistingGroups(checkTypes, itemType);
}
@Override
public <T> T getNewInstance(final Class<T> registeredFor,
final PlayerFactoryArgument arg) {
return factoryRegistry.getNewInstance(registeredFor, arg);
}
@Override
public void removeCachedConfigs() {
final Collection<Class<?>> types = new LinkedHashSet<Class<?>>(
factoryRegistry.getGroupedTypes(IConfig.class));
if (!types.isEmpty()) {
for (final Entry<UUID, PlayerData> entry : playerData.iterable()) {
entry.getValue().removeAllGenericInstances(types);
}
}
}
}

View File

@ -157,9 +157,22 @@ public class CheckUtils {
* @return the log message prefix
*/
public static String getLogMessagePrefix(final Player player, final CheckType checkType) {
return getLogMessagePrefix(player == null ? null : player.getName(), checkType);
}
/**
* Get the standard log message prefix with a trailing space.
*
* @param playerName
* May be null.
* @param checkType
* the check type
* @return the log message prefix
*/
public static String getLogMessagePrefix(final String playerName, final CheckType checkType) {
String base = "[" + checkType + "] ";
if (player != null) {
base += "[" + player.getName() + "] ";
if (playerName != null) {
base += "[" + playerName + "] ";
}
return base;
}

View File

@ -14,10 +14,12 @@
*/
package fr.neatmonster.nocheatplus.worlds;
import java.util.Collection;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.checktype.IConfigDataAccess;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstance;
import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstanceHandle;
/**
* Public access interface for per-world data.
@ -25,7 +27,7 @@ import fr.neatmonster.nocheatplus.components.registry.IGetGenericInstanceHandle;
* @author asofold
*
*/
public interface IWorldData extends IConfigDataAccess, IGetGenericInstance, IGetGenericInstanceHandle {
public interface IWorldData extends IConfigDataAccess, IGetGenericInstance {
/**
@ -59,7 +61,30 @@ public interface IWorldData extends IConfigDataAccess, IGetGenericInstance, IGet
*/
public boolean shouldAdjustToLag(CheckType checkType);
// TODO: Generic data (includes config) storage.
/**
* Remove data from the cache (not from underlying factories, nor from per
* world storage.
*
* @param registeredFor
*/
public <T> void removeGenericInstance(Class<T> registeredFor);
/**
* Remove all generic instances from cache, which are contained in the given
* collection.
*
* @param types
*/
public void removeAllGenericInstances(Collection<Class<?>> types);
/**
* Call dataOnRemoveSubCheckData(...).
*
* @param subCheckRemoval
*/
public void removeSubCheckData(
Collection<Class<? extends IDataOnRemoveSubCheckData>> subCheckRemoval,
Collection<CheckType> checkTypes);
// TODO: isDebugActive(CheckType checkType);

View File

@ -23,8 +23,36 @@ import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.components.registry.factory.IRichFactoryRegistry;
public interface IWorldDataManager {
/**
*
* <hr/>
* <b>Preset groups and automatic registration.</b><br/>
* All of the types mentioned in the lists below are preset for grouping. <br/>
* However only those item types for which a factory gets registered here will
* be automatically put into existing groups. External types like configuration
* instances fetched from IWorldData need to be registered explicitly. <br/>
* <br/>
* Automatic data type grouping with call support (return value controls removal
* of the entire data object):
* <ul>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnReload}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload}</li>
* </ul>
* <br/>
* Automatic data type grouping for direct removal (API/reload/commands):
* <ul>
* <li>{@link fr.neatmonster.nocheatplus.components.data.IData}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.config.IConfig}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.data.ICheckData}</li>
* <li>{@link fr.neatmonster.nocheatplus.components.config.ICheckConfig}</li>
* </ul>
*
* @author asofold
*
*/
public interface IWorldDataManager extends IRichFactoryRegistry<WorldFactoryArgument> {
/**
* Get the default world data, which applies for all worlds for which no
@ -140,4 +168,18 @@ public interface IWorldDataManager {
*/
public IWorldData getWorldDataSafe(Player player);
/**
* Remove data for all worlds for the given check type and sub checks.
*
* @param checkType
*/
public void clearData(CheckType checkType);
/**
* Convenience method to remove cached types that implement IConfig for all
* worlds. Types need to be registered (world data factory or explicitly).
*/
public void removeCachedConfigs();
}

View File

@ -16,6 +16,9 @@ package fr.neatmonster.nocheatplus.worlds;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.bukkit.World;
@ -23,13 +26,15 @@ import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.components.config.value.AlmostBooleanWithOverride;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.components.data.IDataOnReload;
import fr.neatmonster.nocheatplus.components.data.IDataOnRemoveSubCheckData;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload;
import fr.neatmonster.nocheatplus.components.data.checktype.CheckNodeWithDebug;
import fr.neatmonster.nocheatplus.components.data.checktype.CheckTypeTree;
import fr.neatmonster.nocheatplus.components.data.checktype.CheckTypeTree.CheckTypeTreeNodeFactory;
import fr.neatmonster.nocheatplus.components.registry.DefaultGenericInstanceRegistry;
import fr.neatmonster.nocheatplus.components.registry.GenericInstanceRegistry;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOneRegistry;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.utilities.ds.map.InstanceMapLOW;
/**
* Data stored per world.
@ -137,6 +142,9 @@ public class WorldData implements IWorldData {
if (configDebug.setValue(parentNode.configDebug.getValue(), configOverrideType)) {
debug = parentNode.debug;
}
if (configLag.setValue(parentNode.configLag.getValue(), configOverrideType)) {
lag = parentNode.lag;
}
}
/**
@ -145,24 +153,40 @@ public class WorldData implements IWorldData {
* @param rawConfiguration
*/
void update(final ConfigFile rawConfiguration) {
// TODO: A multi update method walking all nodes only once.
// TODO: A multi update method walking all nodes only once?
updateActivation(rawConfiguration, true);
updateDebug(rawConfiguration, true);
updateLag(rawConfiguration, true);
// TODO: contained configurations.
}
@SuppressWarnings("unchecked")
private void updateLag(final ConfigFile rawConfiguration,
final boolean forceUpdateChildren) {
configFlagUpdate(rawConfiguration, forceUpdateChildren, accessLag);
/**
* Update to activation states.
*/
void update() {
// TODO: A multi update method walking all nodes only once?
updateActivation(true);
updateDebug(true);
updateLag(true);
// TODO: contained configurations.
}
@SuppressWarnings("unchecked")
void overrideCheckActivation(final ConfigFile rawConfiguration,
private void updateLag(final ConfigFile rawConfiguration,
final boolean forceUpdateChildren) {
update(rawConfiguration, forceUpdateChildren, accessLag);
}
@SuppressWarnings("unchecked")
private void updateLag(final boolean forceUpdateChildren) {
update(forceUpdateChildren, accessLag);
}
@SuppressWarnings("unchecked")
void overrideCheckActivation(
final AlmostBoolean active, final OverrideType overrideType,
final boolean overrideChildren) {
configFlagOverride(rawConfiguration, active,
override(active,
overrideType, overrideChildren, accessActive);
}
@ -178,8 +202,17 @@ public class WorldData implements IWorldData {
@SuppressWarnings("unchecked")
void updateActivation(final ConfigFile rawConfiguration,
final boolean forceUpdateChildren) {
update(rawConfiguration, forceUpdateChildren, accessActive);
}
configFlagUpdate(rawConfiguration, forceUpdateChildren, accessActive);
/**
* Just update the activation.
* @param forceUpdateChildren
*/
@SuppressWarnings({"unchecked" })
void updateActivation(
final boolean forceUpdateChildren) {
update(forceUpdateChildren, accessActive);
}
@Override
@ -223,12 +256,11 @@ public class WorldData implements IWorldData {
// Instance.
///////////////////
// /** World wide lock ;). */
// private final Lock lock = new ReentrantLock();
/** World wide lock ;). */
private final Lock lock = new ReentrantLock();
WorldData parent = null;
private final Collection<WorldData> children = new LinkedHashSet<WorldData>();
private final GenericInstanceRegistry dataRegistry = new DefaultGenericInstanceRegistry();
private ConfigFile rawConfiguration = null;
@ -237,18 +269,25 @@ public class WorldData implements IWorldData {
private final String worldNameLowerCase;
private WorldIdentifier worldIdentifier = null;
WorldData(String worldName) {
this(worldName, null);
private IFactoryOneRegistry<WorldFactoryArgument> factoryRegistry;
private final InstanceMapLOW dataCache = new InstanceMapLOW(lock, 25);
WorldData(final String worldName,
final IFactoryOneRegistry<WorldFactoryArgument> factoryRegistry) {
this(worldName, null, factoryRegistry);
}
WorldData(String worldName, WorldData parent) {
WorldData(final String worldName, final WorldData parent,
final IFactoryOneRegistry<WorldFactoryArgument> factoryRegistry) {
// TODO: ILockable ?
this.parent = parent;
this.worldNameLowerCase = worldName.toLowerCase(); // Locale.ENGLISH ?
this.worldNameLowerCase = worldName == null ? null : worldName.toLowerCase(); // Locale.ENGLISH ?
this.factoryRegistry = factoryRegistry;
if (parent == null) {
checkTypeTree.setConfigOverrideType(OverrideType.SPECIFIC);
}
else {
checkTypeTree.setConfigOverrideType(OverrideType.DEFAULT);
adjustToParent(parent);
}
}
@ -267,8 +306,8 @@ public class WorldData implements IWorldData {
checkTypeTree.getNode(checkType).adjustToParent(
parent.checkTypeTree.getNode(checkType));
}
// Force update.
checkTypeTree.getNode(CheckType.ALL).update(rawConfiguration);
// Force update (custom overrides might be persistent, just not on object creation).
checkTypeTree.getNode(CheckType.ALL).update();
// TODO: What if children exist?
}
@ -304,25 +343,40 @@ public class WorldData implements IWorldData {
this.children.clear();
}
@Override
public ConfigFile getRawConfiguration() {
return rawConfiguration;
}
void update(final ConfigFile rawConfiguration) {
// TODO: Locking ?
this.rawConfiguration = rawConfiguration;
if (this.parent != null && rawConfiguration != this.parent.rawConfiguration) {
this.parent = null;
this.parent.removeChild(this);
this.parent = null;
this.checkTypeTree.getNode(CheckType.ALL).setConfigOverrideType(OverrideType.SPECIFIC);
}
this.update();
// TODO: Propagate to children?
}
void update() {
// TODO: Locking ?
// TODO: Distinguish updateByConfig and update().
checkTypeTree.getNode(CheckType.ALL).update(rawConfiguration);
// TODO: Propagate to children?
}
@Override
public void overrideCheckActivation(final CheckType checkType,
final AlmostBoolean active, final OverrideType overrideType,
final boolean overrideChildren) {
// TODO: Concept for locking.
checkTypeTree.getNode(checkType).overrideCheckActivation(
active, overrideType, overrideChildren);
// TODO: Propagate to children?
}
// TODO: overrideDebug?
@Override
public ConfigFile getRawConfiguration() {
return rawConfiguration;
}
@Override
@ -335,14 +389,6 @@ public class WorldData implements IWorldData {
return getCheckNode(checkType).isDebugActive();
}
@Override
public void overrideCheckActivation(final CheckType checkType,
final AlmostBoolean active, final OverrideType overrideType,
final boolean overrideChildren) {
// TODO: Concept for locking.
checkTypeTree.getNode(checkType).overrideCheckActivation(rawConfiguration, active, overrideType, overrideChildren);
}
@Override
public IWorldCheckNode getCheckNode(final CheckType checkType) {
return checkTypeTree.getNode(checkType);
@ -350,14 +396,16 @@ public class WorldData implements IWorldData {
@Override
public <T> T getGenericInstance(final Class<T> registeredFor) {
// TODO: Factories.
return dataRegistry.getGenericInstance(registeredFor);
}
@Override
public <T> IGenericInstanceHandle<T> getGenericInstanceHandle(final Class<T> registeredFor) {
// TODO: Factories.
return dataRegistry.getGenericInstanceHandle(registeredFor);
T instance = dataCache.get(registeredFor);
if (instance == null) {
instance = factoryRegistry.getNewInstance(registeredFor,
new WorldFactoryArgument(this));
if (instance != null) {
final T newInstance = dataCache.putIfAbsent(registeredFor, instance);
return newInstance == null ? instance : newInstance;
}
}
return instance;
}
@Override
@ -391,4 +439,58 @@ public class WorldData implements IWorldData {
return getCheckNode(checkType).shouldAdjustToLag();
}
/**
* Remove from cache.
*/
@Override
public <T> void removeGenericInstance(final Class<T> type) {
dataCache.remove(type);
}
@Override
public void removeAllGenericInstances(final Collection<Class<?>> types) {
if (dataCache.isEmpty()) {
return;
}
dataCache.remove(types);
}
@Override
public void removeSubCheckData(
final Collection<Class<? extends IDataOnRemoveSubCheckData>> types,
final Collection<CheckType> checkTypes
) {
final Collection<Class<?>> removeTypes = new LinkedList<Class<?>>();
for (final Class<? extends IDataOnRemoveSubCheckData> type : types) {
final IDataOnRemoveSubCheckData impl = dataCache.get(type);
if (impl != null) {
if (impl.dataOnRemoveSubCheckData(checkTypes)) {
removeTypes.add(type);
}
}
}
if (!removeTypes.isEmpty()) {
dataCache.remove(removeTypes);
}
}
public void onWorldUnload(final World world, final Collection<Class<? extends IDataOnWorldUnload>> types) {
for (final Class<? extends IDataOnWorldUnload> type : types) {
final IDataOnWorldUnload instance = dataCache.get(type);
if (instance != null && instance.dataOnWorldUnload(world, this)) {
dataCache.remove(type);
}
}
}
public void onReload(final Collection<Class<? extends IDataOnReload>> types) {
// (Might collect types in a set.)
for (final Class<? extends IDataOnReload> type : types) {
final IDataOnReload instance = dataCache.get(type);
if (instance != null && instance.dataOnReload(this)) {
dataCache.remove(type);
}
}
}
}

View File

@ -14,10 +14,12 @@
*/
package fr.neatmonster.nocheatplus.worlds;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -29,14 +31,32 @@ import java.util.concurrent.locks.ReentrantLock;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.world.WorldUnloadEvent;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.config.ICheckConfig;
import fr.neatmonster.nocheatplus.components.config.IConfig;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.components.data.ICheckData;
import fr.neatmonster.nocheatplus.components.data.IData;
import fr.neatmonster.nocheatplus.components.data.IDataOnReload;
import fr.neatmonster.nocheatplus.components.data.IDataOnWorldUnload;
import fr.neatmonster.nocheatplus.components.registry.factory.IFactoryOne;
import fr.neatmonster.nocheatplus.components.registry.factory.RichFactoryRegistry;
import fr.neatmonster.nocheatplus.components.registry.factory.RichFactoryRegistry.CheckRemovalSpec;
import fr.neatmonster.nocheatplus.components.registry.feature.INotifyReload;
import fr.neatmonster.nocheatplus.components.registry.order.RegistrationOrder.RegisterMethodWithOrder;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.DefaultConfig;
import fr.neatmonster.nocheatplus.event.mini.MiniListener;
import fr.neatmonster.nocheatplus.utilities.ds.map.HashMapLOW;
public class WorldDataManager implements IWorldDataManager {
public class WorldDataManager implements IWorldDataManager, INotifyReload {
static class IWorldDataEntry implements Entry<String, IWorldData> {
@ -93,15 +113,41 @@ public class WorldDataManager implements IWorldDataManager {
/** Global access lock for this registry. Also used for ConfigFile editing. */
private final Lock lock = new ReentrantLock();
/** Exact case name to WorldData map. */
/** Lower case name to WorldData map. */
// TODO: ID-name pairs / mappings?
private final HashMapLOW<String, WorldData> worldDataMap = new HashMapLOW<String, WorldData>(lock, 10);
private Map<String, ConfigFile> rawConfigurations = new HashMap<String, ConfigFile>(); // COW
private final RichFactoryRegistry<WorldFactoryArgument> factoryRegistry = new RichFactoryRegistry<WorldFactoryArgument>(lock);
private final MiniListener<?>[] miniListeners = new MiniListener<?>[] {
/*
* TODO: Constants in a class 'ListenerTags', plus a plan
* (system.data.player.nocheatplus, system.nocheatplus.data ??,
* nocheatplus.system.data.player...). (RegistryTags for other?).
*/
new MiniListener<WorldUnloadEvent>() {
@EventHandler(priority = EventPriority.LOWEST)
@RegisterMethodWithOrder(tag = "system.nocheatplus.worlddatamanager", beforeTag = "(system\\.nocheatplus\\.playerdatamanager|feature.*)")
@Override
public void onEvent(final WorldUnloadEvent event) {
onWorldUnload(event);
}
},
};
public WorldDataManager() {
// TODO: ILockable
// TODO: Create a default node with some basic settings.
createDefaultWorldData();
// (Call support.)
factoryRegistry.createAutoGroup(IDataOnReload.class);
factoryRegistry.createAutoGroup(IDataOnWorldUnload.class);
// Data/config removal.
factoryRegistry.createAutoGroup(IData.class);
factoryRegistry.createAutoGroup(IConfig.class);
factoryRegistry.createAutoGroup(ICheckData.class);
factoryRegistry.createAutoGroup(ICheckConfig.class);
}
@Override
@ -146,7 +192,18 @@ public class WorldDataManager implements IWorldDataManager {
}
private void createDefaultWorldData() {
createWorldData(null);
lock.lock();
if (worldDataMap.containsKey(null)) {
lock.unlock();
return;
}
ConfigFile config = rawConfigurations.get(null);
if (config == null) {
config = new DefaultConfig();
}
worldDataMap.put(null, new WorldData(null, this));
updateWorldData(null, config);
lock.unlock();
}
/**
@ -188,11 +245,12 @@ public class WorldDataManager implements IWorldDataManager {
lock.lock();
final Map<String, ConfigFile> rawConfigurations = new LinkedHashMap<String, ConfigFile>(rawWorldConfigs.size());
for (final Entry<String, ConfigFile> entry : rawWorldConfigs.entrySet()) {
rawConfigurations.put(entry.getKey().toLowerCase(), entry.getValue());
final String worldName = entry.getKey();
rawConfigurations.put(worldName == null ? null : worldName.toLowerCase(), entry.getValue());
}
this.rawConfigurations = rawConfigurations;
final ConfigFile defaultConfig = this.rawConfigurations.get(null);
final WorldData defaultWorldData = internalGetDefaultWorldData(); // Always the same instance.
this.rawConfigurations = rawConfigurations;
defaultWorldData.update(defaultConfig);
lock.unlock(); // From here on, new instances have a proper config set.
// Update all given
@ -209,14 +267,14 @@ public class WorldDataManager implements IWorldDataManager {
// Update all that are not contained and don't point to the default configuration.
// TODO: Consider deleting world nodes, unless the world is actually loaded.
for (final Entry<String, WorldData> entry : worldDataMap.iterable()) {
if (!rawConfigurations.containsKey(entry.getKey())) {
final String worldName = entry.getKey();
if (worldName != null
&& !rawConfigurations.containsKey(worldName)) {
final WorldData ref = entry.getValue();
if (ref.getRawConfiguration() != defaultConfig) {
lock.lock();
defaultWorldData.addChild(ref); // Redundant calls are ok.
ref.adjustToParent(defaultWorldData); // Inherit specific overrides and more.
lock.unlock();
}
lock.lock();
defaultWorldData.addChild(ref); // Redundant calls are ok.
ref.adjustToParent(defaultWorldData); // Inherit specific overrides and more.
lock.unlock();
}
}
}
@ -269,17 +327,24 @@ public class WorldDataManager implements IWorldDataManager {
* @param rawConfiguration
* @return
*/
private WorldData updateWorldData(final String worldName, final ConfigFile rawConfiguration) {
private WorldData updateWorldData(final String worldName,
final ConfigFile rawConfiguration) {
final WorldData defaultWorldData = internalGetDefaultWorldData();
lock.lock(); // TODO: Might lock outside (pro/con).
final String lcName = worldName.toLowerCase();
final String lcName = worldName == null ? null : worldName.toLowerCase();
WorldData data = worldDataMap.get(lcName);
boolean skipUpdate = false;
if (data == null) {
data = new WorldData(worldName, defaultWorldData);
worldDataMap.put(worldName.toLowerCase(), data);
defaultWorldData.addChild(data);
data = new WorldData(worldName, defaultWorldData, factoryRegistry);
worldDataMap.put(data.getWorldNameLowerCase(), data);
if (rawConfiguration == defaultWorldData.getRawConfiguration()) {
defaultWorldData.addChild(data);
skipUpdate = true;
}
}
if (!skipUpdate) {
data.update(rawConfiguration); // Parent/child state is updated here.
}
data.update(rawConfiguration); // Parent/child state is updated here.
lock.unlock();
return data;
}
@ -287,17 +352,16 @@ public class WorldDataManager implements IWorldDataManager {
@Override
public void overrideCheckActivation(final CheckType checkType, final AlmostBoolean active,
final OverrideType overrideType, final boolean overrideChildren) {
// TODO: Implement changed signature
//
lock.lock();
final IWorldData defaultWorldData = getDefaultWorldData();
defaultWorldData.overrideCheckActivation(checkType, active, overrideType, overrideChildren);
// Override flags.
// TODO: If not under lock, default WorldData needs to be done first.
// TODO: If possible, skip derived from default, once default data is done first.
// TODO: If possible, skip derived from default, since default data is done first.
for (final Entry<String, WorldData> entry : worldDataMap.iterable()) {
final WorldData worldData = entry.getValue();
// TODO: default visibility method including overrideChildWorldDatas (here: false).
worldData.overrideCheckActivation(checkType, active, overrideType, overrideChildren);
final IWorldData worldData = entry.getValue();
if (worldData != defaultWorldData) {
worldData.overrideCheckActivation(checkType, active, overrideType, overrideChildren);
}
}
lock.unlock();
}
@ -328,4 +392,129 @@ public class WorldDataManager implements IWorldDataManager {
}
}
@Override
public <T> void registerFactory(Class<T> registerFor,
IFactoryOne<WorldFactoryArgument, T> factory) {
factoryRegistry.registerFactory(registerFor, factory);
}
@Override
public <G> void createAutoGroup(Class<G> groupType) {
factoryRegistry.createAutoGroup(groupType);
}
@Override
public <T> Collection<Class<? extends T>> getGroupedTypes(
Class<T> groupType) {
return factoryRegistry.getGroupedTypes(groupType);
}
@Override
public <T> Collection<Class<? extends T>> getGroupedTypes(
Class<T> groupType, CheckType checkType) {
return factoryRegistry.getGroupedTypes(groupType, checkType);
}
@Override
public <I> void addToGroups(Class<I> itemType,
Class<? super I>... groupTypes) {
factoryRegistry.addToGroups(itemType, groupTypes);
}
@Override
public void addToExistingGroups(Class<?> itemType) {
factoryRegistry.addToExistingGroups(itemType);
}
@Override
public <I> void addToGroups(CheckType checkType, Class<I> itemType,
Class<? super I>... groupTypes) {
factoryRegistry.addToGroups(checkType, itemType, groupTypes);
}
@Override
public <I> void addToExistingGroups(CheckType checkType,
Class<I> itemType) {
factoryRegistry.addToExistingGroups(checkType, itemType);
}
@Override
public <I> void addToGroups(Collection<CheckType> checkTypes,
Class<I> itemType, Class<? super I>... groupTypes) {
factoryRegistry.addToGroups(checkTypes, itemType, groupTypes);
}
@Override
public <I> void addToExistingGroups(Collection<CheckType> checkTypes,
Class<I> itemType) {
factoryRegistry.addToExistingGroups(checkTypes, itemType);
}
@Override
public <G> void createGroup(Class<G> groupType) {
factoryRegistry.createGroup(groupType);
}
@Override
public <T> T getNewInstance(Class<T> registeredFor,
WorldFactoryArgument arg) {
return factoryRegistry.getNewInstance(registeredFor, arg);
}
/**
* Initializing with online players.
*/
public void onEnable() {
final NoCheatPlusAPI api = NCPAPIProvider.getNoCheatPlusAPI();
for (final MiniListener<?> listener : miniListeners) {
api.addComponent(listener, false);
}
api.addComponent(this);
}
private void onWorldUnload(WorldUnloadEvent event) {
final Collection<Class<? extends IDataOnWorldUnload>> types = factoryRegistry.getGroupedTypes(IDataOnWorldUnload.class);
final World world = event.getWorld();
final WorldData worldData = worldDataMap.get(world.getName().toLowerCase());
worldData.onWorldUnload(event.getWorld(), types);
// TODO: Minimize, clear cache / remove ?
}
@Override
public void onReload() {
final Collection<Class<? extends IDataOnReload>> types = factoryRegistry.getGroupedTypes(IDataOnReload.class);
for (final Entry<String, WorldData> entry : worldDataMap.iterable()) {
entry.getValue().onReload(types);
}
}
@Override
public void clearData(CheckType checkType) {
final CheckRemovalSpec removalSpec = new CheckRemovalSpec(checkType, true, this);
final boolean hasComplete = !removalSpec.completeRemoval.isEmpty();
final boolean hasSub = !removalSpec.subCheckRemoval.isEmpty();
if (hasComplete || hasSub) {
for (final Entry<String, WorldData> entry : worldDataMap.iterable()) {
final WorldData worldData = entry.getValue();
if (hasComplete) {
worldData.removeAllGenericInstances(removalSpec.completeRemoval);
}
if (hasSub) {
worldData.removeSubCheckData(removalSpec.subCheckRemoval, removalSpec.checkTypes);
}
}
}
}
@Override
public void removeCachedConfigs() {
final Collection<Class<?>> types = new LinkedHashSet<Class<?>>(
factoryRegistry.getGroupedTypes(IConfig.class));
if (!types.isEmpty()) {
for (final Entry<String, WorldData> entry : worldDataMap.iterable()) {
entry.getValue().removeAllGenericInstances(types);
}
}
}
}

View File

@ -0,0 +1,25 @@
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.neatmonster.nocheatplus.worlds;
public class WorldFactoryArgument {
public final IWorldData worldData;
public WorldFactoryArgument(IWorldData worldData) {
this.worldData = worldData;
}
}

View File

@ -0,0 +1,38 @@
package fr.neatmonster.nocheatplus.test;
import static org.junit.Assert.fail;
import org.junit.Test;
import fr.neatmonster.nocheatplus.compat.AlmostBoolean;
import fr.neatmonster.nocheatplus.components.config.value.AlmostBooleanWithOverride;
import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.components.config.value.ValueWithOverride;
public class TestConfigValueWithOverride {
private <T> void expectValueIdentity(ValueWithOverride<T> configValue, T value) {
if (configValue.getValue() != value) {
fail("Expect value '" + value + "', got instead: '" + configValue.getValue() + "'.");
}
}
@Test
public void testAlmostBoolean() {
AlmostBooleanWithOverride val1 = new AlmostBooleanWithOverride();
if (!val1.setValue(AlmostBoolean.NO, OverrideType.SPECIFIC)) {
fail("Override to SPECIFIC after init is expected to work.");
}
expectValueIdentity(val1, AlmostBoolean.NO);
if (!val1.setValue(AlmostBoolean.YES, OverrideType.SPECIFIC)) {
fail("Override SPECIFIC -> SPECIFIC is expected to work.");
}
expectValueIdentity(val1, AlmostBoolean.YES);
}
}

View File

@ -65,6 +65,7 @@ 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.net.NetStatic;
import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
import fr.neatmonster.nocheatplus.clients.ModUtil;
import fr.neatmonster.nocheatplus.command.NoCheatPlusCommand;
@ -100,6 +101,7 @@ import fr.neatmonster.nocheatplus.components.registry.feature.TickListener;
import fr.neatmonster.nocheatplus.components.registry.lockable.BasicLockable;
import fr.neatmonster.nocheatplus.components.registry.lockable.ILockable;
import fr.neatmonster.nocheatplus.components.registry.order.SetupOrder;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.ConfigManager;
@ -122,12 +124,13 @@ import fr.neatmonster.nocheatplus.permissions.PermissionUtil.CommandProtectionEn
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.permissions.RegisteredPermission;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.IPlayerDataManager;
import fr.neatmonster.nocheatplus.players.IPlayerData;
import fr.neatmonster.nocheatplus.players.IPlayerDataManager;
import fr.neatmonster.nocheatplus.players.PlayerDataManager;
import fr.neatmonster.nocheatplus.players.PlayerMessageSender;
import fr.neatmonster.nocheatplus.stats.Counters;
import fr.neatmonster.nocheatplus.utilities.ColorUtil;
import fr.neatmonster.nocheatplus.utilities.Misc;
import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener;
import fr.neatmonster.nocheatplus.utilities.ReflectionUtil;
import fr.neatmonster.nocheatplus.utilities.StringUtil;
@ -269,6 +272,17 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
}
@SetupOrder(priority = - 100)
private class ReloadHook implements INotifyReload{
@Override
public void onReload() {
// Only for reloading, not INeedConfig.
processReload();
}
}
private INotifyReload reloadHook = new ReloadHook();
/** Access point for thread safe message queuing. */
// TODO: Replace by access point for message sending in general (relay to asynchronous depending on settings).
private final PlayerMessageSender playerMessageSender = new PlayerMessageSender();
@ -939,23 +953,15 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
initBlockProperties(config);
// Initialize data manager.
worldDataManager.onEnable();
pDataMan.onEnable();
// Register components.
@SetupOrder(priority = - 100)
class ReloadHook implements INotifyReload{
@Override
public void onReload() {
// Only for reloading, not INeedConfig.
processReload();
}
}
// Add the "low level" system components first.
for (final Object obj : new Object[]{
getCoreListener(),
// Put ReloadListener first, because Checks could also listen to it.
new ReloadHook(),
reloadHook,
pDataMan,
new AuxMoving(),
}) {
@ -981,6 +987,8 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
// Register sub-components (allow later added to use registries, if any).
processQueuedSubComponentHolders();
}
// Ensure net types are registered.
NetStatic.registerTypes();
// Register optional default components.
final DefaultComponentFactory dcf = new DefaultComponentFactory();
@ -1018,8 +1026,12 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
}, 1207, 1207);
// Ensure dataMan is first on disableListeners.
disableListeners.remove(pDataMan);
disableListeners.add(0, pDataMan);
// TODO: Why first ?
Misc.putFirst(pDataMan, disableListeners);
Misc.putFirst(pDataMan, notifyReload);
Misc.putFirst(worldDataManager, notifyReload);
// Put ReloadListener first, because Checks could also listen to it.
Misc.putFirst(reloadHook, notifyReload);
// Set up consistency checking.
scheduleConsistencyCheckers();
@ -1518,4 +1530,14 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
return pDataMan;
}
@Override
public void register(RegistrationContext context) {
context.doRegister(); // TODO: ...
}
@Override
public RegistrationContext newRegistrationContext() {
return new RegistrationContext();
}
}

View File

@ -15,6 +15,7 @@
package fr.neatmonster.nocheatplus;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@ -25,6 +26,9 @@ import fr.neatmonster.nocheatplus.components.NoCheatPlusAPI;
import fr.neatmonster.nocheatplus.components.registry.ComponentRegistry;
import fr.neatmonster.nocheatplus.components.registry.DefaultGenericInstanceRegistry;
import fr.neatmonster.nocheatplus.components.registry.event.IGenericInstanceHandle;
import fr.neatmonster.nocheatplus.components.registry.setup.RegistrationContext;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.DefaultConfig;
import fr.neatmonster.nocheatplus.event.mini.EventRegistryBukkit;
import fr.neatmonster.nocheatplus.logging.LogManager;
import fr.neatmonster.nocheatplus.logging.StaticLog;
@ -51,10 +55,14 @@ public class PluginTests {
private final PermissionRegistry permissionRegistry = new PermissionRegistry(10000);
public UnitTestNoCheatPlusAPI() {
StaticLog.setUseLogManager(false); // Ensure either this instance provides it or it's unset.
genericInstanceRegistry.registerGenericInstance(MCAccess.class, new MCAccessBukkit());
for (RegisteredPermission rp : Permissions.getPermissions()) {
permissionRegistry.addRegisteredPermission(rp);
}
Map<String, ConfigFile> rawConfigs = new HashMap<String, ConfigFile>();
rawConfigs.put(null, new DefaultConfig());
worldDataManager.applyConfiguration((rawConfigs));
}
@Override
@ -194,6 +202,16 @@ public class PluginTests {
throw new UnsupportedOperationException();
}
@Override
public RegistrationContext newRegistrationContext() {
throw new UnsupportedOperationException();
}
@Override
public void register(RegistrationContext context) {
throw new UnsupportedOperationException();
}
}
public static void setUnitTestNoCheatPlusAPI(boolean force) {

View File

@ -16,6 +16,7 @@ package fr.neatmonster.nocheatplus.test;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
@ -29,6 +30,7 @@ import fr.neatmonster.nocheatplus.components.config.value.OverrideType;
import fr.neatmonster.nocheatplus.config.ConfPaths;
import fr.neatmonster.nocheatplus.config.ConfigFile;
import fr.neatmonster.nocheatplus.config.DefaultConfig;
import fr.neatmonster.nocheatplus.logging.StaticLog;
import fr.neatmonster.nocheatplus.worlds.WorldDataManager;
public class TestWorldDataManager {
@ -55,9 +57,30 @@ public class TestWorldDataManager {
cfg.set(key, value);
}
/**
* Set for all.
*
* @param map
* @param key
* @param value
*/
void set(Map<String, ConfigFile> map, String key, Object value) {
for (String worldName : map.keySet()) {
set(map, worldName, key, value);
}
}
void setup(Map<String, ConfigFile> map, String... worldNames) {
for (String worldName : worldNames) {
set(map, worldName, "dummy", true);
}
}
@Test
public void BasicTests() {
//PluginTests.setUnitTestNoCheatPlusAPI(false);
StaticLog.setUseLogManager(false);
WorldDataManager worldMan = getWorldDataManager();
@ -66,7 +89,9 @@ public class TestWorldDataManager {
// (Implicitly create configurations via set).
// Default.
set(rawWorldConfigs, null, ConfPaths.COMBINED + ConfPaths.SUB_ACTIVE, "yes");
set(rawWorldConfigs, null, ConfPaths.COMBINED_MUNCHHAUSEN_CHECK, "default");
// All existing.
setup(rawWorldConfigs, null, "Exist1", "Exist2");
set(rawWorldConfigs, ConfPaths.COMBINED_MUNCHHAUSEN_CHECK, "default");
// Exist1
set(rawWorldConfigs, "Exist1", ConfPaths.COMBINED + ConfPaths.SUB_ACTIVE, "no");
@ -76,8 +101,18 @@ public class TestWorldDataManager {
// (Might set some particularly interesting values here.)
/////////////////////////
// Apply configuration
/////////////////////////
worldMan.applyConfiguration(rawWorldConfigs);
for (String worldName : Arrays.asList("Exist1", "Exist2")) {
if (rawWorldConfigs.get(worldName) != worldMan.getWorldData(worldName).getRawConfiguration()) {
fail("Raw configuration set wrongly: " + worldName);
}
}
if (!worldMan.getWorldData("notExist1").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Inherited from default: COMBINED_MUNCHHAUSEN should be active (-> COMBINED is)");
}
@ -94,6 +129,15 @@ public class TestWorldDataManager {
fail("Specific: COMBINED_MUNCHHAUSEN should not be active (directly set)");
}
if (!worldMan.getWorldData("notExist2").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Inherited from default: COMBINED_MUNCHHAUSEN should be active (-> COMBINED is)");
}
////////////////
// Override
////////////////
// Override via config "reload":
set(rawWorldConfigs, "Exist2", ConfPaths.COMBINED_MUNCHHAUSEN_CHECK, true);
worldMan.applyConfiguration(rawWorldConfigs);
@ -101,32 +145,57 @@ public class TestWorldDataManager {
fail("Specific: COMBINED_MUNCHHAUSEN should be active (directly set)");
}
// Specific override (mild / reset with reload).
// Specific override (mild).
worldMan.overrideCheckActivation(CheckType.COMBINED, AlmostBoolean.NO,
OverrideType.SPECIFIC, false);
if (worldMan.getWorldData("notExist2").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Overridden (inherited from default): COMBINED_MUNCHHAUSEN should not be active (-> COMBINED is not)");
}
worldMan.overrideCheckActivation(CheckType.COMBINED, AlmostBoolean.NO,
OverrideType.SPECIFIC, true);
if (worldMan.getWorldData("notExist2").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Overridden (inherited from default): COMBINED_MUNCHHAUSEN should not be active (-> COMBINED is)");
fail("Overridden (inherited from default): COMBINED_MUNCHHAUSEN should not be active (overrideChildren from COMBINED should explicitly set this)");
}
///////////////////
// Fake reload 1
///////////////////
/*
* NOTE: With this testing scenario, ConfigFile instances within
* rawWorldConfigs stay identical, which would not be the case with a
* ConfigManager based reload.
*/
worldMan.applyConfiguration(rawWorldConfigs);
if (!worldMan.getWorldData("notExist2").isCheckActive(CheckType.COMBINED)) {
fail("Inherited from default: COMBINED should be active after reload.");
}
if (!worldMan.getWorldData("notExist2").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Inherited from default: COMBINED_MUNCHHAUSEN should be active (-> COMBINED is)");
}
////////////////
// Override 2
////////////////
worldMan.getWorldData("NotExist3").overrideCheckActivation(CheckType.COMBINED_MUNCHHAUSEN,
AlmostBoolean.NO, OverrideType.SPECIFIC, false);
if (worldMan.getWorldData("notExist3").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Overridden (SPECIFIC): COMBINED_MUNCHHAUSEN should not be active (-directly set)");
}
///////////////////
// Fake reload 2
///////////////////
worldMan.applyConfiguration(rawWorldConfigs);
if (worldMan.getWorldData("notExist3").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Overridden (SPECIFIC): COMBINED_MUNCHHAUSEN should not be active after reload (directly set)");
if (!worldMan.getWorldData("notExist3").isCheckActive(CheckType.COMBINED_MUNCHHAUSEN)) {
fail("Overridden (SPECIFIC): COMBINED_MUNCHHAUSEN should be active after reload (COMBINED is)");
}
// TODO: "Reload with special cases"
// TODO: overriding
}
}