I'm creating an online arena game server (moba) that players can play together. 2v2 or 3v3. The game is similar to Battlerite.
I chose client/server architecture with deterministic server that tells clients about positions, actions etc. I assume everything is OK and players have good connection, so for now i don't care about prediction or lag compensation. I also use static state update -- server sends all needed data to players even if a player didn't move it sends his position, also spell cast data are in state update too. I use UDP for my network backbone and 50ms tick rate on server side. in client side we use interpolation between two update to make game seems smooth . If command to move arrives after a period when you get next update you will move.
here is my client code:
public void loop()
{
while (client_started)
{
try
{
receive();
}
catch (Exception e)
{
throw e;
}
try
{
send();
}
catch (Exception e)
{
throw e;
}
}
}
private void send()
{
if (disconnected)
return;
try
{
foreach (var item in need_ack_packets)
send(item.Value);
on_sampling?.Invoke(command_sample);
if (command_sample.has_data)
{
send(command_sample.to_udp_packet());
command_sample.clear();
}
}
catch (Exception e)
{
throw e;
}
}
private void receive()
{
try
{
var data = client.Receive(ref receive_endpoint);
if (data.Length == 0)
{
//------
}
var pack = udp_packet.from_bytes(data, aes_crypto);
if (pack.is_ack)
need_ack_packets.TryRemove(pack.ack_to_message_id, out _);
if (pack.need_ack)
send_ack(pack);
if (pack.has_body)
{
on_data_receive?.Invoke(this, pack.body);
}
}
catch (Exception e)
{
throw e;
}
}
As you can see on the client side I send data after each server update receive and if an important packet must be sent I send it again and again until server ACKs for that (I send sample in 50 ms rate too). The movement or attack is not important for me but spells are. so i can make sure all important data will reach to server with ACK so in worst sample maybe we have delay.
But my problem is on the server side. Let me give you an example :
- player no. 1 sends a spell/skill cast -- assume it's a powerful strike with a sword or a roll forward.
- the packet is dropped.
- client sends it again.
- server receives it and ACKs. (100 ms delay until now)
- in next update server sends to all that player no. 1 casts a spell.
- what if our own player didn't get this update ? (as I said there is no prediction so he's waiting until server's command)
- what if another player didn't get this update ?
I searched a lot about this subject but can't find answer to this scenario. All documents talk about movement and if an update drops, "don't worry it will be sent soon again (50 ms latter)".
Lets say the spell is roll so if server receive that command will change player position forward for 1 second (a bit in each tick). If a player miss two updates he has a new position but he missed spell cast information. Lets say the spell is powerful strike it must happen in 1 or 2 update so if a client miss two updates he missed it or if we resent spell cast to unlucky client (if attacker and target move) in new update client sees attacker and target are in new positions and there is a spell cast request in update too so powerful strike can't cast on target due to distance between them.
please correct me if I made mistakes in any step of my work.I'm searching for an efficient way to handle these scenarios. how to send important update (repeat whole update, repeat important parts)? how play animations attacks etc to make game smooth? how handle new positions and delayed spell casts?