Depends on what you are trying to achieve and how accurate you want it to be.
- Send shot events. Costly with high ROF. includes timestamp (should be in packet header already), shooter ID, position, direction, bullet type...
- high fidelity. Eats bandwidth / introduce lag if TCP.
- send ammo count separately to keep ammo in sync when packets are lost.
- Send ammo count / last_shot_fired_timestamp, that sort of thing.
- cheap.
- Very innacurate. It's just to make the game more interesting visually.
- Games like counterstrike need good accuracy. People will see bullet holes, so even shot randomisation will affect spray pattern and players notice that kind of discrepancy.
- Games like TF2 don't care. Minigun / scattershot firing can be simulated less accurately.
- BTW, if you use a server / client model, you don't need to be always accurate on clients, as they should be doing nothing. The server should be in charge of the entire logic (ammo, who hits who, reloads, ect...). The server needs to send what is relevant to each player. Meaning, Projectiles need to be done correctly, some hit-scan weapons as well (sniper shots), some can be simplified to save bandwidth (high ROF guns). Depending on what you think it 'relevant' for your game.