- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 538
- Реакции
- 14
Для тех, кто плотно копает CryEngine и сетевую часть Warface, выкладываю пример реализации отправки RMI-запросов (RequestShootHit) через расширения. Трюк в том, что нам не нужно хардкодить адреса функций, если можно заюзать нативные методы IGameObject и GetRMIExtension.
Ниже основные структуры для сериализации хитов и прожектайлов. Это база, на которой строится сетевой урон в игре.
Структуры данных запроса:
Основной пакет SvRequestShootHit:
Включает в себя позицию выстрела, направление, разброс и контейнеры прожектайлов. Сериализация здесь критична, так как любая ошибка в типах данных приведет к десинхрону или кику от MRAC за невалидный пакет.
Механика вызова через InvokeRMI:
Вместо того чтобы искать сигнатуру функции стрельбы и прыгать по оффсетам, можно дернуть InvokeRMI у объекта предмета.
Нюансы реализации:
Кто ковырял последние билды — как сейчас обстоят дела с проверкой predictionHandle на стороне сервера, гайки еще сильнее закрутили?
Ниже основные структуры для сериализации хитов и прожектайлов. Это база, на которой строится сетевой урон в игре.
Структуры данных запроса:
Код:
struct SvRequestHit
{
EntityId targetId; // 0x00
int32 material; // 0x04
int32 typeId; // 0x08
int32 partId; // 0x0C
Vec3 pos; // 0x10
f32 damageReduction; // 0x1C
uint8 physCounter; // 0x20
void SerializeWith(TSerialize ser)
{
ser.Value(0, targetId, 'eid');
ser.Value(0, material, 'mat');
ser.Value(0, typeId, 'hTyp');
ser.Value(0, partId, 'binv');
ser.Value(0, damageReduction, 'hVal');
ser.Value(0, pos);
ser.Value(0, physCounter, 'ui2');
}
};
Основной пакет SvRequestShootHit:
Включает в себя позицию выстрела, направление, разброс и контейнеры прожектайлов. Сериализация здесь критична, так как любая ошибка в типах данных приведет к десинхрону или кику от MRAC за невалидный пакет.
Код:
struct SvRequestShootHit
{
Vec3 shootPos; // 0x00
int32 predictionHandle; // 0x0C
Vec3 dir; // 0x10
uint8 physCounter; // 0x1C
std::vector<SvRequestProjectile> projectiles; // 0x20
uint8 fireCounter; // 0x38
f32 spreadMax; // 0x3C
bool nulled; // 0x40
int32 bulletType; // 0x44
void SerializeWith(TSerialize ser)
{
ser.Value(0, predictionHandle, 'phdl');
ser.Value(0, dir, 'dir3');
ser.Value(0, shootPos);
ser.Value(0, physCounter, 'ui2');
ser.Value(0, fireCounter, 'ui8');
ser.Value(0, spreadMax, 'sprd');
ser.Value(0, nulled, 'bool');
ser.Value(0, bulletType);
// ... логика сериализации вектора ...
}
};
Механика вызова через InvokeRMI:
Вместо того чтобы искать сигнатуру функции стрельбы и прыгать по оффсетам, можно дернуть InvokeRMI у объекта предмета.
Код:
void RequestShootHit(IItem* pItem, SvRequestShootHit& request)
{
auto rmiExt = pItem->GetRMIExtension("RMI:CItemProxy:SvRequestShootHit");
pItem->GetGameObject()->InvokeRMI(rmiExt, request, eRMI_ToServer, -1);
}
Нюансы реализации:
- Этот метод гораздо стабильнее при обновах игры, так как опирается на строковые идентификаторы расширений.
- Для корректной работы вам все равно придется восстановить классы CRMIBody и TSerialize, если вы пишете internal софт.
- MRAC активно чекает аномальный урон и частоту таких запросов. Если слать пакеты без реального выстрела на стороне клиента, мануалбан прилетит очень быстро.
- Не забывайте про physCounter — если счетчик физики не совпадет с серверным, хит просто не засчитает.
Кто ковырял последние билды — как сейчас обстоят дела с проверкой predictionHandle на стороне сервера, гайки еще сильнее закрутили?