proposal: expose an API to access batch packets to reduce proxy latency
Description
Most people use gophertunnel for proxies between the client and server. Typically, a proxy is written like this to redirect packets from the server to the client and vice versa:
pk, err := serverConn.ReadPacket()
if err != nil {
return
}
clientConn.WritePacket(pk)
As we know, when we call WritePacket(pk), it does not actually send the packet immediately; instead, it's queued/buffered in memory and sent later unless we call Flush() after. By default, gophertunnel will flush queued/buffered packets from memory every 50ms, or we can call it a tick. The time between those ticks, we called WritePacket(). As a result, there is unnecessary latency of up to 50ms due to packet buffering/queueing.
Behind the scenes, when queued/buffered packets are flushed, the packets will be batched into a single-batched packet. That single-batched packet will be compressed & encrypted.
Why not call Flush() after WritePacket(), or reducing the flush rate?
Flushing every single packet or reducing the flush rate would create overhead since each packet must be compressed and encrypted. When small data is compressed using snappy/zlib, it can increase in size instead of decreasing, leading to higher bandwidth usage. Additionally, this adds more overhead on the raknet side (like more packet header).
Proposed Solutions
Add (*minecraft.Conn).ReadPackets([]packet.Packet)
This will read the packets from a single batch packet from the connection.
The proxy will do this:
pks, err := serverConn.ReadPackets()
if err != nil {
return
}
for _, pk := range pks {
clientConn.WritePacket(pk)
}
clientConn.Flush()
It will immediately redirect packets from the server to the client without waiting for the flush rate, which can significantly reduce latency by up to 50ms. But there's a problem: when we call Flush(), it will also include another packet from the queue/buffer, but I think that is not a problem for me.
I am not particularly happy with introducing such a concept to the public API, as this complicates the API for really minimal gains. I think a better solution would be to introduce some sort of Proxy/Router type that takes care of this and also resolves other common issues with the current design, such as the need for predialing to obtain resource packs from the target server.
You could also introduce a special flush packet into the stream at batch boundaries. Though you'd have to define it in such a way that the flush packet couldn't be relayed to the client by accident.
You could also introduce a special flush packet into the stream at batch boundaries. Though you'd have to define it in such a way that the flush packet couldn't be relayed to the client by accident.
You can’t send a special flush packet when you’re proxying to a server you don’t control
I'm saying gophertunnel itself could do that.
@dktapps Would you be able to invite me to the cave, by any chance? Thank you
Same here! Can I get an invite to the cave? Thank you
ohhh @cqdetdev told me about that
Please immediately stop using our github issues as a chat for personal requests.