Glenn Fiedler writes about game networking, physics integration, fixed timesteps, and floating point determinism for game programmers.
Commenters call it a must-read for game networking and recommend his articles on UDP, cheating, and netcode.
PhysX itself (assuming Unity still uses PhysX, but any major physics engine will be the same) is unlikely to be your problem, although it is possible that Unity compiles it with undesirable optimisations, or some aggressive multithreading strategy, etc.
In any case, it's quite likely that the physics simulation is not being initialised with identical state on each instance, so the physics engine -- even if it claims to be deterministic -- cannot be expected to arrive at the same solution across instances.
From the scripting layer atop Unity (et al.) you are essentially out of luck. You don't have the requisite control/access to guarantee the simulation input is consistent.
To expand on the "quite likely" variance in the simulation input, there's an unending list of possibilities, so just a couple of points: Unity is really really big and complicated.
The physics engine in Unity is not primarily there to be a FEATURE: physics engine. It's a general purpose collision system, and it's fundamental to Unity's whole world/scene/components. It's always running. Stuff that has nothing to do with what you see as your simulation can affect your simulation.
My unopinionated advice is: read Glenn Fiedler's classic networked physics articles[0].
My opinionated advice is: you're making a game where the physics is the whole game. It's also a very nicely contained problem space. Write your own physics simulation. It's gameplay code. Also, typical rigid body physics engines certainly can…
Just want to chime in that although this comment is a bit harsh, it does hold a lot of truth. As a game netcode engineer, most of my time is spent on latency hiding techniques and its consequences (for example, we use rollback netcode and lots of efforts are spent on minimizing visual/sound glitches when mispredict, rollback and reconcile). There are lots of middleware that help with this (eg. Photon, SnapNet, etc) but in general there's no silver bullet - it's highly gameplay specific. Even for the same game, the solution can vary depending on different trade-offs (i.e. budget, correctness/anticheat).
As to how to store the game state in memory, it's usually not something that needs much thinking: it's simply done the same way (or similar) as the game's client code. After all, netcode is mostly about efficient state replication and this saves CPU time when replicating it across clients - and gives more CPU time for minimizing bandwidth (like delta encoding, quantization, etc). If you want, you can utilize some techniques like ECS to optimize for CPU cache locality, but it affect gameplay code a lot and would need to get the whole team onboard.
Also, I just noticed the username "gafferongames" - Glenn's blog[1] has been a must-read for netcode engineers. It helped me a lot when I started working on netcode in the 2010s
[1] https://gafferongames.com/
It seems clear that if we hadn't inherited a bunch of irreproducible languages and/or instruction sets from the 20th century, no one would bother creating a new one. There are certainly some ways to get optimization advantages from underspecified computation, but especially in a networked world this win is outweighed by the problems it causes.
When you use irreproducible floating-point, you're essentially treating your CPU as an analog computer. For a subset of very specialized numerical applications, this is probably okay. For any kind of integration with interesting, non-numerical system software (let alone scientific publishing!), it's a disaster.
I always recommend this link on FP determinism: http://gafferongames.com/networking-for-game-programmers/floating-point-determinism/
I've done a small amount, but mostly as hobbyist stuff.
If you haven't looked at it already, Glen Fiedler has a great series on game networking on his Gaffer on Games blogs that covers a lot of those topics in ways that were really helpful for my implementations. https://gafferongames.com/tags/networking/
It looks like his site is down, but it's also available on the internet archive: https://web.archive.org/web/20190405204744/https://gafferongames.com/tags/networking/. It's all fairly high-level stuff, and I'm sure it mostly just retreads ground that you're familiar with, but I thought the explanations were all super-clear and helped crystallize a lot of thoughts that I already had but hadn't focused that well.
Sounds fun and I would definitely play with my friends :)
Add touch controls for mobile devices if possible (check PewPew for a similar movement mechanic) and you got yourself a pretty fun mobile game. I'm not sure if mobile browsers would be up to the task though, just throwing random ideas.
Does Construct2 include networking and lag compensation? It's hard to achieve smooth gameplay over the internet, especially for fast-paced games like this one. Check [1] for a great resource on networking.
I have some experience on game networking. Feel free to contact me at (el + my HN username) at gmail if you need some help.
[1] http://gafferongames.com/networking-for-game-programmers/
Variable timesteps have their own host of problems. Considering the target and scope of TFA I think best practices are beside the point, but your suggested replacement is bad practice as well.
The "right thing to do" in most games is to integrate at fixed timesteps that are decoupled from rendering. But this can be tricky to get right, and would require another article to explain [0]. Considering all that, the article's implementation is fine!
[0] Except that the definitive article has already been written, that is:
http://gafferongames.com/game-physics/fix-your-timestep/