Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Something wrong with the Damage System. #2650

Open
V3SC opened this issue Jul 18, 2024 · 3 comments
Open

Something wrong with the Damage System. #2650

V3SC opened this issue Jul 18, 2024 · 3 comments
Assignees
Labels
bug triage Needs a preliminary assessment to determine the urgency and required action

Comments

@V3SC
Copy link

V3SC commented Jul 18, 2024

What happened?

There is a problem with applying damage. Attacker -> Server -> ??

Attacker sends the request to the server -> Server successfully receives the request. But sometimes it has problems when applying the damage to the victim. Or does the server never send the damage to the victim? I don't know.

As you can see in the video, he shoot 5 bullets at the other player, but only 1 of them was applied to the player.

All damage requests are handled successfully in weaponDamageEvent. However, there is a problem when applying the damage to the target player. Some servers are waiting 50ms after the weaponDamageEvent is triggered and manually applying damage to the player if the damage has not been applied.

So far nothing has been done for PvP Servers. Please pay attention to this issue.

Example video:

KEK.mp4

Expected result

Server should apply every damage to the player without any problem.

Reproduction steps

.

Importancy

Unknown

Area(s)

FiveM

Specific version(s)

FiveM/ Every Artifact

Additional information

No response

@V3SC V3SC added bug triage Needs a preliminary assessment to determine the urgency and required action labels Jul 18, 2024
@FabianTerhorst FabianTerhorst self-assigned this Jul 18, 2024
@YasinY
Copy link

YasinY commented Jul 21, 2024

Pretty sure a lot of factors play into account on your issue.
Factors like the dedicated server the server is on, the network up to the server side resmon / load.

To factor those out, you might want to create your own custom sync

@V3SC
Copy link
Author

V3SC commented Jul 21, 2024

Pretty sure a lot of factors play into account on your issue. Factors like the dedicated server the server is on, the network up to the server side resmon / load.

To factor those out, you might want to create your own custom sync

Dedicated Server.
Ryzen 9 3900x CPU (+400 Average usage is %10)
10Gbit Network. (+400 Average usage is 50/100 Mbps.)

And if you read the issue details, the weaponDamageEvent triggers successfully no matter what and it's just a problem to apply the damage. So this is not a network or server related problem it's a Fivem / GTA issue.

@AvarianKnight
Copy link
Contributor

The other client has to accept the damage event for it to be applied to their ped, you can try to use weaponDamageReply to see if they are actually accepting the damage event that the other client does.

This might be helpful in narrowing down the issue.

Do note that this can be inaccurate, there is no way to know in what order the client will reply to messages (if at all), so the caller/target could be wrong.

Here is an (over-engineered) example (note this isn't tested):

export interface WeaponDamageEvent {

	actionResultId: number;
	actionResultName: number;

	damageFlags: number;
	damageTime: Timestamp;
	damageType: number;

	f104: number;
	f112: boolean;
	f112_1: number;
	f120: number;
	f133: boolean;
	hasActionResult: boolean;
	hasImpactDir: boolean;
	hasVehicleData: boolean;
	hitComponent: number;
	hitEntityWeapon: boolean;
	hitGlobalId: number;
	hitGlobalIds: number[];
	hitWeaponAmmoAttachment: boolean;
	impactDirX: number;
	impactDirY: number;
	impactDirZ: number;
	isNetTargetPos: boolean;
	localPosX: number;
	localPosY: number;
	localPosZ: number;
	overrideDefaultDamage: boolean;
	parentGlobalId: number;
	silenced: boolean;
	suspensionIndex: number;
	tyreIndex: number;
	weaponDamage: number;
	weaponType: number;
	willKill: boolean;
};

type Source = number;
type Timestamp = number;

interface PendingDamageData {
	caller: Source,
	health: number,
	timestamp: Timestamp
}

class DamageHandler {
	private static pendingDamages = new Map<Source, PendingDamageData[]>();

	static get PendingDamageMap(): Map<Source, PendingDamageData[]> {
		return this.pendingDamages;
	}

	static add(target: Source, damageData: PendingDamageData): void {
		const pending = this.get(target);
		if (pending) {
			pending.push(damageData);
		} else {
			this.pendingDamages.set(target, [damageData])
		}

	}

	static popFrontForTarget(target: Source): PendingDamageData | null {
		const pending = this.get(target);
		// don't know how we got here, but we have no pending response!
		if (!pending) return null;
		const data = pending.shift();
		// cleanup data from the map if there are no more fields or if we just
		// removed the last one.
		if (!data || pending.length === 0) {
			this.pendingDamages.delete(target);
		}
		// kep our return type consistent, null if it doesn't exist
		if (!data) return null;
		return data;
	}

	static get(target: Source): PendingDamageData[] | undefined {
		return this.pendingDamages.get(target);
	}

	static delete(target: Source): void {
		this.pendingDamages.delete(target);
	}
}

on("weaponDamageEvent", (caller: string, event: WeaponDamageEvent) => {
	const tgt = event.hitGlobalId;
	if (tgt === 0) return;
	const ent = NetworkGetEntityFromNetworkId(tgt);
	if (!IsPedAPlayer(ent)) return;
	const owner = NetworkGetEntityOwner(ent);
	DamageHandler.add(owner, {
		caller: parseInt(caller),
		health: GetEntityHealth(ent),
		timestamp: event.damageTime
	})
})

interface WeaponDamageReply {
	health: number;
	time: Timestamp;
	f131: boolean; // rejected?
}

on("weaponDamageReply", (caller: string, event: WeaponDamageReply) => {
	const src = parseInt(caller);
	const pending = DamageHandler.popFrontForTarget(src);
	if (!pending) return console.log(`Got damage reply without a pending damage request`);
	console.log(`Got damage reply, original health: ${pending.health}, new hp: ${event.health}, was rejected: ${event.f131}, timestamp diff: ${event.time - pending.timestamp}`);
})

setInterval(() => {
	const gameTime = GetGameTimer();
	for (const [target, callerData] of DamageHandler.PendingDamageMap) {
		// if we haven't received a reply after a second then we likely aren't
		// going to recieve one at all, clean it up and note that it didn't ever
		// get a reply
		const deleteForIndicies = [];
		for (const [index, data] of callerData.entries()) {
			if ((data.timestamp + 1000) < gameTime) {
				console.log(`${target} didn't send a reply to ${data.caller} damage event`);
				deleteForIndicies.push(index);
			}
		}

		// the indicies should be in order so we'll just reverse it and delete
		// from end -> begin so we don't remove the wrong indexs
		const reversed = deleteForIndicies.reverse();
		for (const index of reversed) {
			callerData.splice(index, 1);
		}

		// if we removed all of the indicies from the array then we want to
		// remove it from the damage map
		if (callerData.length === 0) {
			// i don't remember if this needs to be called when not inside an
			// iterator
			DamageHandler.delete(target);
		}
	}

}, 1000)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug triage Needs a preliminary assessment to determine the urgency and required action
Projects
None yet
Development

No branches or pull requests

4 participants