• Best practices for simulating bullets
    18 replies, posted
I want to add simulated bullets to my SWEP base. That is to say, they take time to travel, drop, and have drag. Plus other features like ricochets and penetration of light cover. I intend to do this with an entity which will, every tick, predict how much further it should be in the path and trace it. If the trace hits something, it damages it and does some other logic. Otherwise it moves the bullet's point there, applies drop and drag, and does the same thing again next frame. I have two questions. What's the best practice for making this entity in terms of drawing it with a sprite or model or whatever? What's the best practice for synchronizing the visuals on the client with the calculation on the server? I remember previous weapons that tried to do this has the visuals lag behind on the client when used on multiplayer. More generally, I'm trying to solve the question: How do I have a SWEP create an entity so that it appears on the client instantly, even with high amounts of lag? Every attempt I've had so far has the client wait for the server to tell it about the entity.
I've never done something like this but to synchronize between client and server you can set math.randomseed to something consisting of position and angle each time you use random to make outputs deterministic for a given seed. Realistically each bullet will always have a different position and angle, so as long as the server and client both report the same position and angles, the bullet calculations will be synchronized. [editline]24th January 2017[/editline] You could try using synchronized time as a seed but I'm not sure how reliable it is.
Right. I think I've got the synchronization aspect. I've got a good algorithm that lets me simulate the same number of steps on the client and server, regardless of what tickrate and fps they actually run at. What I'm stumped by is getting the entity to appear in the first place. It's like how the client draws a tracer and impact decal before the server even confirms that it shot. I want that, but with my entity. Is there a way to make a clientside entity?
[img]http://wiki.garrysmod.com/favicon.ico[/img] [url=http://wiki.garrysmod.com/page/util/ParticleTracerEx]util.ParticleTracerEx[/url] [img]http://wiki.garrysmod.com/favicon.ico[/img] [url=http://wiki.garrysmod.com/page/util/Decal]util.Decal[/url] Why do you need an entity? Just calculate it in a think hook or something.
[QUOTE=Ott;51722592] Why do you need an entity? Just calculate it in a think hook or something.[/QUOTE] ...I'm a mo-ron. Seems marginally inelegant to have a random think hook on the SWEP base, but it might just work! Quick sanity check. Have a table of bullet "entities" containing position, velocity, etc local to the base (ie, shared between all sweps). Do the loop mentioned in my first post for each of them. Break out into CLIENT and SERVER to handle effects on the client, mechanics on the server. Does this make sense?
Sounds about right; consider using FireBullets for your final impact, starting from the HitPos + HitNormal and in the direction of -HitNormal. That way, it'll automatically handle decal/sounds/hooks/etc. If I may ask, what are you using for your synchronized seed? Never found a good way of doing that.
[QUOTE=TFA;51722672]Sounds about right; consider using FireBullets for your final impact, starting from the HitPos + HitNormal and in the direction of -HitNormal. That way, it'll automatically handle decal/sounds/hooks/etc. If I may ask, what are you using for your synchronized seed? Never found a good way of doing that.[/QUOTE] I'm planning on seeding the weapon on pickup, using the values of the player's steamid, the weapon's entity index, and the number of times the weapon has been dropped (which I have an entire draw-animation-duration to guarantee it gets synced). Or some other kind of incrementer, maybe stored on the player. Plus maybe a server-start random value (again, the whole joining-server time to sync it). I've got a custom rng that passes most of the diehard tests that is also hella fast, so I was gonna use that for the per-weapon rng. Or I'll be lazy and use util.SharedRandom with all that stuff appended together and the last value just the number of times it's been used. But I'm uncertain how equidistributed it is.
Don't use scripted entities, gmod has a 100ms entity interpolation rate on top of network latency. You also can't create entities on the client, atleast not stuff like rpg rockets or grenades that can move and interact. Use traces to simulate your projectiles inside predicted hooks (SWEP:PrimaryFire, GM:FinishMove). In PrimaryFire you want to network the fired bullet yourself to every client except the shooter, assuming you want visual effects such as tracers (you can also use util.SharedRandom here for spread). Update your projectiles properties and continue tracing in GM:FinishMove (in a shared environment), and the server should re-transmit changes here to clients to keep things in sync. You can treat this hook as if the local player fired a projectile here, except just continue tracing from wherever the bullet currently is. You shouldn't need any kind of seed syncing for this method. However, keeping the bullet somewhat in sync with the server is necessary here, for example if it leaves PVS then it could very well hit something on the server without the client knowing about it.
[QUOTE=SFArial;51724141]Use traces to simulate your projectiles inside predicted hooks (SWEP:PrimaryFire, GM:FinishMove). In PrimaryFire you want to network the fired bullet yourself to every client except the shooter, assuming you want visual effects such as tracers (you can also use util.SharedRandom here for spread). Update your projectiles properties and continue tracing in GM:FinishMove (in a shared environment), and the server should re-transmit changes here to clients to keep things in sync. You can treat this hook as if the local player fired a projectile here, except just continue tracing from wherever the bullet currently is.[/QUOTE] I'm confused by how you mean for me to track the state of all of the bullets in the air. Will gmod automatically sync the table containing velocities, owners, damages, positions, etc? Can you give me a minimalist bit of code synchronizing one or two variables? And I'm very confused by how FinishMove plays into all of this.
FinishMove is a predicted hook that gets called every now and then without user input, so it's perfect for handling something like this. You gotta keep bullets in sync between the server and client manually. I uploaded a wip example [URL="https://github.com/SFAriel/garrysmod-deadmansgun/blob/master/entities/weapons/weapon_dmg_base/phys_bullets.lua"]here[/URL]. It's an unfinished gamemode I was working on, feel free to download the whole thing. Currently the bullets don't deal damage yet and the server only networks the bullets once instead of every now and then as it should. I was busy playing around with tracers and ballistics.
Right, I understand it's a predicted hook. But it's not consistently called, is it? Not on equal intervals, at least. Why not use Think? It's what I'm using and I'm only getting bullet desyncs if you drop a packet the exact frame you shoot (it's still sync'd for all the other clients).
I don't think using FinishMove is a good idea as its called per player and not per bullet which just adds lots of complexity in general. The Tick hook is probably something better where simulations like that should happen, if you want a smooth experience you can always interpolate on the client via Think since its called per frame.
You can only use lag compensation inside predicted hooks, that's why. But honestly, i'm no expert, it's all trial and error for me too. That being said, last night I had a lot of trouble keeping bullets in sync with the server so I was playing around with various hooks, and found GM.StartCommand the best to simulate your bullets (but only when ucmd:TickCount() returns a non-zero value). Simulation with this method is locked to server tickrate though, which is 66 by default. With slow moving projectiles it looks bad on a monitor with a higher than 66hz refresh rate. Lag compensation is very important, don't ignore it otherwise you are creating a lot of desync.
[QUOTE=SFArial;51736331]You can only use lag compensation inside predicted hooks, that's why. But honestly, i'm no expert, it's all trial and error for me too. That being said, last night I had a lot of trouble keeping bullets in sync with the server so I was playing around with various hooks, and found GM.StartCommand the best to simulate your bullets (but only when ucmd:TickCount() returns a non-zero value). Simulation with this method is locked to server tickrate though, which is 66 by default. With slow moving projectiles it looks bad on a monitor with a higher than 66hz refresh rate. Lag compensation is very important, don't ignore it otherwise you are creating a lot of desync.[/QUOTE] You don't need lag compensation with entities like that since they're simulated from the server for everyone.
If you want correctly predicted bullets then you need it. If you are okay with ditching prediction then you obviously don't. Prediction increases responsiveness.
I'm not using entities, but all of the initialization for the bullet is called from the predicted SWEP:PrimaryAttack. And then it proceeds deterministically from there.
[QUOTE=SFArial;51737170]If you want correctly predicted bullets then you need it. If you are okay with ditching prediction then you obviously don't. Prediction increases responsiveness.[/QUOTE] Couldn't they run independently on client/server though? Of course, there's the risk of desynch, but the reduction in networking would definitely be worth it IMO. All the server would have to do is start the bullet from a lagpredicted position, which could be done in the PrimaryAttack hook. Then, each tick, you loop through the bullets and temporarily bullet.Owner:LagCompensate as you process them.
When player A shoots, it's called from the predicted function SWEP:PrimaryAttack. I have verified that the values for CurTime, EyeAngles, and ShootPos and synchronized at this point. Player A and the Server both add the computed bullet to their list of simulated bullets. The server then sends the data required to recreate the bullets to every other client (manually transmitting the eyeangles, shootpos, and key material for an RNG for spread and bullet seed for transonic tumble). It runs independently and they all come to the same conclusion. I've verified this will all kinds of fakelag and fakeloss settings, with the shooter stationary and flying through the air. Player A sometimes disagrees with the Server, but they disagree with the Server under the same circumstances with the standard HL2 weapons. All of the other clients never disagree with the Server. Now I'm trying to decide the best way to do the bullet callbacks that I use with this...
I see, I didn't really think about it that way. I will play around with it next week, this might make things simpler.
Sorry, you need to Log In to post a reply to this thread.