Added initial netmsg handlers, need to confirm them with current demo protocol

This commit is contained in:
Jordan Cristiano 2015-05-03 20:13:35 -04:00
parent 5fc4c59245
commit 9a6c95a327
4 changed files with 603 additions and 23 deletions

View File

@ -1,6 +1,7 @@
#include "demofile.h" #include "demofile.h"
#include "demofilebitbuf.h" #include "demofilebitbuf.h"
#include "netmessages.h"
#include <assert.h> #include <assert.h>
#include <vector> #include <vector>
#include <fstream> #include <fstream>
@ -11,18 +12,21 @@ void ParseGameEvent(const std::string& eventBuf)
{ {
CBitRead bitBuf(eventBuf.data(), eventBuf.size()); CBitRead bitBuf(eventBuf.data(), eventBuf.size());
uint32 eventId = bitBuf.ReadUBitLong(9); uint32 eventId = bitBuf.ReadUBitLong(9);
printf("%s\n", eventNames[eventId-1].c_str()); printf("%s\n", eventNames[eventId].c_str());
} }
void ParsePacket(const std::string& packetBuf) void ParsePacket(const char* packetBuf, const int numBytes)
{ {
auto data = packetBuf.data(); assert(numBytes <= NET_MAX_PAYLOAD);
CBitRead bitBuf(packetBuf.data(), packetBuf.size()); CBitRead bitBuf(packetBuf, numBytes);
uint32 typeId = bitBuf.ReadUBitLong(5); while (bitBuf.GetNumBitsLeft() >= NETMSG_TYPE_BITS)
{
uint32 typeId = bitBuf.ReadUBitLong(NETMSG_TYPE_BITS);
printf("%i\n", typeId); printf("%i\n", typeId);
if (typeId != 25) ProcessNetMsg(typeId, bitBuf);
/*if (typeId != 25)
{ {
return; break;
} }
uint32 length = bitBuf.ReadUBitLong(11); uint32 length = bitBuf.ReadUBitLong(11);
@ -32,7 +36,8 @@ void ParsePacket(const std::string& packetBuf)
subpacket.resize(numBytes); subpacket.resize(numBytes);
bitBuf.ReadBits(&subpacket[0], length); bitBuf.ReadBits(&subpacket[0], length);
ParseGameEvent(subpacket); ParseGameEvent(subpacket);*/
}
} }
void ParseEventNames(const char* eventfile, std::vector<std::string>& eventNames) void ParseEventNames(const char* eventfile, std::vector<std::string>& eventNames)
@ -45,6 +50,24 @@ void ParseEventNames(const char* eventfile, std::vector<std::string>& eventNames
} }
} }
void ParseSignonData(const std::string& signonData)
{
CBitRead bitbuf(signonData.data(), signonData.length());
const char cmd = bitbuf.ReadChar();
assert(cmd == dem_signon);
const int32 tick = bitbuf.ReadLong();
bitbuf.SeekRelative(sizeof(democmdinfo_t) * 8);
const int32 seq1 = bitbuf.ReadLong();
const int32 seq2 = bitbuf.ReadLong();
assert(seq1 == seq2);
const int32 numBytes = bitbuf.ReadLong();
std::string packet;
packet.resize(numBytes);
bitbuf.ReadBytes(&packet[0], numBytes);
ParsePacket(packet.data(), numBytes);
}
int main(const int argc, const char* argv[]) int main(const int argc, const char* argv[])
{ {
if (argc < 3) if (argc < 3)
@ -62,12 +85,14 @@ int main(const int argc, const char* argv[])
auto demoHeader = demoFile.GetDemoHeader(); auto demoHeader = demoFile.GetDemoHeader();
ParseSignonData(demoFile.GetSignOnData());
unsigned char cmd; unsigned char cmd;
int32 tick; int32 tick;
int32 sequenceInfo1; int32 sequenceInfo1;
int32 sequenceInfo2; int32 sequenceInfo2;
democmdinfo_t cmdInfo; democmdinfo_t cmdInfo;
std::string packetBuf; char* packetBuf = (char*)malloc(NET_MAX_PAYLOAD);
demoFile.ReadCmdHeader(cmd, tick); demoFile.ReadCmdHeader(cmd, tick);
assert(cmd == dem_synctick && tick == 0); assert(cmd == dem_synctick && tick == 0);
@ -81,11 +106,13 @@ int main(const int argc, const char* argv[])
switch (cmd) switch (cmd)
{ {
case dem_packet: case dem_packet:
{
demoFile.ReadCmdInfo(cmdInfo); demoFile.ReadCmdInfo(cmdInfo);
demoFile.ReadSequenceInfo(sequenceInfo1, sequenceInfo2); demoFile.ReadSequenceInfo(sequenceInfo1, sequenceInfo2);
assert(sequenceInfo1 == sequenceInfo2); assert(sequenceInfo1 == sequenceInfo2);
demoFile.ReadRawData(packetBuf); const int32 length = demoFile.ReadRawData(packetBuf, NET_MAX_PAYLOAD);
ParsePacket(packetBuf); ParsePacket(packetBuf, length);
}
break; break;
case dem_stop: case dem_stop:
assert(i == numFrames && tick == numTicks); assert(i == numFrames && tick == numTicks);
@ -95,10 +122,12 @@ int main(const int argc, const char* argv[])
case dem_usercmd: case dem_usercmd:
case dem_datatables: case dem_datatables:
default: default:
assert(false);
break; break;
} }
} }
free(packetBuf);
demoFile.Close(); demoFile.Close();
return 0; return 0;

View File

@ -233,6 +233,29 @@ public:
int64 ReadSignedVarInt64() { return bitbuf::ZigZagDecode64( ReadVarInt64() ); } int64 ReadSignedVarInt64() { return bitbuf::ZigZagDecode64( ReadVarInt64() ); }
}; };
inline unsigned int CBitRead::PeekUBitLong(int numbits)
{
int nSaveBA = m_nBitsAvail;
int nSaveW = m_nInBufWord;
uint32 const *pSaveP = m_pDataIn;
unsigned int nRet = ReadUBitLong(numbits);
m_nBitsAvail = nSaveBA;
m_nInBufWord = nSaveW;
m_pDataIn = pSaveP;
return nRet;
}
inline int CBitRead::ReadLong(void)
{
return (int)ReadUBitLong(sizeof(long) << 3);
}
inline float CBitRead::ReadFloat(void)
{
uint32 nUval = ReadUBitLong(sizeof(long) << 3);
return *((float *)&nUval);
}
#ifndef MIN #ifndef MIN
#define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) #define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
#endif #endif

414
demboyz/netmessages.cpp Normal file
View File

@ -0,0 +1,414 @@
#include "netmessages.h"
#include "demofilebitbuf.h"
#include <cassert>
namespace math
{
unsigned int log2(unsigned int value)
{
unsigned int res = 0;
while (value >>= 1)
++res;
return res;
}
unsigned int Bits2Bytes(unsigned int bits)
{
return ((bits + 7) >> 3);
}
}
void Net_NOP(CBitRead& bitbuf)
{
// nothing
}
void Net_Disconnect(CBitRead& bitbuf)
{
char message[1024];
bitbuf.ReadString(message, sizeof(message));
}
void Net_File(CBitRead& bitbuf)
{
const unsigned int transferID = bitbuf.ReadUBitLong(32);
char filename[1024];
bitbuf.ReadString(filename, sizeof(filename));
const bool isRequest = bitbuf.ReadOneBit() != 0;
}
void Net_Tick(CBitRead& bitbuf)
{
static const float NET_TICK_SCALEUP = 100000.0f;
const int tick = bitbuf.ReadLong();
const float hostFrameTime = (float)bitbuf.ReadUBitLong(16) / NET_TICK_SCALEUP;
const float hostFrameTimeStdDev = (float)bitbuf.ReadUBitLong(16) / NET_TICK_SCALEUP;
}
void Net_StringCmd(CBitRead& bitbuf)
{
char commandBuffer[1024];
bitbuf.ReadString(commandBuffer, sizeof(commandBuffer));
}
void Net_SetConVar(CBitRead& bitbuf)
{
typedef struct cvar_s
{
char name[MAX_OSPATH];
char value[MAX_OSPATH];
} cvar_t;
const int numVars = bitbuf.ReadByte();
cvar_t cvar;
for (int i = 0; i < numVars; ++i)
{
bitbuf.ReadString(cvar.name, sizeof(cvar.name));
bitbuf.ReadString(cvar.value, sizeof(cvar.value));
}
}
void Net_SignonState(CBitRead& bitbuf)
{
const int signonState = bitbuf.ReadByte();
//assert(signonState == SIGNONSTATE_PRESPAWN);
const int spawnCount = bitbuf.ReadLong();
}
void SVC_Print(CBitRead& bitbuf)
{
char textBuffer[2048];
bitbuf.ReadString(textBuffer, sizeof(textBuffer));
}
void SVC_ServerInfo(CBitRead& bitbuf)
{
const int protocol = bitbuf.ReadShort(); // protocol version
const int serverCount = bitbuf.ReadLong(); // number of changelevels since server start
const bool isHLTV = bitbuf.ReadOneBit() != 0; // HLTV server ?
const bool isDedicated = bitbuf.ReadOneBit() != 0; // dedicated server ?
const int clientCRC = bitbuf.ReadLong(); // client.dll CRC server is using
const int maxClasses = bitbuf.ReadWord(); // max number of server classes
if (protocol <= 17)
{
const int mapCRC = bitbuf.ReadLong(); // server map CRC
}
else
{
char unknown[16];
bitbuf.ReadBytes(unknown, sizeof(unknown));
}
const int playerSlot = bitbuf.ReadByte(); // our client slot number
const int maxClients = bitbuf.ReadByte(); // max number of clients on server
const float tickInterval = bitbuf.ReadFloat(); // server tick interval
const char os = bitbuf.ReadChar(); // 'l' = linux, 'w' = Win32
char gameDir[MAX_OSPATH]; // game directory eg "tf2"
char mapName[MAX_OSPATH]; // name of current map
char skyName[MAX_OSPATH]; // name of current skybox
char hostName[MAX_OSPATH]; // host name
bitbuf.ReadString(gameDir, sizeof(gameDir));
bitbuf.ReadString(mapName, sizeof(mapName));
bitbuf.ReadString(skyName, sizeof(skyName));
bitbuf.ReadString(hostName, sizeof(hostName));
}
void SVC_SendTable(CBitRead& bitbuf)
{
const bool needsDecoder = bitbuf.ReadOneBit() != 0;
const int dataLengthBits = bitbuf.ReadShort();
bitbuf.SeekRelative(dataLengthBits);
}
void SVC_ClassInfo(CBitRead& bitbuf)
{
typedef struct class_s
{
int classID;
char datatablename[256];
char classname[256];
} class_t;
const int numServerClasses = bitbuf.ReadShort();
const int nServerClassBits = math::log2(numServerClasses) + 1;
// if true, client creates own SendTables & classinfos from game.dll
const bool createOnClient = bitbuf.ReadOneBit() != 0;
if (!createOnClient)
{
class_t serverclass;
for (int i = 0; i < numServerClasses; ++i)
{
serverclass.classID = bitbuf.ReadUBitLong(nServerClassBits);
bitbuf.ReadString(serverclass.classname, sizeof(serverclass.classname));
bitbuf.ReadString(serverclass.datatablename, sizeof(serverclass.datatablename));
}
}
}
void SVC_SetPause(CBitRead& bitbuf)
{
const bool paused = bitbuf.ReadOneBit() != 0;
}
void SVC_CreateStringTable(CBitRead& bitbuf)
{
if (bitbuf.PeekUBitLong(8) == ':')
{
bitbuf.ReadByte();
}
char tableName[256];
bitbuf.ReadString(tableName, sizeof(tableName));
const int maxEntries = bitbuf.ReadWord();
const int encodeBits = math::log2(maxEntries);
const int numEntries = bitbuf.ReadUBitLong(encodeBits + 1);
const int lengthInBits = bitbuf.ReadUBitLong(NET_MAX_PALYLOAD_BITS + 3);
const bool userDataFixedSize = bitbuf.ReadOneBit() != 0;
if (userDataFixedSize)
{
const int userDataSize = bitbuf.ReadUBitLong(12);
const int userDataSizeBits = bitbuf.ReadUBitLong(4);
}
bitbuf.SeekRelative(lengthInBits);
}
void SVC_UpdateStringTable(CBitRead& bitbuf)
{
const int tableId = bitbuf.ReadUBitLong(math::log2(MAX_TABLES));
const int numChangedEntries = (bitbuf.ReadOneBit() != 0) ? bitbuf.ReadWord() : 1;
const int lengthInBits = bitbuf.ReadUBitLong(20);
bitbuf.SeekRelative(lengthInBits);
}
void SVC_VoiceInit(CBitRead& bitbuf)
{
char voiceCodec[MAX_OSPATH]; // used voice codec .dll
bitbuf.ReadString(voiceCodec, sizeof(voiceCodec));
const int quality = bitbuf.ReadByte(); // custom quality setting
}
void SVC_VoiceData(CBitRead& bitbuf)
{
const int fromClientIndex = bitbuf.ReadByte();
const bool proximity = !!bitbuf.ReadByte();
const int lengthInBits = bitbuf.ReadWord();
bitbuf.SeekRelative(lengthInBits);
}
void SVC_HLTV(CBitRead& bitbuf)
{
//const int state = bitbuf.ReadByte();
assert(false);
}
void SVC_Sounds(CBitRead& bitbuf)
{
const bool reliableSound = bitbuf.ReadOneBit() != 0;
int numSounds;
int lengthInBits;
if (reliableSound)
{
numSounds = 1;
lengthInBits = bitbuf.ReadUBitLong(8);
}
else
{
numSounds = bitbuf.ReadUBitLong(8);
lengthInBits = bitbuf.ReadUBitLong(16);
}
bitbuf.SeekRelative(lengthInBits);
}
void SVC_SetView(CBitRead& bitbuf)
{
const int entIndex = bitbuf.ReadUBitLong(MAX_EDICT_BITS);
}
void SVC_FixAngle(CBitRead& bitbuf)
{
const bool relative = bitbuf.ReadOneBit() != 0;
const float x = bitbuf.ReadBitAngle(16);
const float y = bitbuf.ReadBitAngle(16);
const float z = bitbuf.ReadBitAngle(16);
}
void SVC_CrosshairAngle(CBitRead& bitbuf)
{
const float x = bitbuf.ReadBitAngle(16);
const float y = bitbuf.ReadBitAngle(16);
const float z = bitbuf.ReadBitAngle(16);
}
void SVC_BSPDecal(CBitRead& bitbuf)
{
Vector pos;
bitbuf.ReadBitVec3Coord(pos);
const int decalTextureIndex = bitbuf.ReadUBitLong(MAX_DECAL_INDEX_BITS);
if (bitbuf.ReadOneBit() != 0)
{
const int entIndex = bitbuf.ReadUBitLong(MAX_EDICT_BITS);
const int modelIndex = bitbuf.ReadUBitLong(SP_MODEL_INDEX_BITS);
}
else
{
const int entIndex = 0;
const int modelIndex = 0;
}
const bool lowPriority = bitbuf.ReadOneBit() != 0;
}
void SVC_TerrainMod(CBitRead& bitbuf)
{
assert(false);
}
void SVC_UserMessage(CBitRead& bitbuf)
{
const int msgType = bitbuf.ReadByte();
// max 256 * 8 bits, see MAX_USER_MSG_DATA
const int lengthInBits = bitbuf.ReadUBitLong(11);
bitbuf.SeekRelative(lengthInBits);
}
void SVC_EntityMessage(CBitRead& bitbuf)
{
const int entIndex = bitbuf.ReadUBitLong(MAX_EDICT_BITS);
const int classID = bitbuf.ReadUBitLong(MAX_SERVER_CLASS_BITS);
// max 256 * 8 bits
const int lengthInBits = bitbuf.ReadUBitLong(11);
bitbuf.SeekRelative(lengthInBits);
}
void SVC_GameEvent(CBitRead& bitbuf)
{
const int lengthInBits = bitbuf.ReadUBitLong(11);
//uint32 eventId = bitbuf.ReadUBitLong(9);
bitbuf.SeekRelative(lengthInBits);
}
void SVC_PacketEntities(CBitRead& bitbuf)
{
const int maxEntries = bitbuf.ReadUBitLong(MAX_EDICT_BITS);
const bool isDelta = bitbuf.ReadOneBit() != 0;
if (isDelta)
{
const int deltaFrom = bitbuf.ReadLong();
}
else
{
const int deltaFrom = -1;
}
const int numBaseline = bitbuf.ReadUBitLong(1);
const int numUpdatedEntries = bitbuf.ReadUBitLong(MAX_EDICT_BITS);
const int lengthInBits = bitbuf.ReadUBitLong(DELTASIZE_BITS);
const bool updateBaseline = bitbuf.ReadOneBit() != 0;
bitbuf.SeekRelative(lengthInBits);
}
void SVC_TempEntities(CBitRead& bitbuf)
{
const int numEntries = bitbuf.ReadUBitLong(EVENT_INDEX_BITS);
const int lengthInBits = bitbuf.ReadUBitLong(NET_MAX_PALYLOAD_BITS);
bitbuf.SeekRelative(lengthInBits);
}
void SVC_Prefetch(CBitRead& bitbuf)
{
enum
{
SOUND = 0,
};
const short type = SOUND; // bitbuf.ReadUBitLong(1);
const short soundIndex = bitbuf.ReadUBitLong(MAX_SOUND_INDEX_BITS);
}
void SVC_Menu(CBitRead& bitbuf)
{
typedef enum
{
DIALOG_MSG = 0, // just an on screen message
DIALOG_MENU, // an options menu
DIALOG_TEXT, // a richtext dialog
DIALOG_ENTRY, // an entry box
DIALOG_ASKCONNECT // Ask the client to connect to a specified IP address. Only the "time" and "title" keys are used.
} DIALOG_TYPE;
DIALOG_TYPE type = (DIALOG_TYPE)bitbuf.ReadShort();
const int lengthInBytes = bitbuf.ReadWord();
bitbuf.SeekRelative(lengthInBytes * 8);
}
void SVC_GameEventList(CBitRead& bitbuf)
{
const int numEvents = bitbuf.ReadUBitLong(MAX_EVENT_BITS);
for (int i = 0; i < numEvents; ++i)
{
const int id = bitbuf.ReadUBitLong(MAX_EVENT_BITS);
char name[MAX_EVENT_NAME_LENGTH];
bitbuf.ReadString(name, sizeof(name));
printf("%s\n", name);
assert(false);
// gameeventmanager.cpp ParseEventList
}
const int lengthInBits = bitbuf.ReadUBitLong(20);
bitbuf.SeekRelative(lengthInBits);
}
void SVC_GetCvarValue(CBitRead& bitbuf)
{
typedef int QueryCvarCookie_t;
QueryCvarCookie_t cookie = bitbuf.ReadSBitLong(32);
char cvarName[256];
bitbuf.ReadString(cvarName, sizeof(cvarName));
}
static const int NUM_MESSAGES = static_cast<uint8_t>(NetMsg::SVC_LASTMSG) + 1;
void ProcessNetMsg(const std::uint32_t msgType, CBitRead& bitbuf)
{
static NetMsgFn netMsgHandler[NUM_MESSAGES] =
{
&Net_NOP,
&Net_Disconnect,
&Net_File,
&Net_Tick,
&Net_StringCmd,
&Net_SetConVar,
&Net_SignonState,
&SVC_Print,
&SVC_ServerInfo,
&SVC_SendTable,
&SVC_ClassInfo,
&SVC_SetPause,
&SVC_CreateStringTable,
&SVC_UpdateStringTable,
&SVC_VoiceInit,
&SVC_VoiceData,
&SVC_HLTV,
&SVC_Sounds,
&SVC_SetView,
&SVC_FixAngle,
&SVC_CrosshairAngle,
&SVC_BSPDecal,
&SVC_TerrainMod,
&SVC_UserMessage,
&SVC_EntityMessage,
&SVC_GameEvent,
&SVC_PacketEntities,
&SVC_TempEntities,
&SVC_Prefetch,
&SVC_Menu,
&SVC_GameEventList,
&SVC_GetCvarValue
};
assert(msgType < NUM_MESSAGES);
netMsgHandler[msgType](bitbuf);
}

114
demboyz/netmessages.h Normal file
View File

@ -0,0 +1,114 @@
#pragma once
#include <cstdint>
class CBitRead;
typedef void(*NetMsgFn)(CBitRead& bitbuf);
enum constants
{
// was 5
NETMSG_TYPE_BITS = 6, // 2^NETMSG_TYPE_BITS > SVC_LASTMSG
// was 96000
NET_MAX_PAYLOAD = 288000, // largest message size in bytes
// was 17
NET_MAX_PALYLOAD_BITS = 19, // 2^NET_MAX_PALYLOAD_BITS > NET_MAX_PAYLOAD
// table index is sent in log2(MAX_TABLES) bits
MAX_TABLES = 32, // Table id is 4 bits
// How many bits to use to encode an edict.
MAX_EDICT_BITS = 11, // # of bits needed to represent max edicts
// Max # of edicts in a level
MAX_EDICTS = (1<<MAX_EDICT_BITS),
MAX_DECAL_INDEX_BITS = 9,
SP_MODEL_INDEX_BITS = 11,
MAX_SERVER_CLASS_BITS = 9,
MAX_EVENT_NAME_LENGTH = 32,
MAX_EVENT_BITS = 9,
MAX_EVENT_NUMBER = (1 << MAX_EVENT_BITS),
MAX_EVENT_BYTES = 1024,
DELTASIZE_BITS = 20, // must be: 2^DELTASIZE_BITS > (NET_MAX_PAYLOAD * 8)
EVENT_INDEX_BITS = 8,
MAX_SOUND_INDEX_BITS = 13,
SIGNONSTATE_NONE = 0, // no state yet, about to connect
SIGNONSTATE_CHALLENGE = 1, // client challenging server, all OOB packets
SIGNONSTATE_CONNECTED = 2, // client is connected to server, netchans ready
SIGNONSTATE_NEW = 3, // just got serverinfo and string tables
SIGNONSTATE_PRESPAWN = 4, // received signon buffers
SIGNONSTATE_SPAWN = 5, // ready to receive entity packets
SIGNONSTATE_FULL = 6, // we are fully connected, first non-delta packet received
SIGNONSTATE_CHANGELEVEL = 7 // server is changing level, please wait
};
enum class NetMsg: std::uint8_t
{
net_NOP = 0, // nop command used for padding
net_Disconnect = 1, // disconnect, last message in connection
net_File = 2, // file transmission message request/deny
net_Tick = 3, // send last world tick
net_StringCmd = 4, // a string command
net_SetConVar = 5, // sends one/multiple convar settings
net_SignonState = 6, // signals current signon state
//
// server to client
//
svc_Print = 7, // print text to console
svc_ServerInfo = 8, // first message from server about game, map etc
svc_SendTable = 9, // sends a sendtable description for a game class
svc_ClassInfo = 10, // Info about classes (first byte is a CLASSINFO_ define).
svc_SetPause = 11, // tells client if server paused or unpaused
svc_CreateStringTable = 12, // inits shared string tables
svc_UpdateStringTable = 13, // updates a string table
svc_VoiceInit = 14, // inits used voice codecs & quality
svc_VoiceData = 15, // Voicestream data from the server
//svc_HLTV = 16, // HLTV control messages
svc_Sounds = 17, // starts playing sound
svc_SetView = 18, // sets entity as point of view
svc_FixAngle = 19, // sets/corrects players viewangle
svc_CrosshairAngle = 20, // adjusts crosshair in auto aim mode to lock on traget
svc_BSPDecal = 21, // add a static decal to the worl BSP
// NOTE: This is now unused!
// svc_TerrainMod = 22, // modification to the terrain/displacement
// Message from server side to client side entity
svc_UserMessage = 23, // a game specific message
svc_EntityMessage = 24, // a message for an entity
svc_GameEvent = 25, // global game event fired
svc_PacketEntities = 26, // non-delta compressed entities
svc_TempEntities = 27, // non-reliable event object
svc_Prefetch = 28, // only sound indices for now
svc_Menu = 29, // display a menu from a plugin
svc_GameEventList = 30, // list of known games events and fields
svc_GetCvarValue = 31, // Server wants to know the value of a cvar on the client.
SVC_LASTMSG = 31 // last known server messages
};
void ProcessNetMsg(const std::uint32_t msgType, CBitRead& bitbuf);