我正在创建一个自定义反作弊。但是,我已经到了一个令我非常难过的地步。我试图检测玩家是否可以在所述位置放置一个区块,但是当我试图使其对非作弊玩家更可靠时,它变得越来越复杂。目前,每当玩家与某个区块(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点,但我不确定这是否有效,也不是最佳实践。
想法:查看玩家是否可以看到该块
这将是非常有效的,但我不确定它是否真实。为此,每次玩家放置一个区块时,我需要检测哪些区块将与玩家的交互半径中的其他区块完全重叠。取所有最终的非重叠块,我可以看到交互的块是否包含这些块。如果没有,交互将被取消。这似乎可能会受到性能影响,我可以看到作弊也会有一些误报。
答案 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
你应该有理由相信玩家是
看着街区。