пятница, 18 марта 2011 г.

Алгоритм изменения Id сетевого клиента. Воплощение в коде.


Почти со всеми вопросами, касающимися алгоритма получения и обработки Id клиента сетевой игры мы разобрали. Пора перейти непосредственно к воплощению задуманного в коде. Но постойте. Кажется, что мы что-то упустили из виду. Так и есть. Ведь все наши рассуждения относительно того, как происходит процесс преобразования приходящей информации, строились исходя из того, что работать будем не с байтами, а с частями этих байтов. Давайте вспомним как все это выглядит в действительности в нашей программе сейчас. Приходит сообщение из 8 байт и мы записываем его в массив.
char ans1[] = { 0xCF, 0xCF, 0xCF, 0xCF, 0x01, 0x02, 0x30, 0x40, 0};
Для примера, приведен массив, в котором хранится присылаемый сервером пакет вместе с заголовком. Значит заголовок:
0xCF, 0xCF, 0xCF, 0xCF
Тело сообщения:
0x01, 0x02, 0x30, 0x40
Но вот беда, наш алгоритм рассчитан на работу с каждой отдельной шестнадцатеричной единицей. А давайте попробуем написать собственную функцию, которая будет из принятого массива 4 байт создавать новый массив размером 8 ячеек, в каждой из которой будет хранится одно шестнадцатеричное число. То есть результат предыдущего тела сообщения должен предстать в виде:
0x0, 0x1, 0x0, 0x2, 0x3, 0x0, 0x4 , 0x0.
Опять же значения каждого числа записаны в шестнадцатеричном виде.
Чтобы была понятна сама схема разбора на составные части, попробуем представить этот процесс для одного числа.
0х01 = 1 * 16 0 + 0 * 16 1 = 1
0х40 = 0 * 16 0 + 4 * 16 1 = 64
Для того чтобы сделать все тоже самое, но только обратно, то есть из десятичного числа получить 16-чное:
1 = 1 / 16 1 = 0 (остаток 1) – это первое 16-чное значение
1 = 1 / 16 0 = 1 (остаток 0) – это второе 16-чное значение

64 = 64 / 16 1 = 4 (остаток 0) – это первое 16-чное значение
0 = 0 / 16 0 = 0 (остаток 0) – это второе 16-чное значение

Вот что получилось, если сделать функцию, для обработки массива любого размера:
char *Decod(char *value, unsigned int len)
    {
        char *dec = new char[len * 2];
        for (unsigned int i = 0, j = 0; i < len*2; i += 2, j++)
        {
            dec[i] = (unsigned char)((unsigned char)value[j] / 16);
            dec[i + 1] = (unsigned char)((unsigned char)value[j] % 16);
         }
    }
Видно, что подавать на вход мы должны массив и его размер, а на выходе будет указатель на массив, который размером в два раза больше. И только после этого можно будет обрабатывать каждый отдельный элемент нового массива согласно выведенного нами алгоритма изменения Id. А вот и этот алгоритм, который мы так усердно расписывали в excel:

if ((value[0] == 0) || (value[0] == 8))  { value[0] += 7; }
        else
        if ((value[0] == 1) || (value[0] == 9)) { value[0] += 5; }
        else
        if ((value[0] == 2) || (value[0] == 10)) { value[0] += 3; }
        else
        if ((value[0] == 3) || (value[0] == 11)) { value[0] += 1; }
        else
        if ((value[0] == 4) || (value[0] == 12)) { value[0] -= 1; }
        else
        if ((value[0] == 5) || (value[0] == 13)) { value[0] -= 3; }
        else
        if ((value[0] == 6) || (value[0] == 14)) { value[0] -= 5; }
        else
        if ((value[0] == 7) || (value[0] == 15)) { value[0] -= 7; }


        if ((value[1] == 0) || (value[1] == 1) || (value[1] == 4) || (value[1] == 5))     { value[1] += 10; }
        else
        if ((value[1] == 2) || (value[1] == 3) || (value[1] == 6) || (value[1] == 7))     { value[1] += 6; }
        else
        if ((value[1] == 8) || (value[1] == 9) || (value[1] == 12) || (value[1] == 13))   { value[1] -= 6; }
        else
        if ((value[1] == 10) || (value[1] == 11) || (value[1] == 14) || (value[1] == 15)) { value[1] -= 10; }


        if ((value[2] == 0)  || (value[2] == 1)  || (value[2] == 4) || (value[2] == 5) || (value[2] == 8) ||
            (value[2] == 9)  || (value[2] == 12) || (value[2] == 13)) { value[2] += 2; }
        else
        if ((value[2] == 2)  || (value[2] == 3)  || (value[2] == 6) || (value[2] == 7) || (value[2] == 10) ||
            (value[2] == 11) || (value[2] == 14) || (value[2] == 15)) { value[2] -= 2; }


        if ((value[3] == 0) || (value[3] == 4)) { value[3] += 11; }
        else
        if ((value[3] == 1) || (value[3] == 5)) { value[3] += 9; }
        else
        if ((value[3] == 2) || (value[3] == 6)) { value[3] += 7; }
        else
        if ((value[3] == 3) || (value[3] == 7)) { value[3] += 5; }
        else
        if ((value[3] == 8) || (value[3] == 12)) { value[3] -= 5; }
        else
        if ((value[3] == 9) || (value[3] == 13)) { value[3] -= 7; }
        else
        if ((value[3] == 10) || (value[3] == 14)) { value[3] -= 9; }
        else
        if ((value[3] == 11) || (value[3] == 15)) { value[3] -= 11; }


        if (((value[7] >=0 ) && (value[7] <=3)) || ((value[7] >= 8 ) && (value[7] <=11))) { value[7] += 4; }
        else
        if (((value[7] >= 4) && (value[7] <= 7)) || ((value[7] >= 12) && (value[7] <= 15))) { value[7] -= 4; }

Где массив value[] это то, что должно у нас появиться после работы функции Decod.

А что же дальше? После обработки каждого отдельного шестнадцатеричного значения, нужно будет свернуть 8 шестнадцатеричных чисел обратно в 4 байта. И в этом нам поможет функция:
char *Encod(char *value, unsigned int len)
    {
        char *en = new char[len/2];
        for (unsigned int i = 0, j = 0; i < len; i += 2, j++)
        {
            en[j] = (unsigned char)((unsigned char)(value[i] * 16) + (unsigned char)value[i + 1]);
            //Console.WriteLine(en[j]);
        }

        return en;
    }
Здесь все происходит ровно наоборот, если сравнивать с функцией Decod, то есть создаем из пары 16-чных чисел одно десятичное.

Пора собирать все это в единый монолит и лишь надеяться на очередное чудо.

Исходный код клиента, который принимает, обрабатывает ID и отправляет серверу.
Для наглядности включена функция вывод массива на экран, которая сначала показывает значения ID пришедшие с сервера в количестве 8 штук (то есть те самые 8 16-чных значений, после разбиения 4 байтов на куски), а затем этот же ID после «магических пассов» и до отправки на сервер.

Маленькое чудо своими руками мы сотворили.

2 комментария: