• Weapon Prediction
    3 replies, posted
So i'm trying to wrap my head around making my weapons fully predicted. Here's a useful post from an old thread: [QUOTE=Shatter;33369381]I've made a swep base that properly predicts all types of weapons. Once you figure out how prediction works, it's really simple. Prediction only happens in MP games. 95% of your swep code needs to be shared, so that it is both ran on the client and the server. The server's CurTime is always a few seconds ahead of the clients. When a shared function is called, like SWEP:Reload, it is called once on the server a little ahead of the clients CurTime, and then called instantly clientside. Then the client interpolates by calling the SWEP:Reload up until the clients CurTime matches the CurTime when the server called it. You can see this happen if you put something like Msg( CurTime() ) inside any of the shared swep functions. It will be printed once serverside, and 3-7ish times clientside. The key to make things predict properly is not to block the interpolation calls the client makes. This can be done by using DTvars, since they already have prediction built in. (NWVars should also, but I have had problems with them.) Almost all functions you use in sweps already support prediction. If you come to a case where you are checking a variable to see if you need to do something, you need to be using a DTvar. When you come to the point of needing to do something only once clientside (like the lerp for moving the viewmodel for ADS) then you use the "if CLIENT and IsFirstTimePredicted() then" conditional check. Serverside, IsFirstTimePredicted() is always true. Do NOT put things like animations/firebullets/setnextprimaryfire in this check, otherwise you will block the client calls and cause prediction errors. None of the public swep bases available predict properly, so don't look at them for help. Hell, even the TTT shotgun reload and garrys css weapons reload is broken. Take a look at the HL2 shotgun code. You can make it work using only 1 DTBool, m_bInReload. Also, the engine has some built in concommands that help a ton when doing this: [url=https://developer.valvesoftware.com/wiki/Cl_predictionlist]cl_predictionlist and cl_pdump[/url] [url=https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking]cl_showerror[/url] cl_showerror will be the most useful, it prints a message to the console every time a prediction error happens. Once you get rid of all the errors, your swep will be perfectly predicted. If you do net_fakelag 400 on the test server, your sweps will still look like you are playing SP :dance: It's 3:30am, I hope this post makes sense, and helps you out.[/QUOTE] My understanding is that shared code is run out of synch (it is run on the client slightly ahead of the server) and so to compensate it gets called over and over on the client until it is in synchronicity with the server. With that in mind, i am trying to make a slightly more intricate weapon base. I need to fix my current code to work in MP. [lua] function SWEP:ReloadThink() if self.ReloadTime and CurTime() >= self.ReloadTime then // the viewmodel is moved fully so play animations self.Weapon:SendWeaponAnim( ACT_VM_RELOAD ) self.Owner:SetAnimation( PLAYER_RELOAD ) self.ReloadEnd = CurTime() + self.Primary.ReloadEnd self.ReloadPlays = {} self.ReloadTime = nil end if self.ReloadEnd then for k,v in pairs( self.ReloadSounds ) do // play custom reload sounds - i cut this code out for readability end if CurTime() >= self.ReloadEnd then // move the viewmodel back and set the clip to full self.Weapon:SetViewModelPosition( self.ReloadPos, self.ReloadAng, self.Primary.AnimSpeed, true ) self.Weapon:SetClip1( self.Primary.ClipSize ) self.ReloadEnd = nil end end end function SWEP:Reload() if self.Weapon:Clip1() == self.Primary.ClipSize or self.Weapon:IsReloading() then return end self.Weapon:SetNextPrimaryFire( CurTime() + self.Primary.ReloadEnd + self.Primary.AnimSpeed * 3 ) self.Weapon:SetViewModelPosition( self.ReloadPos, self.ReloadAng, self.Primary.AnimSpeed ) self.ReloadTime = CurTime() + self.Primary.AnimSpeed // first we move the viewmodel to its reload position before the next part end[/lua] Essentially, SetViewModelPosition is the code that does ironsights except i've modified it to suit my needs. My code works well when i am testing on my own server but i can guarantee it will run like absolute shit in MP. Apparently the HL2MP shotgun uses only a single DTbool in its reload code, how does it manage that? My reload sequences have multiple parts (first the viewmodel moves, then it plays animations, then it moves back) so how can i properly make these sequences predicted? Any help would be appreciated. I think i'm just looking at it all the wrong way.
[QUOTE=twoski;33787722]My reload sequences have multiple parts (first the viewmodel moves, then it plays animations, then it moves back) so how can i properly make these sequences predicted? Any help would be appreciated. I think i'm just looking at it all the wrong way.[/QUOTE] When m_bInReload is true, run the viewmodel animation. Create a simple timer that lasts as long as that animation lasts. Put a timer in it that runs for however long one shell is put in, for number of shells in the max clip. After that, draw back. No timer is needed because your last animation should already be done before you draw back. EDIT: I'm probably completely off.
Here's a quick explanation of prediction: The client is always in the future in the sense of world time. The client is constantly generating moves and then simulating them, which is where you see the IsFirstTimePredicted. These moves haven't necessarily happened on the server yet. Instead what's really happening is that every time the client gets an acknowledge from the server, it will reset the world to the state it received, and then apply all the moves it's been predicting onto that new state, this is where a move has not been predicted before. For example, you could fire your gun on the client (first time prediction), get a world update, and then the client will run that move again, but this time it won't be first time prediction. This is where you would use an IsFirstTimePredicted guard if you have special events like sounds (you can't undo or restart a sound without the player noticing). edit: I actually don't like the example I pasted, the SDK really is the best reference for getting your head around prediction and what to do with networked variabled.
So my current code is usable provided that i put IsFirstTimePredicted() checks in the appropriate places?
Sorry, you need to Log In to post a reply to this thread.