coredump-ch / rpsrtsrs

Rock-Paper-Scissors-Real-Time-Strategy game written in Rust.
GNU Affero General Public License v3.0
11 stars 5 forks source link

NAT Traversal #52

Open dbrgn opened 6 years ago

dbrgn commented 6 years ago

It would be really cool if we could do NAT traversal to play the game over the internet without the need for relaying. For that we'd need UDP hole punching: https://en.wikipedia.org/wiki/UDP_hole_punching

The STUN protocol can be used to detect the network situation.

I found three STUN client packages on crates.io:

The first one doesn't look very active. The other two crates both seem at least kind of active (had releases this year). Runstun seems a bit more complete and also contains a server component.

For the beginning it would probably be enough to simply display this information on the server. The address / port could then be transferred to the clients via text messages or via sound waves (aka telling the other where to connect to)...

cc @lgrahl, in case he's bored and interested in Rust games 😉

lgrahl commented 6 years ago

I see no license so far but if it's not going to be GPL, stun3489 may be a little tricky... but it's a library, so dynamically linking should be fine... I guess? stun3489 seems to lack XOR-MAPPED-ADDRESS but that can be added easily. However, if you don't go for tokio, stun3489 is out anyways.

rustun is compelling on first glance and seems to be a lot more complete than the other two.

If you tell me where I need to look, this might be an interesting side project (during my two month math rampage) to get some practical experience in Rust. :sweat_smile:

lgrahl commented 6 years ago

Aaand I've just seen the protocol uses TCP, right?

dbrgn commented 6 years ago

I see no license so far but if it's not going to be GPL, stun3489 may be a little tricky...

Oh, I wasn't aware of that. Most Rust libs are MIT/Apache 2.0 dual licensed. And since Rust uses static library linking by default, that would be a problem indeed.

Aaand I've just seen the protocol uses TCP, right?

Yes, but that's simply because it was the easiest way to get started and we didn't implement UDP yet :)

Not sure if @rnestler wants to do it himself or if contributions regarding that would be welcome.

lgrahl commented 6 years ago

I made an attempt to port the server to UDP (untested as I can't get the client to work atm - SDL error: Couldn\'t find matching GLX visual) but here is the code: https://github.com/lgrahl/rpsrtsrs/commit/b6dba7757ec85257b0e5fdff55c1f008722ce2f1

Client still needs to be ported.

Edit: There should probably be sequence numbers in the packets (to ignore duplicated packets and handle reordered packets) and retransmissions in case a message has not been ack'ed (which of course also has to be added).

rnestler commented 6 years ago

Yes, but that's simply because it was the easiest way to get started and we didn't implement UDP yet :) Not sure if @rnestler wants to do it himself or if contributions regarding that would be welcome.

Yes I wan't to at least switch parts of the protocol to UDP, but I'm not sure if we should switch everything.

Edit: There should probably be sequence numbers in the packets (to ignore duplicated packets and handle reordered packets) and retransmissions in case a message has not been ack'ed (which of course also has to be added).

That's exactly why I chose to stay with TCP for the moment, because one doesn't have to think about sequence, retransmissions and ACKs. But I think at least for the transmission of the GameState, we could just use UDP with a sequence number. We can just ignore retransmissions and order of packets since we are just interested in the latest gamestate. So if we receive an older one, we can ignore it and if one gets lost, we can just wait for the next one.

dbrgn commented 6 years ago

But I think at least for the transmission of the GameState, we could just use UDP with a sequence number. We can just ignore retransmissions and order of packets since we are just interested in the latest gamestate. So if we receive an older one, we can ignore it and if one gets lost, we can just wait for the next one.

Sounds reasonable to me :)

rnestler commented 6 years ago

I made an attempt to port the server to UDP (untested as I can't get the client to work atm - SDL error: Couldn\'t find matching GLX visual)

You need to have SDL installed (Readme) or choose another window backend (Readme). I should probably improve the instructions in the Readme, see #54.

but here is the code: lgrahl/rpsrtsrs@b6dba77

Nice! But before switching the whole protocol to UDP, I'd like to finalize the protocol first (See #56 for example).

lgrahl commented 6 years ago

56 looks like it will need reliable UDP, possibly even ordering. API-wise, I'd say creating some kind of channel struct that can send messages either reliable or unreliable (a simple flag) is sufficient. That channel then handles ACK packets, retransmission and potentially ordering (which is a bit more tricky).

However, reliable UDP is such a common problem that I suspect a tiny little package exists for this purpose. If not then we should make one.

dbrgn commented 6 years ago

http://www.brynosaurus.com/pub/net/p2pnat/

lgrahl commented 6 years ago

Nice! One to bookmark. :+1:

Btw. math is eating up my resources, so I can't really continue my work for now. Situation may be different in ~2 months. :)

rnestler commented 6 years ago

However, reliable UDP is such a common problem that I suspect a tiny little package exists for this purpose. If not then we should make one.

I found a few:

But couldn't we just use TCP for the reliable connection? Or is this a stupid idea? I have close to zero knowledge about this kind of networking :wink:

lgrahl commented 6 years ago

Of course that would make sense... usually. But UDP hole punching is limited to UDP, so no way to get around using it instead of TCP.

dbrgn commented 6 years ago

According to the link I posted above, TCP hole punching is harder and less supported. So if you want a direct connection by using any STUN server around (there are some public ones), then you can't use TCP.

rnestler commented 6 years ago

So lets go full UDP then. I took a closer look at https://crates.io/crates/cobalt it supports both reliable and unreliable messages and is pretty straight forward to use.

dbrgn commented 6 years ago

That looks cool at first glance.

Re mixing UDP and TCP: https://gafferongames.com/post/udp_vs_tcp/