-
Notifications
You must be signed in to change notification settings - Fork 637
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
HL - Weapons events are sometimes called twice #1621
Comments
I am guessing that this is a time interpolation issue due to the client time being adjusted to match the server time on the client side (client.dll). I other words m_flNextPrimaryAttack gets 0.0 or less two or more times on the client. You will notice that the code for reacting to m_flNextPrimaryAttack and so on is quite different on the client and serverside (check I am not sure how to fix this properly without changing really much, also would be interesting if the original Half-Life had the same issues. I have no explanation why this doesn't happen in Blue Shift and Opposing Force, maybe they have different default networking settings, or your network test environment was different? |
For possible solutions one would need to keep in mind that people can rewind and forward the demo in the viewdemo demoplayer. |
I am using default settings everywhere except for keys binding, video, sound, player (name/model) settings. |
While I cannot make a new branch, at this time, I would like to suggest you the following: This is the code for handling secondary attack events. if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) )
{
if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] )
{
m_fFireOnEmpty = TRUE;
} This is the code for handling primary attack events. else if ((m_pPlayer->pev->button & IN_ATTACK) && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, UseDecrement() ) )
{
if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) )
{
m_fFireOnEmpty = TRUE;
} Add a m_fFireOnEmpty = TRUE;
return; Additionally, Seeing this line after SecondaryAttack(): SecondaryAttack();
m_pPlayer->pev->button &= ~IN_ATTACK2; I recommend adding this equivalence in the primary attack block, after PrimaryAttack(): PrimaryAttack();
m_pPlayer->pev->button &= ~IN_ATTACK; Hope it helps. |
The return statement would make it not play the empty sound I guess. Also I am guessing / hoping that @JoelTroch only checked / printed after the check for if the clip is empty in PrimaryAttack / SecondaryAttack. Also not sure if the button state trickery is a good idea. |
I tried @malortie's code on a clean SDK, it just make the bug more "visible" and as @ripieces stated, the empty sound is no longer. I tried the same fixes client side and it doesn't work. @ripieces The "event counter" you see in the video is coded client side, it's just a CVAR in |
UPDATE: This issue isn't present if you compile without the |
Thanks for trying the suggestion. Glad to see that a bit more of the nature of the problem was identified. @JoelTroch, When not compiling using |
@malortie I was talking about the issue. The empty sound not playing happens if you compile with |
I setup up two variables, m_iPrimaryAttackCount & m_iSecondaryAttackCount which hold values on how many times both PrimaryAttack() & SecondaryAttack() get called. When I was testing, without return statements added as in my suggestions, changes made , I noticed that when you keep holding the Having put the return statements and using a weapon(.i.e: 9mmAR), Here is a link to a quick branch I created from master. Have a look for places where I put preprocessor directives |
Hey, how come whenever I look to make sure my thread isn't a duplicate, I never find anything? I looked all over, and the guy who recommended I post the bug report here said no one else had ever posted anything like this. It's just my luck, I swear! I never get things right... |
It's fine, either way you attract attention to the original topic. Also, about this issue, i seem on CS 1.6 a similar issue with the weapon showing shooting 2 shots when you're only shooting one. |
After a lot of digging i think i've found the cause of this. This code here: Lines 1192 to 1221 in c7240b9
Is used to calculate the next attack delay for predicted weapons. This involves a lot of floating point math. Sometimes the resulting value will have a tiny amount of extra value added, so for example when the next attack time is 0.1, it could actually be 0.1000012344. Because of this, when the server side weapons code checks if it can attack: Lines 627 to 641 in c7240b9
This resolves to false: On the client side however, the delay is always constant (0.1). So there is no floating point math error that creeps in and results in slightly larger values. For example this is data i was getting:
Note how the client value is actually slightly smaller than a whole value. Instead of 0.01 it's 0.00999999 which is also a rounding error, but in the other direction which doesn't cause this problem. The client value is decremented down to 0 and thus passes the condition to fire. The server does not. Every now and then this results in the client getting an extra shot in. Once i added in rounding logic to constrain the values returned by GetNextAttackDelay and changed CanAttack to compare against 0.0001 instead of 0 the problem stopped appearing:
So to fix this the code that calculates the next attack delay needs to round its resulting value down to a few decimals, and the code that checks if you can fire needs to account for the value being a very small positive value as a result of rounding errors. Combined this should eliminate the problem altogether. The required changes are:
And in CanAttack:
And additionally the I've tested this at both a tick rate of 100 and 500 and both produce correct results. |
Looks like this fix doesn't work for every weapon. The glock at least can still glitch out with this in place. I'm investigating to see what can be done. |
Ok, i figured out what's wrong with my solution. The above fix was rounding the To avoid this, the rounding should be performed on the server side without allowing the rounded value to be networked to clients. This is done by only rounding in
This emulates the rounding that occurs when a value is networked to clients. As specified in
The client then reads it as an int along with the sign bit, divides it by 1000 and assigns it to the weapon data By performing the same operations on the value passed into Note that this will increase the rate of fire by a small amount. It's not very significant though. Also, i checked Source to see what it does. It differs in 2 important ways:
|
On vanilla Half-Life, weapons events are sometimes called twice, this means that a single shot can eject 2 shells, play the firing sound twice and/or paint 2 decals.
This issue mostly happens with firearms (Glock, Python, MP5 and Shotgun), other weapons may be affected.
I haven't been able to reproduce this on Blue Shift and Opposing Force.
I made a video to understand the issue better, you can view it on YouTube here : https://www.youtube.com/watch?v=WKEImTN9H3o
Notice that I have fired 2 whole magazines (100 shots in total) and that the counter indicates that the event has been called 104 times.
If you take a very close look at 9 seconds on the video when I fire the 74th bullet, you can see the counter reporting "Event has been called 76 times" and 2 shells being ejected. You can see the same at 11 seconds when I have 5 bullets left in the second magazine.
The text was updated successfully, but these errors were encountered: