Гайд API SF | Урок 4 - RakNet

Статус
В этой теме нельзя размещать новые ответы.

CleanLegend

Известный
Автор темы
Всефорумный модератор
476
928
Введение
RakNet — это сетевой движок, используемый в SA:MP для обмена данными между игроками и сервером. Клиент отправляет пакет серверу, сервер его обрабатывает и рассылает другим игрокам — таким образом это работает.

В этом уроке я покажу, как отправлять свои пакеты и заносить в них данные, как перехватывать отправляемые/получаемые пакеты и читать и перезаписывать данные.

Отправка пакетов
Пример отправки пакета ID_PLAYER_SYNC:

C++:
stOnFootData sync; // объявляем объект структуры stOnFootData, в которой хранятся данные.
memset( &sync, 0, sizeof( stOnFootData ) ); // обнуляем его.

sync = SF->getSAMP()->getPlayers()->pLocalPlayer->onFootData; // копируем данные из структуры локального игрока.

sync.byteHealth = 100; // записываем значение числа жизней ( к примеру ).

BitStream bsActorSync; // объявляем объект класса BitStream, в котором хранятся пакетные данные.
bsActorSync.Write( ( BYTE ) ID_PLAYER_SYNC ); // записываем ID пакета.
bsActorSync.Write( ( PCHAR ) &sync, sizeof( stOnFootData ) ); // записываем данные из структуры sync
SF->getRakNet()->SendPacket( &bsActorSync ); // отправляем пакет на сервер.
RPC (Remote Procedure Call) — оболочка пакета ID_RPC, предназначенная для удалённого выполнения определенных событий. Все RPC перечислены в RPCEnumeration и ScriptRPCEnumeration.

Пример использования RPC_RequestClass, который запрашивает у сервера сменить наш класс (скин) на сервере:

C++:
void CALLBACK cmd_setclass( std::string param ) // объявляем чат-команду /setclass <id>
{
   BitStream bsClass; // объявляем объект класса BitStream, в котором хранятся пакетные данные.
   bsClass.Write( std::stoi( param ) ); // записываем в него ID переданный в команду.
   SF->getRakNet()->SendRPC( RPC_RequestClass, &bsClass );    // отправляем RPC
};

C++:
SF->getSAMP()->registerChatCommand( "setclass", cmd_setclass );    // регистрируем команду
Перехват пакетов
Средствами API можно установить четыре вида перехватов:

  1. RAKHOOK_TYPE_OUTCOMING_RPC — RPC, отправленный серверу;
  2. RAKHOOK_TYPE_OUTCOMING_PACKET — пакет, отправленный серверу;
  3. RAKHOOK_TYPE_INCOMING_RPC — RPC, пришедший от сервера;
  4. RAKHOOK_TYPE_INCOMING_PACKET — пакет, пришедший от сервера.
Пример перехвата отправляемого пакета ID_PLAYER_SYNC:

C++:
bool CALLBACK outcomingData( stRakNetHookParams *params ) // определение callback-функции, которая будет вызвана при отправке какого либо пакета
{
   if( params->packetId == PacketEnumeration::ID_PLAYER_SYNC ) // если отправляемый пакет — это ID_PLAYER_SYNC
   {
       stOnFootData data; // определяем объект, в который сохраним отправляемые данные
       memset( &data, 0, sizeof( stOnFootData ) ); // обнуляем его
       byte packet;

       params->bitStream->ResetReadPointer(); // на всякий случай устанавливаем оффсет чтения на начало
       params->bitStream->Read( packet ); // читаем ID пакета
       params->bitStream->Read( (PCHAR)&data, sizeof( stOnFootData ) ); // читаем отправляемые данные
       params->bitStream->ResetReadPointer(); // снова обнуляем оффсет чтения

       SF->getSAMP()->getChat()->AddChatMessage( D3DCOLOR_XRGB( 255, 255, 0 ), "Наша скорость: %.2f %.2f %.2f",
           data.fMoveSpeed[0], data.fMoveSpeed[1], data.fMoveSpeed[2] ); // пишем в чат скорость нашего передвижения, записанную в пакет

               data.fMoveSpeed[0] = rand()%10;
       data.fMoveSpeed[1] = rand()%10;
       data.fMoveSpeed[2] = rand()%10; // перезаписали скорость на случайную; получается эффект, похожий на Pizdarvanka.

       params->bitStream->ResetWritePointer(); // обнуляем оффсет записи
       params->bitStream->Write( packet ); // пишем ид пакета
       params->bitStream->Write( (PCHAR)&data, sizeof( stOnFootData ) ); // пишем обновлённые данные  
   };

   return true; // успешно завершаем отправку пакета
};

SF->getRakNet()->registerRakNetCallback( RakNetScriptHookType::RAKHOOK_TYPE_OUTCOMING_PACKET, outcomingData ); // регистрируем callback
Пример перехвата входящего (пришедшего от сервера) RPC_ScrServerJoin:

C++:
bool CALLBACK incomingRPC( stRakNetHookParams *params ) // определение callback-функции, которая будет вызвана, если от сервера был получен новый RPC.
{
   if( params->packetId == ScriptRPCEnumeration::RPC_ScrServerJoin ) // если это RPC_ScrServerJoin
   {
       short int sPlayerID;  
       D3DCOLOR D3DPlayerColor;
       byte isNPC, nameLen;
       char szPlayerName[25];

       params->bitStream->ResetReadPointer(); // обнуляем оффсет чтения.
       params->bitStream->Read( sPlayerID ); // читаем ID игрока.
       params->bitStream->Read( D3DPlayerColor ); // цвет ника игрока.
       params->bitStream->Read( isNPC ); // флаг, говорящий о том, NPC это или нет.
       params->bitStream->Read( nameLen ); // длина ника.
       params->bitStream->Read( szPlayerName, nameLen ); // ник.
       szPlayerName[ nameLen ]= '\0'; // обрезаем, чтоб не было мусора
       params->bitStream->ResetReadPointer(); // обнуляем оффсет чтения

       SF->getSAMP()->getChat()->AddChatMessage( D3DPlayerColor, "%s[%d] Подключился к серверу.",
           szPlayerName, sPlayerID ); // добавляем сообщение в чат.          
   };

   return true; // успешно завершаем обработку RPC.
};

SF->getRakNet()->registerRakNetCallback( RakNetScriptHookType::RAKHOOK_TYPE_INCOMING_RPC, incomingRPC ); // регистрируем callback
Автор: urShadow
 
Статус
В этой теме нельзя размещать новые ответы.