unity – How do I guarantee that important one-time events in my UDP network protocol get received?

I’m creating an online arena game server (moba) that players can fight together. 2v2 or 3v3.the game is similar to Battlerite.

i choose client/server architecture with Deterministic server that tells clients about positions and actions , etc. I assume every things is ok and players have good connection so for now i don’t care about prediction or lag compensation. also i use static state update that server sends all needed data to players even if a player didn’t move i send his position. i use udp for my network backbone and 50 ms tick rate on server side then using interpolation for players so they are in past. if command to move, 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 see in 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 ack for that (i send sample in 50 ms rate too). the movement or attack not important for me but spells are. by the way perhaps i decide to ask for ack on every things i send from client and seems there should not be peroblem.

but my problem is on server side. Let me give you an example :

  • player no 1 send a spell/skill cast assume it’s a powerful strike
    with sword or a roll forward.
  • the packet drop.
  • client send it again.
  • server got it and ack. (100 ms delay until now)
  • in next update server send to all, player no 1 want to cast spell.

  1. what if our own player didn’t get this update ? (as i said there isn’t prediction so he wait until server command)
  2. what if another player didn’t get this update ?

i searched a lot about this subject but can’t find this scenario answer. all documents speak about movement and if an update drops don’t worry it will be sent soon again (50 ms latter).

  1. but what if we have important data like this ?
  2. should i wait until all ack the spell then run it’s animations?

if we ack for those updates perhaps we have next update before important one. and probably we need to ack a lot packets because game based on spell cast and attack etc. and we got network congestion. after all in this scenario we have about 200 ms delay for a spell cast.

by the way please correct me if i have mistake in any step of my work.