检查玩家是否可以在所述位置放置一个区块

时间:2017-07-29 06:51:02

标签: java algorithm bukkit a-star anti-cheat

我正在创建一个自定义反作弊。但是,我已经到了一个令我非常难过的地步。我试图检测玩家是否可以在所述位置放置一个区块,但是当我试图使其对非作弊玩家更可靠时,它变得越来越复杂。目前,每当玩家与某个区块(AxisAllignedBB)进行互动时,我会合并一个光线投射算法(使用PlayerInteractEvent),以查看该玩家是否实际上正在查看Block和{{1}事件说他们是。我认为,问题在于玩家的方向每秒只会更新20次,而他们的帧速率可能要高得多。这通常会导致(大约每15个左右我的测试中的一个块位置)BlockFace被错误地取消。

用于查找Block的Raycast算法

PlayerInteractEvent

然而,我怀疑这是问题所在。从我的测试来看,它完美无缺。因此,我认为我必须依赖其他方法。这里有一些想法,但我不确定它们是否令人满意。

想法:近块

我已经考虑过查看放置的块是否在光线投射中发现的块的半径为1块(或者如果我正在查看距离光线的最近距离可能更短),但是这提供了太多问题。如果玩家将光标从障碍物移动到更远的区域,则会触发作弊的误报。另一方面,如果玩家有北,东,南,西的拦截支柱但西北,东北等等,玩家仍然可以建在一个完全封闭的区域。

想法:A *路径寻找算法

如果我在光线投射中的光线上有点G-Cost,G-Cost随着距光线的距离增加而H-Cost是距离目标区块的最近距离,我觉得这可以解决这个难题。我可以在 public static Block getTargetBlock(Location location, Vector direction, double rangeSquared, int maxTrials, TargetMethod targetMethod) { Location loc = location.clone(); Vector dir = direction.normalize(); final double directionX = direction.getX(); final double directionY = direction.getY(); final double directionZ = direction.getZ(); Block block = loc.getBlock(); for (int i = 0; i <= maxTrials; i++) { final double locX = loc.getX(); final double locY = loc.getY(); final double locZ = loc.getZ(); double wholeMoreX = wholeMore(locX,directionX); double moreX = Math.abs(wholeMoreX /directionX); double wholeMoreY = wholeMore(locY,directionY); double moreY = Math.abs(wholeMoreY /directionY); double wholeMoreZ = wholeMore(locZ,directionZ); double moreZ = Math.abs(wholeMoreZ /directionZ); if(moreX < moreY && moreX < moreZ){ if(directionX > 0) block = block.getRelative(BlockFace.EAST); else { block = block.getRelative(BlockFace.WEST); } } else if(moreY < moreX && moreY < moreZ){ if(directionY > 0){ block = block.getRelative(BlockFace.UP); } else{ block = block.getRelative(BlockFace.DOWN); } } else{ if(directionZ > 0){ block = block.getRelative(BlockFace.SOUTH); } else{ block = block.getRelative(BlockFace.NORTH); } } final double scalar = Math.min(Math.min(moreX,moreY),moreZ); Vector addAmount = dir.clone().multiply(scalar); loc.add(addAmount); if(loc.distanceSquared(location) > rangeSquared) return null; AxisAlignedBB boundry = getBoundry(block,targetMethod); if(boundry != null) if(blockFaceCollide(location,direction,boundry) != null) return block; } return null; } 取消之前设置最大G-Cost阈值。然而,问题是将A *与各种AxisAllignedBB块相结合似乎很难。我或许可以创建一个网格,每个块包含100x100x100点,但我不确定这是否有效,也不是最佳实践。

想法:查看玩家是否可以看到该块

这将是非常有效的,但我不确定它是否真实。为此,每次玩家放置一个区块时,我需要检测哪些区块将与玩家的交互半径中的其他区块完全重叠。取所有最终的非重叠块,我可以看到交互的块是否包含这些块。如果没有,交互将被取消。这似乎可能会受到性能影响,我可以看到作弊也会有一些误报。

3 个答案:

答案 0 :(得分:2)

我建议创建一个方法,通知播放器和块是否相交。

示例代码

public static final double ONE_UNIT = 1.0;
public static final double ZERO_UNIT = 0.0;


public static Location getPlayerBlockIntersection(Player player, Block target) {
    if (player == null || target == null) {
        return null;
    }


    double minX = target.getX();
    double minY = target.getY();
    double minZ = target.getZ();

    double maxX = minX + ONE_UNIT;
    double maxY = minY + ONE_UNIT;
    double maxZ = minZ + ONE_UNIT;


    Location origin = player.getEyeLocation();
    double originX = origin.getX();
    double originY = origin.getY();
    double originZ = origin.getZ();


    Vector dir = origin.getDirection();
    double dirX = dir.getX();
    double dirY = dir.getY();
    double dirZ = dir.getZ();


    double divX = ONE_UNIT / dirX;
    double divY = ONE_UNIT / dirY;
    double divZ = ONE_UNIT / dirZ;

    double t0 = ZERO_UNIT;
    double t1 = Double.MAX_VALUE;

    double imin, imax, iymin, iymax, izmin, izmax;

    if (dirX >= ZERO_UNIT) {
        imin = (minX - originX) * divX;
        imax = (maxX - originX) * divX;
    } else {
        imin = (maxX - originX) * divX;
        imax = (minX - originX) * divX;
    }


    if (dirY >= ZERO_UNIT) {
        iymin = (minY - originY) * divY;
        iymax = (maxY - originY) * divY;
    } else {
        iymin = (maxY - originY) * divY;
        iymax = (minY - originY) * divY;
    }

    if ((imin > iymax) || (iymin > imax)) {
        return null;
    }

    if (iymin > imin) {
        imin = iymin;
    }

    if (iymax < imax) {
        imax = iymax;
    }

    if (dirZ >= ZERO_UNIT) {
        izmin = (minZ - originZ) * divZ;
        izmax = (maxZ - originZ) * divZ;
    } else {
        izmin = (maxZ - originZ) * divZ;
        izmax = (minZ - originZ) * divZ;
    }

    if ((imin > izmax) || (izmin > imax)) {
        return null;
    }

    if (izmin > imin) {
        imin = izmin;
    }
    if (izmax < imax) {
        imax = izmax;
    }

    if ((imin >= t1) || (imax <= t0)) {
        return null;
    }

    // check this baby and see if both objects represent an intersection:
    Location intersection = origin.add(dir.multiply(imin));
    return intersection;
}

答案 1 :(得分:0)

我不确定这是否有效。但是我想到了如何使用BlockPlaceEvent并检查当播放器正在查看该块时放置块。

public class ReportDailyData
{
    public DateTime UtcDT;
    public double Day_Pnl;
    public int TradeCount;
    public int Volume;

    public ReportDailyData(DateTime utcDT, double day_Pnl, int tradeCount, int volume)
    {
        UtcDT = utcDT;
        Day_Pnl = day_Pnl;
        TradeCount = tradeCount;
        Volume = volume;
    }

    public string AsString()
    {
        return UtcDT.ToString("yyyyMMdd") + "," + Day_Pnl.ToString("F2") + "," + TradeCount + "," + Volume;
    }
}

public static DataTable Data;
public static DataSpecification DataSpec;

public void Go()
{
     //Fill Data and DataSpec elsewhere

     var dailylist = GetDailyData();
}

   public List<ReportDailyData> GetDailyData()
    {
        List<ReportDailyData> dailyDatas = new List<ReportDailyData>();
        DateTime currentDT = DataSpec.FromDT.Date;
        while (currentDT <= DataSpec.ToDT.Date)
        {
            var rowsForCurrentDT = Data.AsEnumerable().Where(x => x.Field<DateTime>("utcDT").Date == currentDT).ToList();
            if (rowsForCurrentDT.Any())
            {
                double day_Pnl = rowsForCurrentDT.Sum(x => x.Field<double>("Bar_Pnl"));
                var positions = rowsForCurrentDT.Select(x => x.Field<double>("Position")).ToList();
                var deltas = positions.Zip(positions.Skip(1), (current, next) => next - current);
                int tradeCount = deltas.Where(x => x != 0).Count();
                int volume = (int)deltas.Where(x => x != 0).Sum(x => Math.Abs(x));
                dailyDatas.Add(new ReportDailyData(currentDT, day_Pnl, tradeCount, volume));
            }
            else
            {
                dailyDatas.Add(new ReportDailyData(currentDT, 0, 0, 0));
            }

            currentDT = currentDT.AddDays(1);
        }

        return dailyDatas;

    }

随意留下建议。

答案 2 :(得分:0)

我几次读完这个问题以确定。如果我明白这个前提, 您希望验证玩家何时发生互动 看着与之互动的街区。我认为你想要防止&#34;自动构建&#34; mods或 可能会伪造此类事件的那种。

使用Player.getTargetBlock()验证应该是直截了当的。 如果getTargetBlock()返回的块与报告的块相同 通过PlayerInteractEvent你应该有理由相信玩家是 看着街区。