[INC][CS:GO] Send messages to GC + Change real StatTrak value

vanz666

Участник
Сообщения
45
Реакции
157
Инклуд реализует функции для отправки сообщений на GameCoordinator.
В качестве примера приложил плагин, меняющий реальное значение счетчика StatTrak текущего оружия на любое число.
Для работы необходимо расширение SteamWorks
C-подобный:
#if defined _gcmessages_included
 #endinput
#endif
#define _gcmessages_included

#include <steamworks>

#define GCMSG_BUF_SIZE 8192
#define GCMSG_PROTO_MASK 0x80000000

enum
{
    ProtoWireType_Varint = 0,
    ProtoWireType_64bit,
    ProtoWireType_LenDelim,
    ProtoWireType_StartGroup,
    ProtoWireType_EndGroup,
    ProtoWireType_32bit
}

static char g_msgBuf[GCMSG_BUF_SIZE];

static int g_msgPos = -1;

stock void GCMsg_StartMessage(int msgType)
{
    if (g_msgPos != -1)
    {
        LogError("GC message already started.");
        return;
    }

    msgType |= GCMSG_PROTO_MASK;
    
    g_msgBuf[0] = msgType;
    g_msgBuf[1] = msgType >> 8;
    g_msgBuf[2] = msgType >> 16;
    g_msgBuf[3] = msgType >> 24;
    
    g_msgPos = 8;
}

stock void GCMsg_EndMessage()
{
    if (g_msgPos == -1)
    {
        LogError("GC message not started.");
        return;
    }

    int msgType = g_msgBuf[0] | (g_msgBuf[1] << 8) | (g_msgBuf[2] << 16) | (g_msgBuf[3] << 24);
    
    EGCResults result = SteamWorks_SendMessageToGC(msgType, g_msgBuf, g_msgPos);
    
    if (result != k_EGCResultOK)
        LogError("Failed to send GC message (%d)", result);
        
    g_msgPos = -1;
}

stock void GCMsg_WriteString(const char[] data, int field_number)
{
    if (g_msgPos == -1)
    {
        LogError("GC message not started.");
        return;
    }

    g_msgBuf[g_msgPos++] = (field_number << 3) | ProtoWireType_LenDelim;
    
    int strLen = strlen(data);
    
    WriteVarInt32(strLen);
    
    strcopy(g_msgBuf[g_msgPos], sizeof(g_msgBuf) - g_msgPos, data);
    
    g_msgPos += strLen;
}

stock void GCMsg_WriteInt32(int data, int field_number)
{
    if (g_msgPos == -1)
    {
        LogError("GC message not started.");
        return;
    }

    g_msgBuf[g_msgPos++] = (field_number << 3) | ProtoWireType_Varint;
    
    WriteVarInt32(data);
}

stock void GCMsg_WriteInt64(const int data[2], int field_number)
{
    if (g_msgPos == -1)
    {
        LogError("GC message not started.");
        return;
    }

    g_msgBuf[g_msgPos++] = (field_number << 3) | ProtoWireType_Varint;
    
    WriteVarInt64(data);
}

stock void GCMsg_WriteSInt32(int data, int field_number)
{
    GCMsg_WriteInt32(EncodeZigZag32(data), field_number);
}

stock void GCMsg_WriteSInt64(const int data[2], int field_number)
{
    int encoded[2];
    encoded = data;
    EncodeZigZag64(encoded);
    GCMsg_WriteInt64(encoded, field_number);
}

stock void GCMsg_WriteFixed32(int data, int field_number)
{
    if (g_msgPos == -1)
    {
        LogError("GC message not started.");
        return;
    }

    g_msgBuf[g_msgPos++] = (field_number << 3) | ProtoWireType_32bit;
    
    g_msgBuf[g_msgPos++] = data;
    g_msgBuf[g_msgPos++] = data >> 8;
    g_msgBuf[g_msgPos++] = data >> 16;
    g_msgBuf[g_msgPos++] = data >> 24;
}

stock void GCMsg_WriteFixed64(const int data[2], int field_number)
{
    if (g_msgPos == -1)
    {
        LogError("GC message not started.");
        return;
    }

    g_msgBuf[g_msgPos++] = (field_number << 3) | ProtoWireType_64bit;
    
    g_msgBuf[g_msgPos++] = data[0];
    g_msgBuf[g_msgPos++] = data[0] >> 8;
    g_msgBuf[g_msgPos++] = data[0] >> 16;
    g_msgBuf[g_msgPos++] = data[0] >> 24;
    g_msgBuf[g_msgPos++] = data[1];
    g_msgBuf[g_msgPos++] = data[1] >> 8;
    g_msgBuf[g_msgPos++] = data[1] >> 16;
    g_msgBuf[g_msgPos++] = data[1] >> 24;
}

stock void GCMsg_WriteFloat(float data, int field_number)
{
    GCMsg_WriteFixed32(view_as<int>(data), field_number);
}

stock void GCMsg_WriteBool(bool data, int field_number)
{
    GCMsg_WriteInt32(data, field_number);
}

static void WriteVarInt32(int data)
{
    g_msgBuf[g_msgPos] = data | 0x80;
    
    if ((data ^ 0x80000000) >= ((1 << 7) ^ 0x80000000))
    {
        g_msgBuf[g_msgPos + 1] = (data >>> 7) | 0x80;
        
        if ((data ^ 0x80000000) >= ((1 << 14) ^ 0x80000000))
        {
            g_msgBuf[g_msgPos + 2] = (data >>> 14) | 0x80;
            
            if ((data ^ 0x80000000) >= ((1 << 21) ^ 0x80000000))
            {
                g_msgBuf[g_msgPos + 3] = (data >>> 21) | 0x80;
                
                if ((data ^ 0x80000000) >= ((1 << 28) ^ 0x80000000))
                {
                    g_msgBuf[g_msgPos + 4] = data >>> 28;
                    g_msgPos += 5;
                }
                else
                {
                    g_msgBuf[g_msgPos + 3] &= 0x7F;
                    g_msgPos += 4;
                }
            }
            else
            {
                g_msgBuf[g_msgPos + 2] &= 0x7F;
                g_msgPos += 3;
            }
        }
        else
        {
            g_msgBuf[g_msgPos + 1] &= 0x7F;
            g_msgPos += 2;
        }
    }
    else
    {
        g_msgBuf[g_msgPos++] &= 0x7F;
    }
}

static void WriteVarInt64(const int data[2])
{
    int split[3];
    
    split[0] =  data[0];
    split[1] = (data[1] << 4) | (data[0] >>> 28);
    split[2] =  data[1] >>> 24;
    
    int size;

    if (split[2] == 0)
    {
        if (split[1] == 0)
        {
            if ((split[0] ^ 0x80000000) < ((1 << 14) ^ 0x80000000))
            {
                if ((split[0] ^ 0x80000000) < ((1 << 7) ^ 0x80000000))
                    size = 1;
                else
                    size = 2;
            }
            else
            {
                if ((split[0] ^ 0x80000000) < ((1 << 21) ^ 0x80000000))
                    size = 3;
                else
                    size = 4;
            }
        }
        else
        {
            if ((split[1] ^ 0x80000000) < ((1 << 14) ^ 0x80000000))
            {
                if ((split[1] ^ 0x80000000) < ((1 << 7) ^ 0x80000000))
                    size = 5;
                else
                    size = 6;
            }
            else
            {
                if ((split[1] ^ 0x80000000) < ((1 << 21) ^ 0x80000000))
                    size = 7;
                else
                    size = 8;
            }
        }
    }
    else
    {
        if ((split[2] ^ 0x80000000) < ((1 << 7) ^ 0x80000000))
            size = 9;
        else
            size = 10;
    }
    
    for (int i = size - 1; i >= 0; i--)
        g_msgBuf[g_msgPos + i] = (split[i / 4] >>> ((i * 7) % 28)) | 0x80;
    
    g_msgBuf[g_msgPos + (size - 1)] &= 0x7F;
    
    g_msgPos += size;
}

static int EncodeZigZag32(int data)
{
    return (data << 1) ^ (data >> 31);
}

static void EncodeZigZag64(int data[2])
{
    int a = data[0] >>> 31;
    int b = data[1] >> 31;
    
    data[0] =  (data[0] << 1)        ^ b;
    data[1] = ((data[1] << 1) | a)    ^ b;
}
C-подобный:
#pragma semicolon 1

#include <sourcemod>
#include <gcmessages>

#pragma newdecls required

// https://github.com/SteamDatabase/GameTracking-CSGO/blob/master/Protobufs/econ_gcmessages.proto#L72
enum
{
    k_EMsgGC_IncrementKillCountAttribute = 1074
}

// https://github.com/SteamDatabase/GameTracking-CSGO/blob/master/Protobufs/base_gcmessages.proto#L160
enum
{
    CMsgIncrementKillCountAttribute_killer_account_id     = 1,
    CMsgIncrementKillCountAttribute_victim_account_id     = 2,
    CMsgIncrementKillCountAttribute_item_id             = 3,
    CMsgIncrementKillCountAttribute_event_type             = 4,
    CMsgIncrementKillCountAttribute_amount                 = 5
}

public void OnPluginStart()
{
    RegConsoleCmd("givemekills", Command_GiveMeKills);
}

public Action Command_GiveMeKills(int client, int args)
{
    if (!client || !IsClientInGame(client) || !IsPlayerAlive(client) || args < 1)
        return Plugin_Handled;

    char arg[32];
    GetCmdArg(1, arg, sizeof(arg));

    int victim;

    for (victim = 1; victim <= MaxClients; victim++)
        if (victim != client && IsClientConnected(victim) && !IsFakeClient(victim) && IsClientAuthorized(victim))
            break;
            
    if (victim > MaxClients)
        return Plugin_Handled;

    IncrementKillCountAttribute(client, victim, StringToInt(arg));
    
    return Plugin_Handled;
}

void IncrementKillCountAttribute(   int killer/* Игрок которому устанавливаем килы */,
                                    int victim/* Любой игрок на сервере отличный от killer */,
                                    int amount/* Кол-во килов которое добавляется к счетчику */)
{
    int weapon = GetEntPropEnt(killer, Prop_Send, "m_hActiveWeapon");

    if (weapon == -1)
        return;
    
    int itemId[2];
    itemId[0] = GetEntProp(weapon, Prop_Send, "m_iItemIDLow");
    itemId[1] = GetEntProp(weapon, Prop_Send, "m_iItemIDHigh");

    int killer_account_id = GetSteamAccountID(killer);
    int victim_account_id = GetSteamAccountID(victim);
    int event_type = 0;

    GCMsg_StartMessage(k_EMsgGC_IncrementKillCountAttribute);
    
    GCMsg_WriteFixed32(killer_account_id,     CMsgIncrementKillCountAttribute_killer_account_id);
    GCMsg_WriteFixed32(victim_account_id,     CMsgIncrementKillCountAttribute_victim_account_id);
    GCMsg_WriteInt64(itemId,                 CMsgIncrementKillCountAttribute_item_id);
    GCMsg_WriteInt32(event_type,             CMsgIncrementKillCountAttribute_event_type);
    GCMsg_WriteInt32(amount,                 CMsgIncrementKillCountAttribute_amount);

    GCMsg_EndMessage();
}
 
Последнее редактирование:

rejchev

менеджер клоунов
Сообщения
1,669
Реакции
1,291
У кого то эту манеру написания кода уже видел 🧐
 

Truyn

King of clowns
Сообщения
1,591
Реакции
749
Код меняет реальное значение счетчика StatTrak текущего оружия (если таковой имеется)
Для работы необходимо расширение SteamWorks
C-подобный:
void IncrementKillCountAttribute(   int killer/* Игрок которому устанавливаем килы */,
                                    int victim/* Любой игрок на сервере отличный от killer */,
                                    int amount/* Кол-во килов которое добавляется к счетчику */)
{
    int weapon = GetEntPropEnt(killer, Prop_Send, "m_hActiveWeapon");

    if (weapon == -1)
        return;
  
    int itemId[2];
    itemId[0] = GetEntProp(weapon, Prop_Send, "m_iItemIDLow");
    itemId[1] = GetEntProp(weapon, Prop_Send, "m_iItemIDHigh");

    int killer_account_id = GetSteamAccountID(killer);
    int victim_account_id = GetSteamAccountID(victim);
    int event_type = 0;

    int msgType = 1074 | 0x80000000; // k_EMsgGC_IncrementKillCountAttribute = 1074
  
    char msgData[41];
  
    msgData[0] = msgType;
    msgData[1] = msgType >> 8;
    msgData[2] = msgType >> 16;
    msgData[3] = msgType >> 24;

    // optional fixed32 killer_account_id = 1;
    msgData[8]  = (1 << 3) | 5;
    msgData[9]  = killer_account_id;
    msgData[10] = killer_account_id >> 8;
    msgData[11] = killer_account_id >> 16;
    msgData[12] = killer_account_id >> 24;
  
    // optional fixed32 victim_account_id = 2;
    msgData[13] = (2 << 3) | 5;
    msgData[14] = victim_account_id;
    msgData[15] = victim_account_id >> 8;
    msgData[16] = victim_account_id >> 16;
    msgData[17] = victim_account_id >> 24;
  
    // optional uint64 item_id = 3;
    msgData[18] = (3 << 3) | 0;
    msgData[19] =   itemId[0] | 0x80;
    msgData[20] =  (itemId[0] >> 7) | 0x80;
    msgData[21] =  (itemId[0] >> 14) | 0x80;
    msgData[22] =  (itemId[0] >> 21) | 0x80;
    msgData[23] = ((itemId[0] >> 28) & 0xF) | ((itemId[1] & 0x7) << 4) | 0x80;
    msgData[24] =  (itemId[1] >> 3) | 0x80;
    msgData[25] =  (itemId[1] >> 10) | 0x80;
    msgData[26] =  (itemId[1] >> 17) | 0x80;
    msgData[27] =  (itemId[1] >> 24) | 0x80;
    msgData[28] =  (itemId[1] >> 31) & 0x1;

    // optional uint32 event_type = 4;
    msgData[29] = (4 << 3) | 0;
    msgData[30] =  event_type | 0x80;
    msgData[31] = (event_type >> 7) | 0x80;
    msgData[32] = (event_type >> 14) | 0x80;
    msgData[33] = (event_type >> 21) | 0x80;
    msgData[34] = (event_type >> 28) & 0xF;
  
    // optional uint32 amount = 5;
    msgData[35] = (5 << 3) | 0;
    msgData[36] =  amount | 0x80;
    msgData[37] = (amount >> 7) | 0x80;
    msgData[38] = (amount >> 14) | 0x80;
    msgData[39] = (amount >> 21) | 0x80;
    msgData[40] = (amount >> 28) & 0xF;

    SteamWorks_SendMessageToGC(msgType, msgData, sizeof(msgData));
}
Не кидай палками а можно как до дополнить хотя бы парой команд к кому применить и на сколько увеличить
 

Mr_panica

XenForo one 💖
Сообщения
921
Реакции
436
Не кидай палками а можно как до дополнить хотя бы парой команд к кому применить и на сколько увеличить
Можно купить готовое
1589200691924.png

[CS: GO] - StatTrak Core
 

Mr_panica

XenForo one 💖
Сообщения
921
Реакции
436
раз есть такой вариант зачем платить?
Раз там отдельный плагин, да ещё и приватный, то он будет поддерживаться.
Ну и модули есть для випа и шопа, вряд ли тут будет поддержка этого)
Ну, и 300 рублей, я думаю, что на продаже такого функционала в випке, это окупится мгновенно)
Хотя знать не могу, в КС не играю)
 

vanz666

Участник
Сообщения
45
Реакции
157
шо следующее)? стикеры? брелки?
Разве плагина на стикеры нет в свободном доступе? Помню где-то выкладывали.
А вообще, можно менять любые атрибуты оружия, имитируя сообщения от gamecoordinator'а, будь то стикеры, скины и т.д.
В SteamWorks для этого есть форварды SteamWorks_GCSendMessage и SteamWorks_GCRetrieveMessage, но лично у меня они работают как пустышки, то есть никогда не вызываются.
Если у кого есть желание можно написать автору расширения по этому поводу. А также было бы супер добавить в них поддержку типа protobuf, как это сделано для usermessages, чтобы вручную не парсить.
Не кидай палками а можно как до дополнить хотя бы парой команд к кому применить и на сколько увеличить
Сделал на команду givemekills <кол-во килов>, при этом на сервере вместе с собой должен находиться еще один стим игрок.
Для уменьшения указать отрицательное значение.
C-подобный:
#pragma semicolon 1

#include <sourcemod>
#include <steamworks>

#pragma newdecls required

public void OnPluginStart()
{
    RegConsoleCmd("givemekills", Command_GiveMeKills);
}

public Action Command_GiveMeKills(int client, int args)
{
    if (!client || !IsClientInGame(client) || !IsPlayerAlive(client) || args < 1)
        return Plugin_Handled;

    char arg[32];
    GetCmdArg(1, arg, sizeof(arg));

    int victim;

    for (victim = 1; victim <= MaxClients; victim++)
        if (victim != client && IsClientConnected(victim) && !IsFakeClient(victim) && IsClientAuthorized(victim))
            break;
        
    if (victim > MaxClients)
        return Plugin_Handled;

    IncrementKillCountAttribute(client, victim, StringToInt(arg));

    return Plugin_Handled;
}

void IncrementKillCountAttribute(   int killer/* Игрок которому устанавливаем килы */,
                                    int victim/* Любой игрок на сервере отличный от killer */,
                                    int amount/* Кол-во килов которое добавляется к счетчику */)
{
    int weapon = GetEntPropEnt(killer, Prop_Send, "m_hActiveWeapon");

    if (weapon == -1)
        return;

    int itemId[2];
    itemId[0] = GetEntProp(weapon, Prop_Send, "m_iItemIDLow");
    itemId[1] = GetEntProp(weapon, Prop_Send, "m_iItemIDHigh");

    int killer_account_id = GetSteamAccountID(killer);
    int victim_account_id = GetSteamAccountID(victim);
    int event_type = 0;

    int msgType = 1074 | 0x80000000; // k_EMsgGC_IncrementKillCountAttribute = 1074

    char msgData[41];

    msgData[0] = msgType;
    msgData[1] = msgType >> 8;
    msgData[2] = msgType >> 16;
    msgData[3] = msgType >> 24;

    // optional fixed32 killer_account_id = 1;
    msgData[8]  = (1 << 3) | 5;
    msgData[9]  = killer_account_id;
    msgData[10] = killer_account_id >> 8;
    msgData[11] = killer_account_id >> 16;
    msgData[12] = killer_account_id >> 24;

    // optional fixed32 victim_account_id = 2;
    msgData[13] = (2 << 3) | 5;
    msgData[14] = victim_account_id;
    msgData[15] = victim_account_id >> 8;
    msgData[16] = victim_account_id >> 16;
    msgData[17] = victim_account_id >> 24;

    // optional uint64 item_id = 3;
    msgData[18] = (3 << 3) | 0;
    msgData[19] =   itemId[0] | 0x80;
    msgData[20] =  (itemId[0] >> 7) | 0x80;
    msgData[21] =  (itemId[0] >> 14) | 0x80;
    msgData[22] =  (itemId[0] >> 21) | 0x80;
    msgData[23] = ((itemId[0] >> 28) & 0xF) | ((itemId[1] & 0x7) << 4) | 0x80;
    msgData[24] =  (itemId[1] >> 3) | 0x80;
    msgData[25] =  (itemId[1] >> 10) | 0x80;
    msgData[26] =  (itemId[1] >> 17) | 0x80;
    msgData[27] =  (itemId[1] >> 24) | 0x80;
    msgData[28] =  (itemId[1] >> 31) & 0x1;

    // optional uint32 event_type = 4;
    msgData[29] = (4 << 3) | 0;
    msgData[30] =  event_type | 0x80;
    msgData[31] = (event_type >> 7) | 0x80;
    msgData[32] = (event_type >> 14) | 0x80;
    msgData[33] = (event_type >> 21) | 0x80;
    msgData[34] = (event_type >> 28) & 0xF;

    // optional uint32 amount = 5;
    msgData[35] = (5 << 3) | 0;
    msgData[36] =  amount | 0x80;
    msgData[37] = (amount >> 7) | 0x80;
    msgData[38] = (amount >> 14) | 0x80;
    msgData[39] = (amount >> 21) | 0x80;
    msgData[40] = (amount >> 28) & 0xF;

    SteamWorks_SendMessageToGC(msgType, msgData, 41);
}
 
Последнее редактирование:

vanz666

Участник
Сообщения
45
Реакции
157
Up: добавлен инклуд для работы с GC сообщениями и приложен полный плагин.
 
Сверху Снизу