gschup / ggrs

GGRS is a reimagination of GGPO, enabling P2P rollback networking in Rust. Rollback to the future!
Other
507 stars 25 forks source link

Implemented Broadcast Options for `NonBlockingSocket` #67

Open bushrat011899 opened 10 months ago

bushrat011899 commented 10 months ago

Objective

Solution

I've added additional methods to NonBlockingSocket for more specific forms of message transmission:

Since these methods can be defined in terms of repeated send_to calls, default implementations are provided for all three, ensuring no breaking change in the external API. What this does allow is for implementers of NonBlockingSocket to create optimised transmission behaviour for certain cases.

To utilise these new transmission options, I've done some refactoring of UdpProtocol. First, I've introduced a BROADCAST MessageHeader magic value, which will allow endpoints to accept broadcasted messages. (currently, every message includes a connection-specific magic number which makes broadcast infeasible. By adding a broadcast special value, the check can be bypassed).

Next, I refactored the input and checksum senders to instead clear the pending message queues, and then broadcast. I clear the message queues first to ensure the order of messages is preserved. Broadcast is then done immediately, since I can't think of a reasonable design change to allow a mixture of singlecast and broadcast messages across all connections.

Finally, I changed send_all_messages to actually send all messages using send_many_to. If a socket implementation has a way of batching messages together effectively, this change would be heavily utilised.

Some other misc. changes:

Notes

Currently, send_many_to_many is unused by GGRS in this PR, as I could not find any instances of sending the same batch of messages to the same list of peers. However, it's such a natural consequence of adding batched singlecast and single multicast, it feels incomplete to leave it out. Perhaps in the future there may be a use for it?

Additionally, in the UdpProtocol::send_input_to_many method, I check that all input packets actually are the same before using send_to_many, falling back to repeated send_to on difference. I'm not confident enough to refactor how the input messages are created to ensure they are all identical, or have the differing parts could be segregated out, but I do think that's something to look into for future performance boosts.

Finally, I haven't changed the implementation of NonBlockingSocket for UdpNonBlockingSocket, since I don't know if for UDP packets there's a way to get substantially more efficiency with batching up the messages. It might, but testing would be required to justify such a change. For more bespoke implementation like in #45 however, I think the changes would be substantial.