public ReflectBlock(ReflectBase base, ReflectBlockPosition blockPosition) throws ClassNotFoundException { final Class<?> clazz = Class.forName(base.nmsPackageName + ".Block"); // byID (static) nmsGetById = ReflectionUtil.getMethod(clazz, "getById", int.class); // getMaterial nmsGetMaterial = ReflectionUtil.getMethodNoArgs(clazz, "getMaterial"); // updateShape Method method = null; Class<?> clazzIBlockAccess = Class.forName(base.nmsPackageName + ".IBlockAccess"); if (blockPosition != null) { method = ReflectionUtil.getMethod(clazz, "updateShape", clazzIBlockAccess, blockPosition.nmsClass); } if (method == null) { method = ReflectionUtil.getMethod( clazz, "updateShape", clazzIBlockAccess, int.class, int.class, int.class); useBlockPosition = false; } else { useBlockPosition = true; } nmsUpdateShape = method; // Block bounds fetching. The array uses the order the methods (used to) appear in the nms // class. String[] names = new String[] { "getMinX", "getMaxX", "getMinY", "getMaxY", "getMinZ", "getMaxZ" }; // FUTURE GUESS. Method[] methods = tryBoundsMethods(clazz, names); if (methods == null) { names = guessBoundsMethodNames(clazz); if (names != null) { methods = tryBoundsMethods(clazz, names); } if (methods == null) { methods = new Method[] {null, null, null, null, null, null}; } } // TODO: Test which is which [ALLOW to configure and also save used ones to config, by mc // version]. // TODO: Dynamically test these ? [needs an extra world/space to place blocks inside of...] if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS)) { NCPAPIProvider.getNoCheatPlusAPI() .getLogManager() .debug( Streams.INIT, "[NoCheatPlus] ReflectBlock: Use methods for shape: " + StringUtil.join(Arrays.asList(names), ", ")); } this.nmsGetMinX = methods[0]; this.nmsGetMaxX = methods[1]; this.nmsGetMinY = methods[2]; this.nmsGetMaxY = methods[3]; this.nmsGetMinZ = methods[4]; this.nmsGetMaxZ = methods[5]; }
/** * Handle the '/nocheatplus reload' command. * * @param sender the sender * @return true, if successful */ private void handleReloadCommand(final CommandSender sender) { final LogManager logManager = NCPAPIProvider.getNoCheatPlusAPI().getLogManager(); if (!sender.equals(Bukkit.getConsoleSender())) { sender.sendMessage(TAG + "Reloading configuration..."); } logManager.info(Streams.INIT, TAG + "Reloading configuration..."); // Do the actual reload. ConfigManager.cleanup(); ConfigManager.init(access); 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); } // Reset debug flags to default (temp, heavy). DataManager.restoreDefaultDebugFlags(); // Tell the registered listeners to adapt to new config, first sort them (!). Collections.sort(notifyReload, Order.cmpSetupOrder); for (final INotifyReload component : notifyReload) { component.onReload(); } // Say to the other plugins that we've reloaded the configuration. Bukkit.getPluginManager().callEvent(new NCPReloadEvent()); // Log reloading done. if (!sender.equals(Bukkit.getConsoleSender())) { sender.sendMessage(TAG + "Configuration reloaded!"); } logManager.info(Streams.INIT, TAG + "Configuration reloaded."); logManager.info( Streams.DEFAULT_FILE, StringUtil.join(VersionCommand.getVersionInfo(), "\n")); // Queued (!). }
@Override public void checkConsistency(final Player[] onlinePlayers) { // Check online player tracking consistency. int missing = 0; int changed = 0; int expectedSize = 0; for (int i = 0; i < onlinePlayers.length; i++) { final Player player = onlinePlayers[i]; final String name = player.getName(); // if (player.isOnline()){ expectedSize += 1 + (name.equals(name.toLowerCase()) ? 0 : 1); if (!this.onlinePlayers.containsKey(name)) { missing++; // TODO: Add the player [problem: messy NPC plugins?]? } if (player != this.onlinePlayers.get(name)) { changed++; // Update the reference. addOnlinePlayer(player); // } } } // TODO: Consider checking lastLogout for too long gone players. final int storedSize = this.onlinePlayers.size(); if (missing != 0 || changed != 0 || expectedSize != storedSize) { final List<String> details = new LinkedList<String>(); if (missing != 0) { details.add("missing online players (" + missing + ")"); } if (expectedSize != storedSize) { // TODO: Consider checking for not online players and remove them. details.add( "wrong number of online players (" + storedSize + " instead of " + expectedSize + ")"); } if (changed != 0) { details.add("changed player instances (" + changed + ")"); } LogUtil.logWarning( "[NoCheatPlus] DataMan inconsistencies: " + StringUtil.join(details, " | ")); } }
private void outpuDebugMove( final Player player, final double hDistance, final double limitH, final double yDistance, final double limitV, final ModelFlying model, final List<String> tags, final MovingData data) { final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove(); StringBuilder builder = new StringBuilder(350); final String dHDist = lastMove.toIsValid ? " (" + StringUtil.formatDiff(hDistance, lastMove.hDistance) + ")" : ""; final String dYDist = lastMove.toIsValid ? " (" + StringUtil.formatDiff(yDistance, lastMove.yDistance) + ")" : ""; builder.append( "hDist: " + hDistance + dHDist + " / " + limitH + " , vDist: " + yDistance + dYDist + " / " + limitV); final PlayerMoveData thisMove = data.playerMoves.getCurrentMove(); if (lastMove.toIsValid) { builder.append( " , fdsq: " + StringUtil.fdec3.format(thisMove.distanceSquared / lastMove.distanceSquared)); } if (thisMove.verVelUsed != null) { builder.append(" , vVelUsed: " + thisMove.verVelUsed); } builder.append(" , model: " + model.id); if (!tags.isEmpty()) { builder.append(" , tags: "); builder.append(StringUtil.join(tags, "+")); } builder.append(" , jumpphase: " + data.sfJumpPhase); thisMove.addExtraProperties(builder, " , "); debug(player, builder.toString()); }
/** * Output information specific to player-move events. * * @param player * @param from * @param to * @param mcAccess */ public static void outputMoveDebug( final Player player, final PlayerLocation from, final PlayerLocation to, final double maxYOnGround, final MCAccess mcAccess) { final StringBuilder builder = new StringBuilder(250); final Location loc = player.getLocation(); // TODO: Differentiate debug levels (needs setting up some policy + document in // BuildParamteres)? if (BuildParameters.debugLevel > 0) { builder.append("\n-------------- MOVE --------------\n"); builder.append(player.getName() + " " + from.getWorld().getName() + ":\n"); addMove(from, to, loc, builder); } else { builder.append(player.getName() + " " + from.getWorld().getName() + " "); addFormattedMove(from, to, loc, builder); } final double jump = mcAccess.getJumpAmplifier(player); final double speed = mcAccess.getFasterMovementAmplifier(player); final double strider = BridgeEnchant.getDepthStriderLevel(player); if (BuildParameters.debugLevel > 0) { try { // TODO: Check backwards compatibility (1.4.2). Remove try-catch builder.append( "\n(walkspeed=" + player.getWalkSpeed() + " flyspeed=" + player.getFlySpeed() + ")"); } catch (Throwable t) { } if (player.isSprinting()) { builder.append("(sprinting)"); } if (player.isSneaking()) { builder.append("(sneaking)"); } if (player.isBlocking()) { builder.append("(blocking)"); } final Vector v = player.getVelocity(); if (v.lengthSquared() > 0.0) { builder.append("(svel=" + v.getX() + "," + v.getY() + "," + v.getZ() + ")"); } } if (speed != Double.NEGATIVE_INFINITY) { builder.append("(e_speed=" + (speed + 1) + ")"); } final double slow = PotionUtil.getPotionEffectAmplifier(player, PotionEffectType.SLOW); if (slow != Double.NEGATIVE_INFINITY) { builder.append("(e_slow=" + (slow + 1) + ")"); } if (jump != Double.NEGATIVE_INFINITY) { builder.append("(e_jump=" + (jump + 1) + ")"); } if (strider != 0) { builder.append("(e_depth_strider=" + strider + ")"); } // Print basic info first in order NCPAPIProvider.getNoCheatPlusAPI() .getLogManager() .debug(Streams.TRACE_FILE, builder.toString()); // Extended info. if (BuildParameters.debugLevel > 0) { builder.setLength(0); // Note: the block flags are for normal on-ground checking, not with yOnGrond set to 0.5. from.collectBlockFlags(maxYOnGround); if (from.getBlockFlags() != 0) builder.append( "\nfrom flags: " + StringUtil.join(BlockProperties.getFlagNames(from.getBlockFlags()), "+")); if (from.getTypeId() != 0) addBlockInfo(builder, from, "\nfrom"); if (from.getTypeIdBelow() != 0) addBlockBelowInfo(builder, from, "\nfrom"); if (!from.isOnGround() && from.isOnGround(0.5)) builder.append(" (ground within 0.5)"); to.collectBlockFlags(maxYOnGround); if (to.getBlockFlags() != 0) builder.append( "\nto flags: " + StringUtil.join(BlockProperties.getFlagNames(to.getBlockFlags()), "+")); if (to.getTypeId() != 0) addBlockInfo(builder, to, "\nto"); if (to.getTypeIdBelow() != 0) addBlockBelowInfo(builder, to, "\nto"); if (!to.isOnGround() && to.isOnGround(0.5)) builder.append(" (ground within 0.5)"); NCPAPIProvider.getNoCheatPlusAPI() .getLogManager() .debug(Streams.TRACE_FILE, builder.toString()); } }
/** * @param player * @param from * @param to * @param data * @param cc * @param time Milliseconds. * @return */ public Location check( final Player player, final PlayerLocation from, final PlayerLocation to, final MovingData data, final MovingConfig cc, final long time, final boolean useBlockChangeTracker) { // Reset tags, just in case. tags.clear(); // Some edge data for this move. final GameMode gameMode = player.getGameMode(); final ModelFlying model = cc.getModelFlying(player, from); final PlayerMoveData thisMove = data.playerMoves.getCurrentMove(); // if (!data.thisMove.from.extraPropertiesValid) { // // TODO: Confine by model config flag or just always do [if the latter: do it in // the listener]? // data.thisMove.setExtraProperties(from, to); // } thisMove.modelFlying = model; final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove(); // Calculate some distances. final double yDistance = thisMove.yDistance; final double hDistance = thisMove.hDistance; final boolean flying = gameMode == BridgeMisc.GAME_MODE_SPECTATOR || player.isFlying(); final boolean sprinting = time <= data.timeSprinting + cc.sprintingGrace; // Lost ground, if set so. if (model.ground) { MovingUtil.prepareFullCheck(from, to, thisMove, Math.max(cc.yOnGround, cc.noFallyOnGround)); if (!thisMove.from.onGroundOrResetCond) { if (from.isSamePos(to)) { if (lastMove.toIsValid && lastMove.hDistance > 0.0 && lastMove.yDistance < -0.3 // Copy and paste from sf. && LostGround.lostGroundStill( player, from, to, hDistance, yDistance, sprinting, lastMove, data, cc, tags)) { // Nothing to do. } } else if (LostGround.lostGround( player, from, to, hDistance, yDistance, sprinting, lastMove, data, cc, useBlockChangeTracker ? blockChangeTracker : null, tags)) { // Nothing to do. } } } // Horizontal distance check. double[] resH = hDist( player, from, to, hDistance, yDistance, sprinting, flying, lastMove, time, model, data, cc); double limitH = resH[0]; double resultH = resH[1]; // Check velocity. if (resultH > 0) { double hFreedom = data.getHorizontalFreedom(); if (hFreedom < resultH) { // Use queued velocity if possible. hFreedom += data.useHorizontalVelocity(resultH - hFreedom); } if (hFreedom > 0.0) { resultH = Math.max(0.0, resultH - hFreedom); if (resultH <= 0.0) { limitH = hDistance; } tags.add("hvel"); } } else { data.clearActiveHorVel(); // TODO: test/check ! } resultH *= 100.0; // Normalize to % of a block. if (resultH > 0.0) { tags.add("hdist"); } // Vertical move. double limitV = 0.0; // Limit. double resultV = 0.0; // Violation (normalized to 100 * 1 block, applies if > 0.0). // Distinguish checking method by y-direction of the move. if (yDistance > 0.0) { // Ascend. double[] res = vDistAscend(from, to, yDistance, flying, thisMove, lastMove, model, data, cc); resultV = Math.max(resultV, res[1]); limitV = res[0]; } else if (yDistance < 0.0) { // Descend. double[] res = vDistDescend(from, to, yDistance, flying, lastMove, model, data, cc); resultV = Math.max(resultV, res[1]); limitV = res[0]; } else { // Keep altitude. double[] res = vDistZero(from, to, yDistance, flying, lastMove, model, data, cc); resultV = Math.max(resultV, res[1]); limitV = res[0]; } // Velocity. if (resultV > 0.0 && data.getOrUseVerticalVelocity(yDistance) != null) { resultV = 0.0; tags.add("vvel"); } // Add tag for maximum height check (silent set back). final double maximumHeight = model.maxHeight + player.getWorld().getMaxHeight(); if (to.getY() > maximumHeight) { // TODO: Allow use velocity there (would need a flag to signal the actual check below)? tags.add("maxheight"); } resultV *= 100.0; // Normalize to % of a block. if (resultV > 0.0) { tags.add("vdist"); } final double result = Math.max(0.0, resultH) + Math.max(0.0, resultV); if (data.debug) { outpuDebugMove(player, hDistance, limitH, yDistance, limitV, model, tags, data); } // Violation handling. Location setBack = null; // Might get altered below. if (result > 0.0) { // Increment violation level. data.creativeFlyVL += result; // Execute whatever actions are associated with this check and the violation level and find // out if we // should cancel the event. final ViolationData vd = new ViolationData(this, player, data.creativeFlyVL, result, cc.creativeFlyActions); if (vd.needsParameters()) { vd.setParameter( ParameterName.LOCATION_FROM, String.format(Locale.US, "%.2f, %.2f, %.2f", from.getX(), from.getY(), from.getZ())); vd.setParameter( ParameterName.LOCATION_TO, String.format(Locale.US, "%.2f, %.2f, %.2f", to.getX(), to.getY(), to.getZ())); vd.setParameter( ParameterName.DISTANCE, String.format(Locale.US, "%.2f", TrigUtil.distance(from, to))); if (!tags.isEmpty()) { vd.setParameter(ParameterName.TAGS, StringUtil.join(tags, "+")); } } if (executeActions(vd).willCancel()) { // Compose a new location based on coordinates of "newTo" and viewing direction of // "event.getTo()" // to allow the player to look somewhere else despite getting pulled back by NoCheatPlus. setBack = data.getSetBack(to); } } else { // Maximum height check (silent set back). if (to.getY() > maximumHeight) { setBack = data.getSetBack(to); } if (setBack == null) { // Slowly reduce the violation level with each event. data.creativeFlyVL *= 0.97; } } // Return setBack, if set. if (setBack != null) { // Check for max height of the set-back. if (setBack.getY() > maximumHeight) { // Correct the y position. setBack.setY(getCorrectedHeight(maximumHeight, setBack.getWorld())); if (data.debug) { debug(player, "Maximum height exceeded by set-back, correct to: " + setBack.getY()); } } data.sfJumpPhase = 0; return setBack; } else { // Adjust the set-back and other last distances. data.setSetBack(to); // Adjust jump phase. if (!thisMove.from.onGroundOrResetCond && !thisMove.to.onGroundOrResetCond) { data.sfJumpPhase++; } else if (thisMove.touchedGround && !thisMove.to.onGroundOrResetCond) { data.sfJumpPhase = 1; } else { data.sfJumpPhase = 0; } return null; } }
/** * This will be called from within the plugin in onEnable, after registration of all core * listeners and components. After each components addition processQueuedSubComponentHolders() * will be called to allow registries for further optional components. * * @param plugin * @return */ public Collection<Object> getAvailableComponentsOnEnable(NoCheatPlus plugin) { final List<Object> available = new LinkedList<Object>(); // Add components (try-catch). // TODO: catch ClassNotFound, incompatibleXY rather !? // Check: inventory.fastconsume. try { // TODO: Static test methods !? FastConsume.testAvailability(); available.add(new FastConsume()); NCPAPIProvider.getNoCheatPlusAPI() .addFeatureTags("checks", Arrays.asList(FastConsume.class.getSimpleName())); } catch (Throwable t) { StaticLog.logInfo("Inventory checks: FastConsume is not available."); } // Check: inventory.gutenberg. try { Gutenberg.testAvailability(); available.add(new Gutenberg()); NCPAPIProvider.getNoCheatPlusAPI() .addFeatureTags("checks", Arrays.asList(Gutenberg.class.getSimpleName())); } catch (Throwable t) { StaticLog.logInfo("Inventory checks: Gutenberg is not available."); } // Hot fix: falling block end portal. try { HotFixFallingBlockPortalEnter.testAvailability(); available.add(new HotFixFallingBlockPortalEnter()); NCPAPIProvider.getNoCheatPlusAPI() .addFeatureTags( "checks", Arrays.asList(HotFixFallingBlockPortalEnter.class.getSimpleName())); } catch (RuntimeException e) { } // ProtocolLib dependencies. if (protocolLibPresent.isAvailable()) { // Check conditions. boolean protocolLibAvailable = false; for (final IActivation condition : protocolLibActivation) { if (condition.isAvailable()) { protocolLibAvailable = true; break; } } // Attempt to react. if (protocolLibAvailable) { try { available.add(new ProtocolLibComponent(plugin)); } catch (Throwable t) { StaticLog.logWarning("Failed to set up packet level access with ProtocolLib."); if (ConfigManager.getConfigFile().getBoolean(ConfPaths.LOGGING_EXTENDED_STATUS)) { NCPAPIProvider.getNoCheatPlusAPI().getLogManager().debug(Streams.INIT, t); } } } else { List<String> parts = new LinkedList<String>(); parts.add("Packet level access via ProtocolLib not available, supported configurations: "); for (IDescriptiveActivation cond : protocolLibActivation) { parts.add(cond.getNeutralDescription()); } StaticLog.logWarning(StringUtil.join(parts, " | ")); } } else { StaticLog.logInfo("Packet level access: ProtocolLib is not available."); } return available; }