Facepunch / garrysmod-issues

Garry's Mod issue tracker
143 stars 56 forks source link

Viewmodel sequences are out of sync on client and server #4408

Closed Kinyom closed 4 years ago

Kinyom commented 4 years ago

It seems that viewmodel sequences are slightly out of sync between the client and server.

console Here's my console showing info both clientside and serverside about a sequence being played.

Here's my code for printing the info, which I put at the end of my SWEP:Think hook: if (IsFirstTimePredicted() and self:GetActivity() == ACT_VM_RELOAD) then print(self.Owner:GetViewModel():GetCycle()*self:SequenceDuration(), self.Owner:GetViewModel():GetCycle()*self:SequenceDuration()*30, self:SequenceDuration()) end

What's printed first is how long the sequence has been running, what comes next is what frame it's on (It runs at 30 FPS, and lasts for 57/30 frames), then afterwards is the total sequence duration (technically 1.9, but 1.8999... here due to Source's float imprecision).

As you can see, clientside the sequence ends (once it gets to 1.8980999565804, it stops and freezes on the last frame until the animation is changed), before the sequence has finished serverside. I'm not entirely sure what's causing this or if it's just some inescapable float imprecision issue (I hope not), but the reason this is a big deal is because it makes sending an "idle" animation when an animation is done (via a Get/SetNextIdle networkvar timer set to self:SequenceDuration, then doing SendWeaponAnim( ACT_VM_IDLE ) in SWEP:Think - the classic way) look super bad, since the end result is your viewmodel freezing on its last animation frame for a few ticks/game frames before the idle animation gets really sent/takes effect serverside.

robotboy655 commented 4 years ago

I'd say its more likely to do with your code/logic for playing those idle animations. I don't remember the built-in fists/medkit having same problems and they use pretty much same system for idle anims iirc.

Also it is very much expected that there will be some latency between client and server, which is why you'd use prediction for this as well.

Kinyom commented 4 years ago

For reference, this happened on a listen server. I haven't tested other states but I'm pretty confident the same result will happen.

if (self:GetNextIdle() <= CurTime() and self:GetNextIdle() ~= -1) then
    self:SendWeaponAnim( ACT_VM_IDLE )
    self:SetNextIdle( -1 )
end

This is the code I'm running in my SWEP:Think hook for idle animations (NextIdle is defined in SetupDataTables, of course). I use the following code for sending the reload animation in my reload function:

self:SendWeaponAnim( ACT_VM_RELOAD )
self:SetNextIdle( CurTime() + self:SequenceDuration() )

No "if (SERVER) then"s screwing prediction over or anything - it all works fine.

I have two hunches as to why this weirdness happens. The first is that somehow, the NextIdle "timer" syncs up with the server time, even clientside (so it ends up sending the idle animation when the reload animation would be completed on the server), and the second is that the timing might be right, but SendWeaponAnim might "officially" register on the server, which renders the client timing moot and only really takes into account server timing.

As for why you might not have noticed this happening to the Fists and Medkit, it might be because the idle animations are somewhat subtle compared to the weapon model I'm working with, where the weapon bobbing up and down is pretty noticeable. I'll go in-game and run some more tests.

Kinyom commented 4 years ago

Okay, after some quick testing I can confirm this does affect weapon_fists. I recorded a short video but can't find a good place to upload it. https://gofile.io/?c=UYuk8o Here, try this

Edit: I think I found what's going on. After some tests it looks like SendWeaponAnim does not actually do anything clientside - or at the least, if it does, it doesn't actually send an animation. I believe the NextIdle timer is working fine, but SendWeaponAnim only sends the idle animation on the server. As a result, since the animation is out of sync clientside, it stops for a few frames while it waits for the server to send the animation.

Kinyom commented 4 years ago

Alright, deeper explanation that I think might be what's causing this. It looks like that in the PrimaryAttack hook, sending an animation clientside actually does work. As you're intended to send it serverside to let prediction work, it doesn't play the animation completely correctly (it cannot be interrupted by the same animation being played again, I think is what happens - which is intended behaviour as far as I know, since as I said it needs to be played in both states for prediction stuff), but it still works perfectly fine. However, this is the only hook I've found where this happens. If I send a weapon anim clientside in SWEP:Think, it doesn't work. If I do it outside of the SWEP with lua_run_cl, it doesn't work, et cetera. So I think what's going on here is that some sort of prediction state that allows the correct behaviour to happen might not be set for Think and such, but is set for PrimaryAttack.

robotboy655 commented 4 years ago

That kinda makes sense, SWEP:Think is not predicted. Why don't you try (Re)setSequence functions on the viewmodel?

Kinyom commented 4 years ago

That kinda makes sense, SWEP:Think is not predicted. Why don't you try (Re)setSequence functions on the viewmodel?

I tried it with SendViewModelMatchingSequence and ran into the same result. I’ll try ResetSequence when I get home later.

Kinyom commented 4 years ago

ResetSequence and SetSequence have the same issue.

Also, the wiki says SWEP:Think is predicted. Is it supposed to be?

One more thing, since SWEP:Think isn't predicted I actually hooked into PlayerTick (a hook I'm pretty sure is predicted) and ran my idle behaviour there; I got the same result as if I used Think.

Kinyom commented 4 years ago

Any updates?

Kinyom commented 4 years ago

@robotboy655 Any word on a possible fix/looking into it?

mcNuggets1 commented 4 years ago

You got issue-blocked.

robotboy655 commented 4 years ago

As far as understand, and its really hard to, this is a duplicate of https://github.com/Facepunch/garrysmod-issues/issues/3229

I just fixed that issue, test if yours is now solved or not. If not, I'll need minimal SWEP code + the model to reproduce the issue,

@mcNuggets1 Please refrain from shitposting on this repo, unless of course you want to be blocked.