Page 1 of 1

Ability Damage/Heals in Client Log

Posted: Thu Jan 15, 2015 11:09 am
by Londo
After days and day of decyphering packets I have finally derived the algorithm used by mythic to encode damage information in packets.

The damage/heal information is packed into array of integers that expands with larger data (Supposedly to save bandwidth, as this is a very common packet). Original server emulator code done by Dyox does not have correct algorithm to encode damage information.

(Here is example of each field controlled individually)
Image

Here is the algorithm (Can be improved for speed).

Code: Select all

public static List<byte> Pack(List<int> values)
        {
            List<byte> bytes = new List<byte>();
            foreach (int val in values)
            {
                if (val == 0)
                {
                    bytes.Add(0);
                    continue;
                }
                else if (val == 1)
                {
                    bytes.Add(1);
                    continue;
                }
                else if (val == -1)
                {
                    bytes.Add(2);
                    continue;
                }

                int signedValue = val;
                if (val != 1)
                {
                    if (val > 0)
                        signedValue = signedValue - 1;
                    else
                        signedValue = -signedValue;
                }

                int bitCount = BitCount(signedValue);
                int bitCountRemaning = bitCount;
                int octetCount = 0;
                int c = 0;
                while (bitCountRemaning > 0)
                {
                    if (c == 0)
                        bitCountRemaning -= 6;
                    else
                        bitCountRemaning -= 7;
                    octetCount++;
                }

                int offset = 0;
                int shift = 6;
                for (int i = 0; i < octetCount; i++)
                {
                    int mask = CreateMask(offset, offset + shift);
                    int octet = (mask & signedValue) >> offset;

                    if (i == 0)
                    {
                        octet = octet << 1; //LSB is the sign bit on first octet

                        if (val > 0)//if value was positive, we need to set sign flag
                            octet |= 1 << 0; //1 for positive
                        else
                            octet &= ~(1 << 0); //0 for negative
                    }

                    if (i != (octetCount - 1)) //if not on last octet, set expansion bit
                        octet |= 0x80;

                    bytes.Add((byte)octet);
                    offset += shift;

                    if (i == 0) //first octet we write 6 bits, then 7 bits on rest
                        shift = 7;
                }
            }
            return bytes;
        }
public void SendAbilityEffect(Client client, Ability ability, AbilityResult abilityResult, Character from, Character target,
            int? hitAmount, int? absorbedAmount, int? mitigatedAmount, int? overhealedAmount)
        {
            MemoryStream stream = client.GetWriteBuffer();
            PacketUtil.WriteByte(stream, (byte)Opcode.F_CAST_PLAYER_EFFECT);
            PacketUtil.WriteUInt16(stream, (ushort)from.Client.CharacterID);
            PacketUtil.WriteUInt16(stream, (ushort)target.Client.CharacterID);
            PacketUtil.WriteUInt16(stream, (ushort)ability.ID);


            if (ability.IsHeal)
                PacketUtil.WriteByte(stream, (byte)0);
            else
                PacketUtil.WriteByte(stream, (byte)6);


            PacketUtil.WriteByte(stream, (byte)abilityResult); //  BLOCK = 4,  PARRY = 5,  EVADE = 6,  DISRUPT = 7,  MISS = 8,   CRITICAL = 9


            if (ability.IsHeal)
                PacketUtil.WriteByte(stream, (byte)2);
            else
            {
                if (absorbedAmount.HasValue && mitigatedAmount.HasValue)
                    if (hitAmount.Value == 0)
                        PacketUtil.WriteByte(stream, (byte)50);
                    else
                        PacketUtil.WriteByte(stream, (byte)43);
                else if (absorbedAmount.HasValue && !mitigatedAmount.HasValue)
                    PacketUtil.WriteByte(stream, (byte)111);
                else if (!absorbedAmount.HasValue && mitigatedAmount.HasValue)
                    PacketUtil.WriteByte(stream, (byte)122);
                else
                    PacketUtil.WriteByte(stream, (byte)195);
            }

            List<int> numsToSend = new List<int>();

            if (hitAmount.HasValue)
            {
                if (ability.IsHeal)
                    numsToSend.Add(-hitAmount.Value);
                else
                    numsToSend.Add(hitAmount.Value);

                if (ability.IsHeal && overhealedAmount.HasValue)
                    numsToSend.Add(-overhealedAmount.Value);
                else
                {
                    if (mitigatedAmount.HasValue && absorbedAmount.HasValue)
                    {
                        numsToSend.Add(-mitigatedAmount.Value);
                        numsToSend.Add(-absorbedAmount.Value);
                    }
                    else if (mitigatedAmount.HasValue && !absorbedAmount.HasValue)
                    {
                        numsToSend.Add(-mitigatedAmount.Value);
                    }
                    else if (!mitigatedAmount.HasValue && absorbedAmount.HasValue)
                    {
                        numsToSend.Add(0);
                        numsToSend.Add(-absorbedAmount.Value);
                    }
                }
            }

            foreach (byte octet in PacketUtil.Pack(numsToSend))
                PacketUtil.WriteByte(stream, octet);

            PacketUtil.WriteByte(stream, 0x00);
            client.SendPacket("F_CAST_PLAYER_EFFECT", stream, false, false);
        }
        private static int BitCount(int value)
        {
            int bitCount = 0;
            int r = value;
            while (r > 0)
            {
                r /= 2;
                bitCount++;
            }
            return bitCount;
        }

        private static int CreateMask(int a, int b)
        {
            int r = 0;
            for (int i = a; i <= b; i++)
                r |= 1 << i;

            return r;
        }



Re: Ability Damage/Heals in Client Log

Posted: Thu Jan 15, 2015 11:10 am
by Elven
Once again, amazing work! :D

Re: Ability Damage/Heals in Client Log

Posted: Thu Jan 15, 2015 5:28 pm
by chaoscode
good work!

Re: Ability Damage/Heals in Client Log

Posted: Thu Jan 15, 2015 6:16 pm
by Tesq
sweept londo !! :D

Re: Ability Damage/Heals in Client Log

Posted: Thu Jan 15, 2015 7:36 pm
by Morf
Great stuff Londo.