mirror of
https://github.com/asofold/CompatNoCheatPlus.git
synced 2024-11-18 11:16:23 +01:00
Example sketch for new hook system (designed for replacing events
completely later).
This commit is contained in:
parent
dd0c22fae2
commit
074626c2e7
@ -0,0 +1,13 @@
|
||||
package me.asofold.bpl.cncp.hooks.ncp;
|
||||
|
||||
|
||||
/**
|
||||
* Extend this class for maximum future compatibility.<br>
|
||||
* Especially the onCheckFailure method might get extended with check specific arguments,
|
||||
* this class will provide compatibility with older method signatures, where possible.
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractNCPHook implements NCPHook {
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package me.asofold.bpl.cncp.hooks.ncp;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Compatibility hooks have to implement this.<br>
|
||||
* To set VL etc they have to make use of another API,
|
||||
* be it in NCPHookManager or probably rather better in NCP or some NCPAPI class.
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
public interface NCPHook{
|
||||
|
||||
/**
|
||||
* For logging purposes.
|
||||
* @return
|
||||
*/
|
||||
public String getHookName();
|
||||
|
||||
/**
|
||||
* For logging purposes.
|
||||
* @return
|
||||
*/
|
||||
public String getHookVersion();
|
||||
|
||||
/**
|
||||
* This is called on failure of a check.<br>
|
||||
* This is the minimal interface, one should probably add specific information
|
||||
* like (target) locations and VL, but with this a lot is possible already (cncp).<br>
|
||||
* See AbstractNCPHook for future questions.
|
||||
* @param groupId Check group id.
|
||||
* @param checkId Check id for the specific check (for instance: NCPHookManager.MOVING_NOFALL).
|
||||
* @param player The player that failed the check.
|
||||
* @return If to cancel the check failure processing.
|
||||
*/
|
||||
public boolean onCheckFailure(Integer groupId, Integer checkId, Player player);
|
||||
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,294 @@
|
||||
package me.asofold.bpl.cncp.hooks.ncp;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
|
||||
/**
|
||||
* After-check-failure hook manager to be integrated into NoCheatPlus.<br>
|
||||
*
|
||||
*
|
||||
* Questions: sync for registering, or extra map for chat ? [maybe setLocked flag with sync object just for changing and query from external thread]
|
||||
*
|
||||
* @author mc_dev
|
||||
*
|
||||
*/
|
||||
public final class NCPHookManager {
|
||||
|
||||
/* ----------------------------------------------------------------------------*
|
||||
/*
|
||||
* Ids for groups and checks:
|
||||
*
|
||||
* TODO: could change to Enums with int values.
|
||||
*
|
||||
* A check group starts at thousands and covers all
|
||||
* ids till next thousand - 1, for instance 1000 to 1999.
|
||||
*
|
||||
* This is important for also being able to check the check group for the check.
|
||||
*/
|
||||
|
||||
// ALL
|
||||
public static final Integer ALL = 0;
|
||||
|
||||
// MOVING
|
||||
public static final Integer MOVING = 1000;
|
||||
|
||||
public static final Integer MOVING_NOFALL = 1001;
|
||||
|
||||
// FIGHT
|
||||
public static final Integer FIGHT = 2000;
|
||||
|
||||
public static final Integer FIGHT_SPEED = 2001;
|
||||
|
||||
// ...
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* Internal data: */
|
||||
|
||||
/**
|
||||
* Ids given to hooks.
|
||||
*/
|
||||
private static int maxHookId = 0;
|
||||
|
||||
/**
|
||||
* Hook id to hook.
|
||||
*/
|
||||
private final static Map<Integer, NCPHook> allHooks = new HashMap<Integer, NCPHook>();
|
||||
|
||||
/**
|
||||
* Mapping the check ids to the hooks.
|
||||
*/
|
||||
private static final Map<Integer, List<NCPHook>> hooksByChecks = new HashMap<Integer, List<NCPHook>>();
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* Internals: manage hooks internally */
|
||||
|
||||
private static Integer getNewHookId(){
|
||||
maxHookId ++;
|
||||
return maxHookId;
|
||||
}
|
||||
|
||||
/**
|
||||
* for registration purposes only.
|
||||
* @param hook
|
||||
* @return Unique id associated with that hook (returns an existing id if hook is already present).
|
||||
*/
|
||||
private static Integer getId(NCPHook hook){
|
||||
if (hook == null) throw new NullPointerException("Hooks must not be null."); // just in case.
|
||||
Integer id = null;
|
||||
for (Integer refId : allHooks.keySet()){
|
||||
if (hook == allHooks.get(refId)){
|
||||
id = refId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id == null){
|
||||
id = getNewHookId();
|
||||
allHooks.put(id, hook);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add hook to the hooksByChecks mappings, for the check id and if different group id.
|
||||
* assumes that the hook already has been registered in the allHooks map.
|
||||
* @param checkId
|
||||
* @param hook
|
||||
*/
|
||||
private static void addToMappings(Integer checkId, NCPHook hook) {
|
||||
Integer groupId = (checkId.intValue() / 1000) * 1000; // NOTE: group id calculation
|
||||
addToMapping(checkId, hook);
|
||||
if (checkId.intValue() != groupId.intValue()) addToMapping(groupId, hook);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param checkId
|
||||
* @param hook
|
||||
*/
|
||||
private static void addToMapping(Integer checkId, NCPHook hook) {
|
||||
List<NCPHook> hooks = hooksByChecks.get(checkId);
|
||||
if (hooks == null){
|
||||
hooks = new ArrayList<NCPHook>();
|
||||
hooks.add(hook);
|
||||
hooksByChecks.put(checkId, hooks);
|
||||
}
|
||||
else if (!hooks.contains(hook)) hooks.add(hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove from internal mappings, both allHooks and hooksByChecks.
|
||||
* @param hook
|
||||
* @param hookId
|
||||
*/
|
||||
private static void removeFromMappings(NCPHook hook, Integer hookId) {
|
||||
allHooks.remove(hookId);
|
||||
List<Integer> rem = new LinkedList<Integer>();
|
||||
for (Integer checkId : hooksByChecks.keySet()){
|
||||
List<NCPHook> hooks = hooksByChecks.get(checkId);
|
||||
if (hooks.remove(hook)){
|
||||
if (hooks.isEmpty()) rem.add(checkId);
|
||||
}
|
||||
}
|
||||
for (Integer checkId : rem){
|
||||
hooksByChecks.remove(checkId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String getHookDescription(final NCPHook hook){
|
||||
return hook.getHookName() + " [" + hook.getHookVersion() + "]";
|
||||
}
|
||||
|
||||
private static final void logHookAdded(NCPHook hook){
|
||||
Bukkit.getLogger().info("[NoCheatPlus/Compat] Added hook: " + getHookDescription(hook));
|
||||
}
|
||||
|
||||
private static final void logHookRemoved(NCPHook hook){
|
||||
Bukkit.getLogger().info("[NoCheatPlus/Compat] Removed hook: " + getHookDescription(hook));
|
||||
}
|
||||
|
||||
private static final void logHookFailure(final Integer groupId, final Integer checkId, final Player player, final NCPHook hook, final Throwable t){
|
||||
// TODO: might accumulate failure rate and only log every so and so seconds or disable hook if spamming (leads to ncp spam though)?
|
||||
final StringBuilder builder = new StringBuilder(1024);
|
||||
builder.append("[NoCheatPlus/Compat] Hook " + getHookDescription(hook) + " encountered an unexpected exception:\n");
|
||||
builder.append("Processing: ");
|
||||
if (checkId.intValue() != groupId.intValue()) builder.append("Group " + groupId + " ");
|
||||
builder.append("Check " + checkId);
|
||||
builder.append(" Player " + player.getName());
|
||||
builder.append("\n");
|
||||
builder.append("Exception (" + t.getClass().getSimpleName() + "): " + t.getMessage() + "\n");
|
||||
for (StackTraceElement el : t.getStackTrace()){
|
||||
builder.append(el.toString());
|
||||
}
|
||||
|
||||
Bukkit.getLogger().severe(builder.toString());
|
||||
}
|
||||
|
||||
private static final boolean applyHooks(final Integer groupId, final Integer checkId, final Player player, final List<NCPHook> hooks) {
|
||||
for (int i = 0; i < hooks.size(); i ++){
|
||||
final NCPHook hook = hooks.get(i);
|
||||
try{
|
||||
if (hook.onCheckFailure(groupId, checkId, player)) return true;
|
||||
}
|
||||
catch (Throwable t){
|
||||
// TODO: maybe distinguish some exceptions here (Interrupted ?).
|
||||
logHookFailure(groupId, checkId, player, hook, t);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------- */
|
||||
/* Internal API for NCP */
|
||||
/*
|
||||
* Internally NCP will now instead of events call hooks:
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is called by checks when players fail them.
|
||||
* @param checkId Id for the check, should be taken from the constants defined in this class.
|
||||
* @param player The player that fails the check.
|
||||
* @return if to cancel the VL processing.
|
||||
*/
|
||||
public static final boolean shouldCancelVLProcessing(final Integer checkId, final Player player){
|
||||
|
||||
// checks for hooks registered for all events, only for the group and specifically for the check.
|
||||
// A Paradigm could be to return true as soon as one hook has returned true.
|
||||
final Integer groupId = (checkId.intValue() / 1000) * 1000; // NOTE: group id calculation
|
||||
|
||||
// Most specific:
|
||||
final List<NCPHook> hooksCheck = hooksByChecks.get(checkId);
|
||||
if (hooksCheck != null){
|
||||
if (applyHooks(groupId, checkId, player, hooksCheck)) return true;
|
||||
}
|
||||
|
||||
// Group:
|
||||
if (checkId.intValue() != groupId.intValue()){
|
||||
final List<NCPHook> hooksGroup= hooksByChecks.get(groupId);
|
||||
if (hooksCheck != null){
|
||||
if (applyHooks(groupId, checkId, player, hooksGroup)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
// general (all):
|
||||
final List<NCPHook> hooksAll = hooksByChecks.get(ALL);
|
||||
if (hooksAll != null){
|
||||
if (applyHooks(groupId, checkId, player, hooksAll)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* External API for adding hooks etc. */
|
||||
|
||||
/**
|
||||
* Register a hook for a specific check id (all, group, or an individual check).
|
||||
* @param checkId
|
||||
* @param hook
|
||||
* @return An id to identify the hook, will return the existing id if the hook was already present somewhere.
|
||||
*/
|
||||
public static Integer addHook(Integer checkId, NCPHook hook){
|
||||
Integer hookId = getId(hook);
|
||||
addToMappings(checkId, hook);
|
||||
logHookAdded(hook);
|
||||
return hookId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a hook for several individual checks id (all, group, or an individual checks).
|
||||
* @param checkIds Array of ids to register the hook for.
|
||||
* @param hook
|
||||
* @return
|
||||
*/
|
||||
public static Integer addHook(Integer[] checkIds, NCPHook hook){
|
||||
Integer hookId = getId(hook);
|
||||
for (Integer checkId : checkIds){
|
||||
addToMappings(checkId, hook);
|
||||
}
|
||||
logHookAdded(hook);
|
||||
return hookId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hook
|
||||
* @return hook id if present, null otherwise.
|
||||
*/
|
||||
public static Integer removeHook(NCPHook hook){
|
||||
Integer hookId = null;
|
||||
for (Integer refId : allHooks.keySet()){
|
||||
if (hook == allHooks.get(refId)){
|
||||
hookId = refId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hookId == null) return null;
|
||||
removeFromMappings(hook, hookId);
|
||||
logHookRemoved(hook);
|
||||
return hookId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hookId if present, null otherwise.
|
||||
* @return
|
||||
*/
|
||||
public static NCPHook removeHook(Integer hookId){
|
||||
NCPHook hook = allHooks.get(hookId);
|
||||
if (hook == null) return null;
|
||||
removeFromMappings(hook, hookId);
|
||||
logHookRemoved(hook);
|
||||
return hook;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user