void PrintStackTrace(int skipCount, const char* executableName) { void* addresses[16]; const int symbolCount = backtrace(addresses, UDT_COUNT_OF(addresses)); if(skipCount >= symbolCount) { return; } char** const messages = backtrace_symbols(addresses, symbolCount); if(messages == NULL) { return; } printf("Stack trace:\n"); for(int i = skipCount; i < symbolCount; ++i) { printf("#%d %s\n", i, messages[i]); if(executableName == NULL) { continue; } char callBuffer[256]; sprintf(callBuffer,"addr2line %p -e %s", addresses[i], executableName); system(callBuffer); } free(messages); }
void udtBaseParser::ResetForGamestateMessage() { _inServerMessageSequence = -1; _inServerCommandSequence = -1; _inReliableSequenceAcknowledge = -1; _inClientNum = -1; _inChecksumFeed = -1; _inParseEntitiesNum = 0; _inServerTime = S32_MIN; _inLastSnapshotMessageNumber = S32_MIN; _outServerCommandSequence = 0; _outSnapshotsWritten = 0; _outWriteFirstMessage = false; _outWriteMessage = false; memset(_inEntityBaselines, 0, sizeof(_inEntityBaselines)); memset(_inSnapshots, 0, sizeof(_inSnapshots)); memset(_inConfigStrings, 0, sizeof(_inConfigStrings)); for(u32 i = 0; i < (u32)UDT_COUNT_OF(_inEntityEventTimesMs); ++i) { _inEntityEventTimesMs[i] = S32_MIN; } _configStringAllocator.Clear(); _tempAllocator.Clear(); _privateTempAllocator.Clear(); }
static void UTF8_WriteCodePoint(u32& newLength, char* output, u32 codePoint, const char* input) { for(u32 i = 0; i < (u32)UDT_COUNT_OF(ShortEscapeCodePoints); ++i) { if(codePoint == ShortEscapeCodePoints[i].CodePoint) { newLength = 2; output[0] = '\\'; output[1] = ShortEscapeCodePoints[i].OutputChar; return; } } // Range: 0x0000 - 0x001F if(codePoint <= 0x001F) { newLength = 6; output[0] = '\\'; output[1] = 'u'; output[2] = HexDigits[(codePoint >> 12) & 15]; output[3] = HexDigits[(codePoint >> 8) & 15]; output[4] = HexDigits[(codePoint >> 4) & 15]; output[5] = HexDigits[codePoint & 15]; return; }
const udtString udtBaseParser::GetConfigString(s32 csIndex) const { if(csIndex < 0 || csIndex >= (s32)UDT_COUNT_OF(_inConfigStrings)) { return udtString::NewEmptyConstant(); } return _inConfigStrings[csIndex]; }
void udtBaseParser::WriteBigConfigStringCommand(const udtString& csIndex, const udtString& csData) { // Simple example: // cs idx "name0\value0\name1\value1\name2\value2\name3\value3" // becomes: // bcs0 idx "name0\value0\" // bcs1 idx "name1\value1\" // bcs1 idx "name2\value2\" // bcs2 idx "name3\value3" const u32 maxLengthPerCmd = MAX_STRING_CHARS - 2; const u32 perCmdOverhead = 8 + csIndex.GetLength(); const u32 maxDataLength = maxLengthPerCmd - perCmdOverhead; u32 outputChunks = 0; for(u32 i = 2;; ++i) { const u32 perCmdData = (csData.GetLength() + i - 1) / i; if(perCmdData + perCmdOverhead <= maxLengthPerCmd) { outputChunks = i; break; } } u32 dataOffset = 0; for(u32 i = 0; i < outputChunks; ++i) { const char* bcsIdxRaw = i == 0 ? "0" : (i == outputChunks - 1 ? "2" : "1"); const udtString data = udtString::NewSubstringRef(csData, dataOffset, bcsIdxRaw[0] == '2' ? (u32)~0 : maxDataLength); const udtString bcs = udtString::NewConstRef("bcs"); const udtString bcsIdx = udtString::NewConstRef(bcsIdxRaw); const udtString space = udtString::NewConstRef(" "); const udtString quote = udtString::NewConstRef("\""); udtString command = udtString::NewEmpty(_tempAllocator, MAX_STRING_CHARS); const udtString* cmdPieces[] = { &bcs, &bcsIdx, &space, &csIndex, &space, "e, &data, "e }; udtString::AppendMultiple(command, cmdPieces, (u32)UDT_COUNT_OF(cmdPieces)); _outMsg.WriteByte(svc_serverCommand); _outMsg.WriteLong(_outServerCommandSequence); _outMsg.WriteString(command.GetPtr(), (s32)command.GetLength()); ++_outServerCommandSequence; dataOffset += maxDataLength; } }
bool udtPath::Combine(udtString& combinedPath, udtVMLinearAllocator& allocator, const udtString& folderPath, const udtString& extra) { const bool isSeparatorNeeded = !HasTrailingSeparator(folderPath); const udtString path = udtString::IsNullOrEmpty(folderPath) ? udtString::NewConstRef(".") : folderPath; const udtString separator = isSeparatorNeeded ? udtString::NewConstRef(GetSeparator()) : udtString::NewEmptyConstant(); const udtString* strings[] = { &path, &separator, &extra }; combinedPath = udtString::NewFromConcatenatingMultiple(allocator, strings, (u32)UDT_COUNT_OF(strings)); return true; }
static bool UTF8_NeedsEscaping(u32& newLength, u32 codePoint) { for(u32 i = 0; i < (u32)UDT_COUNT_OF(ShortEscapeCodePoints); ++i) { if(codePoint == ShortEscapeCodePoints[i].CodePoint) { newLength = 2; // Of form: "\A". return true; } } // Range: 0x0000 - 0x001F if(codePoint <= 0x001F) { newLength = 6; // Of form: "\uABCD". return true; } return false; }
UDT_API(s32) udtGetStringArray(udtStringArray::Id arrayId, const char*** elements, u32* elementCount) { if(elements == NULL || elementCount == NULL) { return (s32)udtErrorCode::InvalidArgument; } switch(arrayId) { case udtStringArray::Weapons: *elements = WeaponNames; *elementCount = (u32)(UDT_COUNT_OF(WeaponNames) - 1); break; case udtStringArray::PowerUps: *elements = PowerUpNames; *elementCount = (u32)(UDT_COUNT_OF(PowerUpNames) - 1); break; case udtStringArray::MeansOfDeath: *elements = MeansOfDeathNames; *elementCount = (u32)(UDT_COUNT_OF(MeansOfDeathNames) - 1); break; case udtStringArray::PlayerMeansOfDeath: *elements = PlayerMeansOfDeathNames; *elementCount = (u32)(UDT_COUNT_OF(PlayerMeansOfDeathNames) - 1); break; case udtStringArray::Teams: *elements = TeamNames; *elementCount = (u32)(UDT_COUNT_OF(TeamNames) - 1); break; case udtStringArray::CutPatterns: *elements = CutPatternNames; *elementCount = (u32)(UDT_COUNT_OF(CutPatternNames) - 1); break; default: return (s32)udtErrorCode::InvalidArgument; } return (s32)udtErrorCode::None; }
void udtBaseParser::WriteGameState() { _outMsg.Init(_outMsgData, sizeof(_outMsgData)); _outMsg.Bitstream(); _outMsg.WriteLong(_inReliableSequenceAcknowledge); _outMsg.WriteByte(svc_gamestate); _outMsg.WriteLong(_outServerCommandSequence); ++_outServerCommandSequence; _protocolConverter->StartGameState(); // Config strings. for(u32 i = 0; i < (u32)UDT_COUNT_OF(_inConfigStrings); ++i) { const udtString& cs = _inConfigStrings[i]; if(_outProtocol == _inProtocol && !udtString::IsNullOrEmpty(cs)) { _outMsg.WriteByte(svc_configstring); _outMsg.WriteShort((s32)i); _outMsg.WriteBigString(cs.GetPtr(), cs.GetLength()); continue; } udtVMScopedStackAllocator allocatorScope(_tempAllocator); udtConfigStringConversion outCs; _protocolConverter->ConvertConfigString(outCs, _tempAllocator, (s32)i, cs.GetPtr(), cs.GetLength()); if(outCs.Index >= 0 && outCs.String.GetLength() > 0) { _outMsg.WriteByte(svc_configstring); _outMsg.WriteShort(outCs.Index); _outMsg.WriteBigString(outCs.String.GetPtr(), outCs.String.GetLength()); } } idLargestEntityState nullState; Com_Memset(&nullState, 0, sizeof(nullState)); // Baseline entities. for(s32 i = 0; i < ID_MAX_PARSE_ENTITIES; ++i) { // We delta from the null state because we write a full entity. const idEntityStateBase* const newState = GetBaseline(i); // Write the baseline entity if it's not filled with 0 integers. if(memcmp(&nullState, newState, _inProtocolSizeOfEntityState)) { _outMsg.WriteByte(svc_baseline); // @NOTE: MSG_WriteBits is called in there with newState.number as an argument. idLargestEntityState newStateOutProto; _protocolConverter->ConvertEntityState(newStateOutProto, *newState); _outMsg.WriteDeltaEntity(&nullState, &newStateOutProto, true); } } _outMsg.WriteByte(svc_EOF); _outMsg.WriteLong(_inClientNum); _outMsg.WriteLong(_inChecksumFeed); _outMsg.WriteByte(svc_EOF); }
bool udtBaseParser::ParseCommandString() { s32 commandStringLength = 0; const s32 commandSequence = _inMsg.ReadLong(); const char* const commandStringTemp = _inMsg.ReadString(commandStringLength); // Do we have it already? if(_inServerCommandSequence >= commandSequence) { // Yes, don't bother processing it. return true; } // Copy the string to some temporary location. udtVMScopedStackAllocator scopedTempAllocator(_tempAllocator); udtString commandString = udtString::NewClone(_tempAllocator, commandStringTemp, (u32)commandStringLength); // We haven't, so let's store the last sequence number received. _inServerCommandSequence = commandSequence; bool plugInSkipsThisCommand = false; tokenize: idTokenizer& tokenizer = _tokenizer; tokenizer.Tokenize(commandString.GetPtr()); const int tokenCount = tokenizer.GetArgCount(); const udtString commandName = (tokenCount > 0) ? tokenizer.GetArg(0) : udtString::NewEmptyConstant(); s32 csIndex = -1; bool isConfigString = false; if(tokenCount == 3 && udtString::Equals(commandName, "cs")) { if(StringParseInt(csIndex, tokenizer.GetArgString(1)) && csIndex >= 0 && csIndex < (s32)UDT_COUNT_OF(_inConfigStrings)) { isConfigString = true; const char* const csStringTemp = tokenizer.GetArgString(2); u32 csStringLength = (u32)strlen(csStringTemp); udtConfigStringConversion outCs; _protocolConverter->ConvertConfigString(outCs, _tempAllocator, csIndex, csStringTemp, csStringLength); if(outCs.NewString || outCs.Index != csIndex) { commandString = udtString::NewEmpty(_privateTempAllocator, 2 * BIG_INFO_STRING); sprintf(commandString.GetWritePtr(), "cs %d \"%s\"", outCs.Index, outCs.String.GetPtr()); commandStringLength = (s32)strlen(commandString.GetPtr()); csStringLength = outCs.String.GetLength(); } // Copy the config string to some safe location. _inConfigStrings[csIndex] = udtString::NewClone(_configStringAllocator, csStringTemp, csStringLength); } } else if(tokenCount == 3 && udtString::Equals(commandName, "bcs0")) { // Start a new big config string. sprintf(_inBigConfigString, "cs %s \"%s", tokenizer.GetArgString(1), tokenizer.GetArgString(2)); plugInSkipsThisCommand = true; } else if(tokenCount == 3 && udtString::Equals(commandName, "bcs1")) { // Append to current big config string. strcat(_inBigConfigString, tokenizer.GetArgString(2)); plugInSkipsThisCommand = true; } else if(tokenCount == 3 && udtString::Equals(commandName, "bcs2")) { // Append to current big config string and finalize it. strcat(_inBigConfigString, tokenizer.GetArgString(2)); strcat(_inBigConfigString, "\""); commandString = udtString::NewConstRef(_inBigConfigString); commandStringLength = (s32)strlen(_inBigConfigString); goto tokenize; } if(EnablePlugIns && !PlugIns.IsEmpty() && !plugInSkipsThisCommand) { udtCommandCallbackArg info; info.CommandSequence = commandSequence; info.String = commandString.GetPtr(); info.StringLength = commandStringLength; info.ConfigStringIndex = csIndex; info.IsConfigString = isConfigString; info.IsEmptyConfigString = isConfigString ? udtString::IsNullOrEmpty(tokenizer.GetArg(2)) : false; for(u32 i = 0, count = PlugIns.GetSize(); i < count; ++i) { PlugIns[i]->ProcessCommandMessage(info, *this); } } if(ShouldWriteMessage()) { if(csIndex >= 0 && commandStringLength >= MAX_STRING_CHARS) { WriteBigConfigStringCommand(tokenizer.GetArg(1), tokenizer.GetArg(2)); } else if(commandStringLength < MAX_STRING_CHARS) { _outMsg.WriteByte(svc_serverCommand); _outMsg.WriteLong(_outServerCommandSequence); _outMsg.WriteString(commandString.GetPtr(), commandStringLength); ++_outServerCommandSequence; } else { _outMsg.WriteByte(svc_nop); } } _privateTempAllocator.Clear(); return true; }