void HandleAbility(ref byte[] data, ref int conn)
    {
        int target = data[1] | data[2] << 8; //deserialize
        int casted = data[3] | data[4] << 8; //deserialize

        if (data.Length != 7)
        {
            Debug.LogWarning("deformed packet in HandleAbility from " + points[conn].id);
            return;
        }

        Ext.DebugByte(data, "received ability: ");

        if (target != 0 && !EntityExists(target))
        {
            Debug.LogWarning("player tried to target nonexistant entity " + target);
            return;
        }

        if (target != 0 && !(entities[target] is Entity))
        {
            Debug.LogWarning("player tried to cast at a SyncObject not an Entity");
            return;
        }

        if (!CheckAbilitySanity(ref casted))
        {
            return;
        }

        if (!Spell.GetSpell(casted).targetedTT)
        {
            Debug.LogWarning(points[conn].id + " tried casting a non-ray ability as a ray");
            return;
        }

        Spell.GetSpell(casted).TryStartCast(target != 0 ? entities[target] as Entity : null, points[conn]);


        //Also check if they used AoE or something?...


        //points[conn].Cast(entities[target].e, WorldFunctions.instance.allSpells[casted], new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, data[5], data[6] * 4)); //try cast normal ability
    }
    //basically the listen process. Mother network interface class that inherits NetworkInterface uses this to continously enter into handling data on a listen loop, hence 'override'. The start point into handling.
    public override void HandleData(int conn, byte[] receiveBytes)
    {
        if (logAll)
        {
            Ext.DebugByte(receiveBytes, "server received: ");
        }

#if debug
        debugEstimatedDataIn += receiveBytes.Length + 30; //count bytes length plus a udp packet header which averages 20-40 bytes
        if (packetLossPer > 0)
        {
            if (UnityEngine.Random.value < packetLossPer / 100)
            {
                droppedPacketsCounter++;
                return;
            }
        }

        if (delaySec > 0) //if delay is enabled
        {
            Timing.CallDelayed(delaySec, () => { ReceiveRaw(conn, receiveBytes, false, true); });
            return;
        }
#endif
        //Debug.Log("handling data");

        // Debug.Log("received");
        // Tools.DebugByte(receiveBytes);

        //main network code code...
        switch (receiveBytes[0])
        {
        case (netcodes.join):
            AddPlayer(conn);
            break;

        case (netcodes.playerQuit):
            RemovePlayer(conn);
            break;

        case (netcodes.inputs):
            HandleInputs(ref conn, ref receiveBytes);
            break;

        case (netcodes.pos3d):
            HandlePos(ref conn, ref receiveBytes);
            break;

        case (netcodes.infoplz):
            HandleAskOfPlayer(ref receiveBytes, ref conn);
            break;

        case (netcodes.yourid):     //in this case actually asking for their id
            HandleYourID(ref receiveBytes, ref conn);
            break;

        case (netcodes.ability):
            HandleAbility(ref receiveBytes, ref conn);
            break;

        case (netcodes.aoeAbility):
            HandleAbilityAOE(ref receiveBytes, ref conn);
            break;

        case (netcodes.rayAbility):
            HandleAbilityRay(ref receiveBytes, ref conn);
            break;

        case (netcodes.chat):
            HandleChat(ref receiveBytes, ref conn);
            break;

        case (netcodes.shops):
            HandleShops(ref receiveBytes, ref conn);
            break;
        }
    }