PDA

Просмотр полной версии : [Lineage2] rGuard Interlude


wongerlt
23.03.2017, 14:05
Hello,
Any one tryed to adapt it on interlude?
Im trying, all work perfect except "Olympiad hero list tab" i get this error:
General protection fault!

History: UUIScript::execGetPledgeCrestTexFromPledgeCrestID <- UObject::ProcessEvent <- (HeroTowerWnd Transient.HeroTowerWnd0, Function Interface.HeroTowerWnd.OnEvent) <- XMLUIManager::ExecuteUIEvent <- NConsoleWnd::OnHeroList <- UGameEngine::OnHeroList <- ExHeroListPacket <- UNetworkHandler::Tick <- Function Name=ExHeroListPacke <- UGameEngine::Tick <- UpdateWorld <- MainLoop


Where can be problem?
rGaurd part of code:
void __cdecl new_SendPacket(unsigned int This, char *Format, ...)
{
if(enumas==0)
enumas=1;

unsigned char buf[10240];
int size = 0, len;
wchar_t *wstr;

va_list args;
va_start(args, Format);

while (*Format != 0)
{

switch (*Format)
{
case 'c':
*(unsigned char*) (buf + size) = va_arg(args, unsigned char);
size++;
break;
case 'h':
*(unsigned short int*) (buf + size) = va_arg(args, unsigned short int);
size += 2;
break;
case 'd':
*(unsigned int*) (buf + size) = va_arg(args, unsigned int);
size += 4;
break;
case 'Q':
*(unsigned __int64*) (buf + size) = va_arg(args, unsigned __int64);
size += 8;
break;
case 'b':
len = va_arg(args, unsigned int);
memcpy(buf + size, va_arg(args, void*), len);
size += len;
break;
case 'S':
wstr = va_arg(args, wchar_t*);
if (wstr == 0)
{
len = 2;
*(unsigned short int*) (buf + size) = 0;
}
else
{
len = wcslen(wstr) * 2 + 2;
memcpy(buf + size, wstr, len);
}
size += len;
break;
default:
true_SendPacket(This, "cc", 0xA1, 0x04);
ErrorExit("Send Packet Unknown Format!");
break;
}
Format++;
}

va_end(args);

true_SendPacket(This, "b", size, (int)buf);
}

int __fastcall new_AddNetworkQueue(unsigned int This, unsigned int EDX, TNetworkPacket *NetworkPacket)
{
if(enumas==0) enumas=1;
if (SendPacket == NULL) {
__asm {
mov ebx, ecx
mov ecx, [ebx + 0x48] ; see offset in disassembler
mov edx, [ecx]
mov _clientSocket, ecx
mov ecx, [edx + 0x68]; in disassembler too
mov _sendPacket, ecx;
}
true_SendPacket = (_SendPacket) splice((unsigned char*) _sendPacket, new_SendPacket);
SendPacket = 1;
}
return true_AddNetworkQueue(This, EDX, NetworkPacket);
}

ScythLab
23.03.2017, 19:12
unsigned char buf = (unsigned char*)malloc(65535);

and
free(buf);
after
true_SendPacket(This, "b", size, (int)buf);

wongerlt
24.03.2017, 13:15
unsigned char buf = (unsigned char*)malloc(65535);

and
free(buf);
after
true_SendPacket(This, "b", size, (int)buf);

both codes after true_SendPacket...?

Now i get error about buf (unsigned char differs in levels of indirection from unsigned char[1024]) and about cannot convert from unsigned char* to unsigned char

wongerlt
28.03.2017, 13:20
Any one? :)

Добавлено через 1 час 12 минут
Ok, im fixed errors but same situtation im still get critical error on hero list in game.

ScythLab
29.03.2017, 12:23
Либо забудь о программировании, либо начинай думать самостоятельно.
Код, который я тебе привел, элементарен и прост, если ты его не можешь понять и скомпилировать, то найми себе программиста, кто сделает это за тебя, а сам занимайся более полезными вещами.

alexov
30.03.2017, 02:55
ща поцан такой русский выучит еще чтобы твой коммент прочитать))
я вот сам до сих пор не могу собраться за c++ взяться чтобы сплайсинг сделать))

ScythLab
30.03.2017, 21:30
alexov, пацан, если хоть немного мозгов есть, то воспользуется переводчиком. К тому же это этого его проблемы, не мои, что он зашел на русский ресурс и здесь надеется, чтобы за него все решили.
Ну а ты если возьмешься за с++, то я так думаю, что осилишь как заюзать массив на 64к вместо жалких 10, которых для некоторых пакетов не хватает ;)

alexov
31.03.2017, 03:30
жестко как-то ты последнее время отвечаешь всем) злой стал))

я массив динамически выделяю, если пакет не влезает, массив заново выделяется. то есть на первых пакетах эта куча созданий массива, но зато потом он остается как раз того размера как нужен ни больше ни меньше)

а на с++ мне поди даже память выделять не придется, я хочу сделать просто коннектор: что приходит в функцию, то сразу в сокет записывать, отправлять себе по сети. а дальше уж на шарпе горы свернуть не вопрос)

Smwr
31.03.2017, 08:09
то есть на первых пакетах эта куча созданий массива
но зачем такое? под size выделено 4 байта -> максимум 2^16, столько можно выделить один раз и не задумываться.

то сразу в сокет записывать, отправлять себе по сети.
куда по сети если не секрет?

ScythLab
31.03.2017, 19:35
жестко как-то ты последнее время отвечаешь всем) злой стал))не люблю людей, кто головой думать не хочет, особенно тех кто кричит "помогите, помогите, не получается", начинаешь ему помогать, а по факту человек просто хочет чтобы за него все сделали

но зачем такое? под size выделено 4 байта -> максимум 2^16, столько можно выделить один раз и не задумываться.кстати, здесь все же крайне желательно использовать локальные переменные и так сказать "локальную" память, т.е. либо стек буферов свой делать и бегать по ним, либо постоянно выделять память (эта операция очень быстрая благодаря менеджеру памяти компилятора)

Smwr
31.03.2017, 22:00
кстати, здесь все же крайне желательно использовать локальные переменные и так сказать "локальную" память, т.е. либо стек буферов свой делать и бегать по ним, либо постоянно выделять память (эта операция очень быстрая благодаря менеджеру памяти компилятора)
буффер будет отправлен немедленно в том же потоке, зачем постоянно перевыделять, если можно выделить один раз?

alexov
31.03.2017, 23:34
но зачем такое? под size выделено 4 байта -> максимум 2^16, столько можно выделить один раз и не задумываться.


куда по сети если не секрет?

1) Мы в таком случае выделяем больше памяти чем нужно. Как-то это не оптимально. А однажды окажется что мы выделяем меньше памяти чем нужно, и мы упадем. Плохо. Алгоритмы должны быть адаптивными.

2) У меня бот на C#, работает через перехват траффика. на некоторых серверах траффик зашифрован., а значит его нужно хватать через сплайсинг., но делается это на Delphi или C++, поэтому я хочу сделать коннектор и отправлять "чистый" траффик в бота.

Smwr
01.04.2017, 09:02
1) Мы в таком случае выделяем больше памяти чем нужно. Как-то это не оптимально. А однажды окажется что мы выделяем меньше памяти чем нужно, и мы упадем. Плохо. Алгоритмы должны быть адаптивными.
спорить о выделении лишних 20-30 килобайт для запаса это даже не смешно :D:D
Кстати говоря, выделение динамической памяти не самая быстрая операция, её рекомендуют сторониться.

alexov
01.04.2017, 10:37
спорить о выделении лишних 20-30 килобайт для запаса это даже не смешно :D:D
Кстати говоря, выделение динамической памяти не самая быстрая операция, её рекомендуют сторониться.

сегодня я пишу софтину для 10 клиентов, а завтра для 10000. я хочу чтобы мой код нормально масштабировался. И я не хочу вылететь когда пакеты внезапно станут больше. И выделение памяти происходит буквально пару раз лишних, это не критично

supernewbie
01.04.2017, 10:57
alexov, можно изловчиться и сделать один буфер на один поток, передавая этот буфер текущему боту с которым поток работает.
в итоге если ядер скажем 4, то это всего-лишь 4 * 64K байт на буферы

ScythLab
01.04.2017, 18:10
буффер будет отправлен немедленно в том же потоке, зачем постоянно перевыделять, если можно выделить один раз?ты перехватил функцию Send, тебе нужно в ней до 64к памяти, когда ты собираешься выделить эту память? внутри функции и сохранить ссылку на память в глобальной переменной? если так, то следующей вызов функции может идти из другого потока и причем параллельно с предыдущим, в итоге крах ла2, либо дисконнект

alexov, можно изловчиться и сделать один буфер на один поток, передавая этот буфер текущему боту с которым поток работает.
в итоге если ядер скажем 4, то это всего-лишь 4 * 64K байт на буферыты хочешь сказать, что если у меня одноядерный проц, то я не смогу запустить на нем, скажем, 100 потоков? :)

здесь 2 простых решения:
1) глобальный массив массивов (можно его динамическим сделать, тогда все будет кошерно) и использовать его по кругу (даже паттерн программирования такой есть)
2) выделять память каждый раз внутри функции Send; если память гарантированно освобождать после этого и посмотреть на работу менеджера памяти, то можно увидеть, что операция очень и очень быстрая (на 100 циклов выделений/освобождения потребуется пару микросекунд, а ла2 очень редко когда пропускается хотя бы 100 пакетов/секунду)

PS. второй вариант проще, т.к. в (1) все равно придется делать некоторую синхронизацию по работе с общим буфером

Smwr
01.04.2017, 18:23
следующей вызов функции может идти из другого потока и причем параллельно с предыдущим
не может. клиент дергает этот метод только из главного потока.
а дергать его ботом не из главного даже самая стремная защита не позволит.



и вообще, как то вы все усложняете простые вещи :D:D

ScythLab
01.04.2017, 22:52
не может. клиент дергает этот метод только из главного потока.я бы сказал, что "в основном" дергает из главного потока, не зря же у них внутри идет работа с критическими секциями, значит они что-то знают ;)
PS. а если ты пишешь код для одного потока, то тупо делаешь глобальный статический массив и не паришься

а дергать его ботом не из главного даже самая стремная защита не позволит.а я дергал и мне защиты позволяли, только я их хорошо просил :D

и вообще, как то вы все усложняете простые вещи :D:Dиногда такие усложнения так много волос на жопе берегут, что лучше лишний раз перебздеть

alexov
02.04.2017, 03:39
alexov, можно изловчиться и сделать один буфер на один поток, передавая этот буфер текущему боту с которым поток работает.
в итоге если ядер скажем 4, то это всего-лишь 4 * 64K байт на буферы

можно изловчиться и сделать 10-20 переменных во всей программе и переиспользовать их по мере освобождения) но это потребует такой нетривиальной логики которую ни один человек не осилит)

Ну а если серьезно - использовать на 4х ядерном процессоре только 4 потока, это хорошая идея, для этого и придумали асинхронное программирование. Я так и делаю. Но вот в чем загвоздка, при асинхронном программировании код превращается в последовательность из коллбэков, а порядком их исполнения рулит тредпул.

Так вот к чему это я. Если для каждого клиента создать полный контекст из данных которые ему нужны, включая буфер для сетевых данных, код получается простой и красивый, потому что нам не важно в каком порядке на самом деле исполняется код. Но как только мы заведем общий буфер для группы клиентов, сразу появляются ограничения на то как мы должны писать код, чтобы этот буфер не запороть. Не знаю как это понятнее объяснить, но я думаю что те кто уже пишет асинхронный код, должны понять.

supernewbie
02.04.2017, 11:42
ты хочешь сказать, что если у меня одноядерный проц, то я не смогу запустить на нем, скажем, 100 потоков? :)

сможешь, но зачем?) если внутри потока не используется блокирующих функций кроме ожидания когда все возможные на данный момент действия завершены, то поток будет максимально эффективно использовать ядро, конечно если так сделать не получится то можно и 100 и 1000 потоков сделать, но если прикинуть то на 10000 ботов вряд ли понадобится даже 1000 потоков, потому что это получается по 10 ботов на поток, что за неоптимальное использование там будет происходить?) вообще даже при неоптимальном коде можно использовать формулу типа {количество_ядер * коэффициент_неоптимальности} и запускать скажем по 10 потоков на ядро, чтобы система разобралась кто простаивает, а кто нет. в итоге 40 потоков и в них можно впихнуть теже 10000 ботов по 250 ботов на поток, а дальше уже дело оптимизации, сможешь ли ты написать тело обработки одного бота чтобы 250 ботов успевали обработаться за уделенное им время. но и да, если код оптимален то производить потоки более чем на 90% загружающих ядро в количестве большем, чем имеется ядер, нет смысла, потому как это только ухудшит производительность, плюс кстати у потоков же ещё стек выделяется + куча внутренних поточных штук, так что каждый поток это ещё блок памяти. в общем лучше писать оптимальный код, создавать минимум потоков, создавать один общий буфер для одного потока и отдавать одному потоку как можно больше ботов на обработку. но в реальности как получится так и будет :D

Но как только мы заведем общий буфер для группы клиентов, сразу появляются ограничения на то как мы должны писать код, чтобы этот буфер не запороть
идея в следующем (далее псевдокод):

переменные на каждый поток:
общий_сетевой_буфер = массив байт равный максимальному размеру пакета (65535 в нашем случае)
боты = список ботов для обработки

тело_потока:

для каждого бота в ботах потока делаем:
{
бот.установить_буфер(общий_сетевой_буфер);
// в этом моменте бот использует общий буфер потока
// для приема и отправки данных
бот.получить_сетевые_данные_используя_буфер();
бот.послать_данные_используя_буфер();
бот.аннулировать_буфер();
// в этом моменте у бота больше нет буфера
// в следующей итерации общий буфер перейдет к следующему боту
}

при такой архитектуре единственное что нужно держать в памяти экземпляра бота это переменные необходимые для воссоздания пакетов при записи их в буфер для отправки и буферизацию фрагментированных пакетов при приеме фрагметированных данных, но буфер для фрагментов можно сделать равным максимально возможной длине элемента пакета (скажем максимальная длина строки - 64 символа) в итоге буфер для фрагментов пакета - (64 * 2 + 2) байт

в общем что-то такое можно придумать)

ScythLab
02.04.2017, 19:54
сможешь, но зачем?)ты путаешь программирование для каких-нибудь расчетов, там где тебе нужно максимально эффективно использовать ресурсы компа и обычные программы, которые, как правило, проектируются с точки зрения удобства разработки, поддержки, и чтобы у пользователя интерфейс по минимуму тормозил, тогда как в фоне идет работа с сетью, диском и прочими данными. Как раз ла2 является представителем второй категории, да и боту никогда не нужно столько расчетов, чтобы задумываться об эффективности использования каждого ядра и ручного распределения своих потоков между ядрами (у меня бот использует менее 1% от моего проца, и может его подгружать только при прорисовке карты, т.к. этот участок крайне не оптимален и медленный)

alexov
03.04.2017, 06:09
идея в следующем (далее псевдокод):

переменные на каждый поток:
общий_сетевой_буфер = массив байт равный максимальному размеру пакета (65535 в нашем случае)
боты = список ботов для обработки

тело_потока:

для каждого бота в ботах потока делаем:
{
бот.установить_буфер(общий_сетевой_буфер);
// в этом моменте бот использует общий буфер потока
// для приема и отправки данных
бот.получить_сетевые_данные_используя_буфер();
бот.послать_данные_используя_буфер();
бот.аннулировать_буфер();
// в этом моменте у бота больше нет буфера
// в следующей итерации общий буфер перейдет к следующему боту
}



Повторюсь, экономить потоки это хорошая идея. Но делать это надо не так. В том что ты предлагаешь следующие проблемы:
1) ручная балансировка ботов между потоками
2) прием данных бывает либо блокирующий, что в твоем случае не подходит. либо асинхронный. что в твоем случае не подходит. либо активное ожидание - привет дополнительные задержки и излишний расход процессорного времени.

И снова мы приходим к тому что код должен быть полностью асинхронный. А в асинхронном коде ограничение на неразрывность обработки одного пакета является злом. Вот захочу я посреди обработки пакета отправить пакет другому клиенту, в этот момент я должен отпустить поток исполняющий мой код, и прощайте буфера.

Yegor
04.04.2017, 01:45
код должен быть полностью асинхронный.

Тоесть получить доп задержки ожидания месаджей от винды?

supernewbie
04.04.2017, 02:57
Yegor, если не вызывать WaitMessage то мессаджей ждать не придется, если ты об этом


поделитесь пожалуйста серверами без шифрации и протоколом около Gracia Final - High Five для бототестов :)

alexov
04.04.2017, 06:41
Тоесть получить доп задержки ожидания месаджей от винды?

О каких задержках идет речь? можно пруф?
лично у меня никаких задержек нет

Yegor
05.04.2017, 01:27
Yegor, если не вызывать WaitMessage то мессаджей ждать не придется, если ты об этом

Каким образом в асинхронном режиме ваш процесс узнает что пришла очередная порция данных в буфер, новое подключение и т.п.?
Вашему коду ждать не прийдется но месаг будет пойман и обработан в главном потоке ожидания месаджей, а это все задержки.

supernewbie
05.04.2017, 01:52
Yegor, хехе, я об этом подозревал, но не хотел вслух говорить.

но насколько долгие эти задержки? по хорошему надо затестить этот момент и сравнить с WSAPoll и с реализацией на блокирующих функциях (1 сокет = 2 потока). может быть это уже кем-нибудь было сделано?

alexov
05.04.2017, 07:55
Каким образом в асинхронном режиме ваш процесс узнает что пришла очередная порция данных в буфер, новое подключение и т.п.?
Вашему коду ждать не прийдется но месаг будет пойман и обработан в главном потоке ожидания месаджей, а это все задержки.

Скажу честно я не вникал как это работает в ядре Windows, я пишу на C#, внимательно изучил документацию и провел свои тесты.

Пусть это немного наивно, но вот что я сделал:
1) взял в аренду VDS, пропинговал его, получил пинг 59мс (да я знаю что пинг идет по ICMP)
2) написал свой сервер и свой клиент, подключился и дал 50mbps нагрузку по траффику через много копий клиента, сделал замеры времени ответа средствами языка, у меня снова получилось 59мс. 0% загрузка процессора, и ровно 4 потока в серверном приложении.

может быть задержки о которых вы говорите измеряются в наносекундах?) какая тогда разница

wongerlt
24.08.2017, 03:01
so any one know where problem?
void __cdecl new_SendPacket(unsigned int This, char *Format, ...)
{
if(enumas==0)
enumas=1;

unsigned char *buf;
buf = (unsigned char *) malloc(65535);
int size = 0, len;
wchar_t *wstr;
va_list args;
va_start(args, Format);

while (*Format != 0)
{

switch (*Format)
{
case 'c':
*(unsigned char*) (buf + size) = va_arg(args, unsigned char);
size++;
break;
case 'h':
*(unsigned short int*) (buf + size) = va_arg(args, unsigned short int);
size += 2;
break;
case 'd':
*(unsigned int*) (buf + size) = va_arg(args, unsigned int);
size += 4;
break;
case 'Q':
*(unsigned __int64*) (buf + size) = va_arg(args, unsigned __int64);
size += 8;
break;
case 'b':
len = va_arg(args, unsigned int);
memcpy(size+buf, va_arg(args, void*), len);
size += len;
break;
case 'S':
wstr = va_arg(args, wchar_t*);
if (wstr == 0)
{
len = 2;
*(unsigned short int*) (buf + size) = 0;
}
else
{
len = wcslen(wstr) * 2 + 2;
memcpy(size+buf, wstr, len);
}
size += len;
break;
default:
true_SendPacket(This, "cc", 0xA1, 0x04);
break;
}
Format++;
}

va_end(args);


true_SendPacket(This, "b", size, (int)buf);
free(buf);
}
all work but still error on hero list.

alexov
24.08.2017, 10:22
translate #5 post in this thread

wongerlt
24.08.2017, 14:57
with him code same error on game..