Best way to handle authoritative shot, or a good sanity check?

I’m currently migrating my third person shooter from Unity networking to uLink, and also making it fully authoritative. (Side note: uLink is an absolute joy to develop with).

In it’s current incarnation, I have authoritative movement set up just fine. However, my shooting works similarly to the 3rd person shooter demo project in the resources section, where there is an “aim” object that is positioned via raycast some distance in front of the 3rd person orbit camera’s forward dir, and the gun barrel emits a bullet in the direction of:

bulletDir = (aimTarget.position - barrel.position).

I’m currently handling it “authoritatively” by having the local client spawn “for show” bullets, while sending an RPC to the server with bulletDir as a parameter. The server then calculates the real damage dealing bullet, while at the same time relaying the ShootForShow RPC with the same bulletDir to all the proxy clients.

So the damage dealing is authoritative, but the client is still controlling the direction. How exploitable is that? Is there any sanity checks I could do on the server side? I’d really prefer not to have to add authoritative camera rotation.

In general you don’t need to think about this I would say. No game that I know does what you’re asking, it leaves you open to aim bots but pretty much very FPS/Action 3D Person game is. It’s just to hard check and costs to much on the server. There’s also the possibility of the “aim checking” kicking people that are really good. There are records of CS professionals beating people with aim bots, these people would get kicked by your “camera speed/direction”-detection, as they out-perform the computer.

There’s also the issue of “flukes”, basically someone could pull his mouse 180* and shoot without looking and hitting someone in the head with the first bullet, pure luck - but it would look like “cheating” to your algorithm.

This is a none issue.

Awesome, thanks. That’s what I needed to hear :slight_smile: I thought it might’ve been a little too anal.

I guess if someone actually goes to the trouble to develop an aimbot for my game, it’s probably a good problem to have.

Well, yes I would say! By then you will probably be rolling around in piles of cash, looking something like this:

So I have the authoritative shooting set up now and its working just fine. The only thing is, for obvious reasons, the client has to sort of “lead” the other players because only the server is shooting the real shots that register damage and tell everyone to create a blood particle effect, and the client’s simulation of the players is however many milliseconds behind the real, server simulation.

I was thinking of allowing the client to at least validate whether or not he hit an enemy (but not register damage or create particles), then compare the angle of the clients shot to the server’s shot, and if they’re within a reasonable delta, have the server validate it and broadcast the damage and particles. Does that sound about right?

The way to solve this is properly explained in this document: https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

The short version is this:

  • Have the server store “previous” states for all interesting entities
  • When a client fire it’s weapon, it sends it’s “shoot” command to the server
  • The server derives a somewhat correct “game time” for the client using ping + some other time variables (game dependent)
  • The server rewinds (yes, rewinds) the world state (positions, possibly animations)
  • The server performs the hit calculation against the “rewinded” world state
  • The server resets the world to it’s “real” state

This is what commonly is called “lag compensation” and allows you to point directly at peoples heads in games like CS; BattleField, etc. and actually shoot where you aim.

The version you proposed works also, but is less accurate.

I love when this happens, because it’s a great opportunity to get insight into a person by their reaction. Often, when they get a lucky shot, they’re just that good, when they are the recipient of a lucky shot, it’s cheating, hacks, etc.

So yeah, the Valve solution scares the crap out of me. I decided I’m going to let the client send the server a boolean flag as to whether or not he hit an enemy, then the server will check it with a ray, and if it didn’t hit an enemy, check it again with a spherecast (possibly with a variable radius based on the client’s ping time [with a ceiling of course]). I wrote up some skeleton code, but if you’re not familiar with uLink or their owner/creator/proxy paradigm it might not make much sense to you (in which case you should check it out. uLink is the bomb): Snipt - Søk Forbrukslån & Smålån Uten Sikkerhet (2021)

As I said, this will work - but it’s not as reliable as the Valve solution. I read your code, it looks nice. I assume the owner/creator/proxy is split like this:

Owner = client that owns the object in question
Creator = client/server that created the object, usually the server
Proxy = remote client that is neither the one that owns and object nor is the server (mostly “other” players)

I have the same split (99.99% of all network engines do) in SlimNet, they are called this for me though:

    public enum ActorRole : byte
    {
        Authority, // Every actor on the server
        Autonom, // Actors when on their owning client
        Simulated // Actors when simulated on a remote client
    }

Nice. I’ve watched your videos and stuff, SlimNet looks like it has some serious potential. I didn’t mean to imply that uLink uses an unusual paradigm, I was just referring to uLink’s specific way of leveraging it nicely with built-in methods and whatnot.

In case you were wondering… the spherecast double check didn’t work quite as accurately as I wanted, so I decided to take a different approach, more similar to valve’s, but far more rudimentary.

Now, when a client claims he hit an enemy, he tells the server the hit point and the root position of the enemy he hit (as well as the viewID of the enemy he hit, so the server can find the object)… then the server calculates the delta between the current enemy root position, and the client’s slightly older one, and adds the delta to the client’s hit point, and tests a ray against it (so the point it tests against is in the same position relative to the enemy, but not the world).

I’m still doing the spherecast check if the raycast fails, but 97% of the time the client’s claim is validated. in 500 shots, 488 were valid, 10 were fudged with spherecasts after the ray missed, and 2 were just failures. Good enough for me.