I know that UDP and TCP are not to be used at the same time, so I should only use one. I have read in this site that TCP should be used for a sandbox game. The problem is, My game is going to have fast-paced combat, I feel like using TCP would ruin that, But I don't want to use UDP because I need the packets of a placed/destroyed block to come through 100% of the time. I have heard that Terraria uses TCP, so maybe I can get away with it, if I disabled Nagel's Algorithim.
1\$\begingroup\$ It sounds like you understand the trade-offs involved, and your next step is to profile a test using your proposed method to check whether it performs adequately for your game's unique needs. \$\endgroup\$– DMGregory ♦Mar 22, 2022 at 19:40
2\$\begingroup\$ FWIW, if you use UDP, you may risk having to re-implement TCP over it yourself. Also, I remember having read that internet hubs don't mind much dropping UDP packets more often because they're "less important". \$\endgroup\$– Vaillancourt ♦Mar 22, 2022 at 19:57
\$\begingroup\$ @Vaillancourt Also because TCP normally slows down significantly with just one packet drop! I actually worked on a piece of network equipment that had this feature. I wonder if sending non-TCP packets that look like TCP would allow someone to cheat. \$\endgroup\$– user253751Mar 23, 2022 at 15:50
I know that UDP and TCP are not to be used at the same time, so I should only use one.
Lies. You are not going to use them both on the same socket. Just like you are not going to use two ports on the same socket. But that does not mean you cannot write a game that uses both kinds of sockets.
I have read in this site that TCP should be used for a sandbox game
Sounds like a half truth. They probably mean you need reliability, do you? And TCP provides such reliability. But that does not mean TCP is the only option…
The problem is, My game is going to have fast-paced combat, I feel like using TCP would ruin that
Depends. It is probably fine for LAN where you are not going to have tons of users, nor tons of lost packages, nor high latency. For online play… I don't know. But - because of what I explain below - going with TCP will be easier, so I suggest you start with TCP and get something working.
But I don't want to use UDP because I need the packets of a placed/destroyed block to come through 100% of the time
So you need reliability. Well, you can build it on top of UDP. The trade-off is that you would have to implement a more complex net-code and in exchange you will have control over reliability.
The simplest solution is to have the receiver peer reply with an acknowledge for everything message they get, and the sender peer send messages again if they don't get an acknowledge after a timeout period. So the sender will keep sending all messages for which it didn't get an acknowledge.
You may also care about the order of messages. For example, it is not the same "add block" followed by "remove block" than "remove block" followed by "add block". In this case if the receiver peer gets messages out of order (the messages are numbered, and the receiver keeps track if they skip a number) it must hold onto them until it receives the ones in between.
That leads us to something else. You can have the receiver peer tell the sender peer what messages it is missing - instead of having the sender peer keep track of which it didn't get an acknowledge. In this scenario, the sender peer would only keep sending the latest message until it gets its acknowledge, and only resend older messages if said acknowledge also says some old messages are missing.
But that does not work for everything. For example, if you are sending the position of a player, you don't really care if the receiver peer got all the messages… You only care if they got the latest one. So there is no point in retrying obsolete messages.
Thus, if you have a stream of data (you could be sending multiple streams of data over the same socket), it is better for the receiver peer to reply with the number of the latest message of the stream they got. That way they don't need to send an acknowledge for every message they get. And the sender peer would still know if they are up to date.
Now you can imagine another version of this where the receiver peer send a heartbeat, in which it will send what was the latest message it got. If the sender peer stops receiving the heartbeat for a while it will assume a disconnection. Now, if the sender peer signals if a stream must be reliable or not, the receiver peer can send on its heartbeat which messages it didn't get, but only for the reliable streams. A drawback of only using a heartbeat is losing the ability to measure RTT (Round Trip Time) - which you will later find out you need, but I'll leave lag compensation out of this answer - a workaround is to have a dedicated message to test that.
And with the sender peer having some idea of the receiver peer disconnected, it can decide to stop sending messages… And when it hears from the other peer again it can decide to send a whole update instead of all the steps. Imagine, when a player joins the game, the server would send the state of the map, instead of the sending all the "add block" and "remove block" to make it. Same thing here, instead of sending all the messages that the receiver peer missed, just send the final state. And you don't have to send the whole map. Just the chunks in the intimidate vicinity of the player. CHUNKS!
Of course, this isn't a complete description. But I want to point out that with UDP you can accomplish reliability patterns that are not possible with TCP, but it will require more work to implement them. So I would still recommend you start with TCP. And if it is actually a problem and you need to switch to UDP, I hope I gave you an idea of what you could do.
I have heard that Terraria uses TCP
Yes. It is fine.
so maybe I can get away with it, if I disabled Nagel's Algorithim.
Sure, you can set
TCP_NODELAY. Be aware that more messages, means more acknowledges. On the flip side: implement your own buffer, and make network IO a step in your game loop.