chuck.js/app/Game/Channel/GameObjects/Doll.js
Jeena 71e4b4e847 Replace cheat-detection teleport with server reconciliation
The old PUNKBUSTER check compared client-reported position to server
position and snapped the player back when latency made them diverge,
which felt like getting teleported under any real network conditions.

Replaces that with proper client-side prediction + reconciliation:
client tags each input with a sequence number and keeps an input
buffer; server tracks the last processed sequence and reports its
authoritative position via a per-user inputAck alongside each
worldUpdate. The client only corrects when the actual disagreement
exceeds what the unacked input time can explain — so steady-state
movement runs purely on local physics, and only genuine unexpected
events (collisions, being hit) trigger a smooth blend toward the
server state.

Includes adaptive threshold scaling so high-latency sessions don't
false-positive corrections during normal running.
2026-05-11 00:37:42 +00:00

109 lines
No EOL
3.5 KiB
JavaScript
Executable file

define([
"Game/Core/GameObjects/Doll",
"Game/Channel/GameObjects/Item",
"Lib/Vendor/Box2D",
"Lib/Utilities/NotificationCenter"
],
function (Parent, Item, Box2D, nc) {
"use strict";
function Doll(physicsEngine, uid, player) {
Parent.call(this, physicsEngine, uid, player);
}
Doll.prototype = Object.create(Parent.prototype);
Doll.prototype.findCloseItem = function(x) {
var self = this;
function findItem(array) {
for (var i = 0; i < array.length; i++) {
var item = array[i];
if(item.isGrabbingAllowed(self.player)) {
return item;
}
}
}
if (x < 0) { // looking left
return findItem(this.reachableItems.left);
} else {
return findItem(this.reachableItems.right);
}
};
Doll.prototype.onImpact = function(isColliding, fixture) {
var self = this;
Parent.prototype.onImpact.call(this, isColliding, fixture);
if(isColliding) {
var otherBody = fixture.GetBody();
if(otherBody) {
var item = otherBody.GetUserData();
if(item instanceof Item) {
var itemVelocity = item.body.GetLinearVelocity();
//var itemMass = item.body.GetMass();
var ownVelocity = this.body.GetLinearVelocity();
var b2Math = Box2D.Common.Math.b2Math;
var absItemVelocity = b2Math.AbsV(itemVelocity);
var min = 1;
var damage = 0;
if(absItemVelocity.x > min || absItemVelocity.y > min) {
if(item.lastMoved && item.lastMoved.player != this.player) {
var collision = b2Math.SubtractVV(itemVelocity, ownVelocity);
// Tested max velocity banana: 50
var velocityDamage = collision.Length() / 50;
// Max weight of piano: 15
var weightDamage = item.options.weight / 15;
// Max danger of knife: 3
var dangerDamage = item.options.danger / 3;
// + 0.5 and / 2: offsetting for lower velocity impact
// * 300: tested imperically by throwing piano from deadly height
// * 80: tested imperically by throwing knife fast
damage = (velocityDamage + 0.5) * (weightDamage * 300 + dangerDamage * 80) / 2;
var lastMovedPlayer = item.lastMoved.player;
var callback = function() {
self.player.addDamage(damage, lastMovedPlayer, item);
};
nc.trigger(nc.ns.channel.engine.worldQueue.add, callback);
}
}
// only set lastMovedBy if player wasn't hurt by collision
if (damage === 0) {
item.setLastMovedBy(this.player);
}
}
}
}
};
Doll.prototype.getUpdateData = function(getSleeping) {
var updateData = Parent.prototype.getUpdateData.call(this, getSleeping);
if(updateData) {
updateData.as = this.getActionState();
updateData.laxy = this.lookAtXY;
}
return updateData;
};
return Doll;
});