private void RefreshThread() { using (TimedUdpClient client = new TimedUdpClient()) { IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0); mainWindowInterface.SetServerProperty(this, "Title", "Connecting to Server"); mainWindowInterface.SetServerProperty(this, "State", StateEnum.Querying); // reset properties mainWindowInterface.SetServerProperty(this, "Ping", null); mainWindowInterface.SetServerProperty(this, "GameName", null); mainWindowInterface.SetServerProperty(this, "Map", null); mainWindowInterface.SetServerProperty(this, "NumSpectators", null); mainWindowInterface.SetServerProperty(this, "MaxSpectators", null); mainWindowInterface.SetServerProperty(this, "PasswordProtected", null); try { DateTime connectTime = DateTime.Now; // attempt to connect Int32 colonIndex = address.IndexOf(':'); // what's the timeout??? client.Connect(address.Remove(colonIndex), Convert.ToInt32(address.Substring(colonIndex + 1))); // update title as "connected" mainWindowInterface.SetServerProperty(this, "Title", "Connected"); if (abortRefresh) { Common.AbortThread(Thread); } // send A2S_INFO // -1 (int), A2S_INFO, "Source Engine Query" (string) DateTime sendTime = DateTime.Now; client.Send(new Byte[] { 0xFF, 0xFF, 0xFF, 0xFF, A2S_INFO, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x00 }); // receive A2S_INFO reply Byte[] infoReply = client.Receive(ref ipEndPoint); if (infoReply == null) { throw new ApplicationException("Server not responding."); } // calculate ping TimeSpan ping = DateTime.Now - connectTime; mainWindowInterface.SetServerProperty(this, "Ping", ping.Milliseconds); // parse A2S_INFO reply ParseInfoQueryReply(infoReply); if (client.Available > 0) // fuuuuuuckkkk yoooooouuu Vaaaaaaalllvvvveeee { client.Receive(ref ipEndPoint); } // send A2S_SERVERQUERY_GETCHALLENGE // -1 (int), A2S_SERVERQUERY_GETCHALLENGE /*client.Send(new Byte[] { 0xFF, 0xFF, 0xFF, 0xFF, A2S_SERVERQUERY_GETCHALLENGE }); // receive and parse A2S_SERVERQUERY_GETCHALLENGE reply Byte[] serverQueryGetChallengeReply = client.Receive(ref ipEndPoint); if (serverQueryGetChallengeReply == null) { throw new ApplicationException("Server ignored challenge."); }*/ //Int32 challengeNumber = ParseServerQueryGetChallengeReply(serverQueryGetChallengeReply); Int32 challengeNumber = -1; while (true) { if (abortRefresh) { Common.AbortThread(Thread); } // send A2S_PLAYER BitWriter bitWriter = new BitWriter(); bitWriter.WriteInt32(-1); bitWriter.WriteByte(A2S_PLAYER); bitWriter.WriteInt32(challengeNumber); client.Send(bitWriter.Data); // receive and parse A2S_PLAYER reply Byte[] playerReply = client.Receive(ref ipEndPoint); if (playerReply == null) { if (challengeNumber != -1) { // oh, it's a sourcetv server and valve are too incompetent to implement s2c_player break; } throw new ApplicationException("Player query failed."); } // check for a challenge number (source servers) BitBuffer bitBuffer = new BitBuffer(playerReply); if (bitBuffer.ReadInt32() != -1) { throw new ApplicationException("Bad A2S_PLAYER reply"); } Byte type = bitBuffer.ReadByte(); if (type == S2C_CHALLENGE) { challengeNumber = bitBuffer.ReadInt32(); continue; } else if (type == S2C_PLAYER) { ParsePlayerQueryReply(bitBuffer); } else { throw new ApplicationException(String.Format("Bad A2S_PLAYER type: {0}", type)); } break; } mainWindowInterface.SetServerProperty(this, "State", StateEnum.Succeeded); } catch (ThreadAbortException) { throw; } catch (Exception ex) { if (!abortRefresh) { mainWindowInterface.SetServerProperty(this, "Title", ex.Message); mainWindowInterface.SetServerProperty(this, "State", StateEnum.Failed); } } finally { client.Close(); } } }
private void CalculateProtocol15Hack(Int32 dataLength) { /* * Valve broke network protocol 15 by changing the protocol without incrementing the protocol number. * * The change adds an extra bit after each message ID, so the only thing that's guaranteed to read correctly with every protocol 15 demo now if the header. * * The first message in the first sigon frame is always svc_print or svc_serverinfo, so if either of those are invalid, odds are that the demo uses the changed protocol. * */ // read in data block Byte[] frameData = parser.Reader.ReadBytes(dataLength); BitBuffer bitBuffer = new BitBuffer(frameData); // parse messages SourceDemoParser.MessageId messageId = (SourceDemoParser.MessageId)bitBuffer.ReadUnsignedBits(5); if (messageId == SourceDemoParser.MessageId.SVC_Print) { String s = bitBuffer.ReadString(); if (s.Contains("Map") || s.Contains("Build") || s.Contains("Players")) { // Looks like a valid svc_print string. return; } Protocol15Hack = true; } else if (messageId == SourceDemoParser.MessageId.SVC_ServerInfo) { UInt32 networkProtocol = bitBuffer.ReadUnsignedBits(16); if (networkProtocol != this.networkProtocol) { // Should match header, must be invalid. Protocol15Hack = true; } } }
/// <summary> /// Parses S2C_PLAYER. /// </summary> /// <param name="data"></param> /// <param name="si"></param> private void ParsePlayerQueryReply(BitBuffer bitBuffer) { Int32 nPlayers = bitBuffer.ReadByte(); for (Int32 i = 0; i < nPlayers; i++) { bitBuffer.SeekBytes(1); // skip index Player player = new Player(); player.Name = bitBuffer.ReadString(); player.Score = bitBuffer.ReadInt32(); Single time = bitBuffer.ReadSingle(); player.Time = (time == -1 ? "BOT" : Common.DurationString(time)); mainWindowInterface.AddPlayerToServer(this, player); } }
/// <summary> /// Parses S2C_CHALLENGE and returns the challenge number. /// </summary> /// <param name="data"></param> /// <returns></returns> private Int32 ParseServerQueryGetChallengeReply(Byte[] data) { BitBuffer bitBuffer = new BitBuffer(data); if (bitBuffer.ReadInt32() != -1) { throw new ApplicationException("Bad A2S_SERVERQUERY_GETCHALLENGE reply"); } Byte type = bitBuffer.ReadByte(); if (type != S2C_CHALLENGE) { throw new ApplicationException(String.Format("Bad A2S_SERVERQUERY_GETCHALLENGE type: {0}", type)); } return bitBuffer.ReadInt32(); }
private void MessageClCorpse() { Byte length = parser.BitBuffer.ReadByte(); Int32 messageDataOffset = parser.BitBuffer.CurrentByte; if (demo.ConvertNetworkProtocol() && demo.Game != null) { // Get the message data and create a BitBuffer for it. byte[] messageData = parser.BitBuffer.ReadBytes(length); BitBuffer messageBitBuffer = new BitBuffer(messageData); // Have the game handler convert the message. demo.Game.ConvertClCorpseMessageCallback(demo.GameVersion, messageBitBuffer); // Remove the old message data and insert the new converted message data. parser.Seek(messageDataOffset, SeekOrigin.Begin); parser.BitBuffer.RemoveBytes(length); parser.BitBuffer.InsertBytes(messageBitBuffer.Data); } else { parser.Seek(length); } }
/// <summary> /// Parses S2C_INFO. /// </summary> /// <param name="data"></param> /// <param name="si"></param> private void ParseInfoQueryReply(Byte[] data) { BitBuffer bitBuffer = new BitBuffer(data); if (bitBuffer.ReadInt32() != -1) { throw new ApplicationException("Bad A2S_INFO reply"); } // read reply type Byte type = bitBuffer.ReadByte(); Boolean sourceEngine; if (type == S2C_INFO_SOURCE) { sourceEngine = true; } else if (type == S2C_INFO_GOLDSRC) { sourceEngine = false; } else { throw new ApplicationException(String.Format("Bad A2S_INFO type: {0}", type)); } mainWindowInterface.SetServerProperty(this, "SourceEngine", sourceEngine); // read the rest if (!sourceEngine) { bitBuffer.ReadString(); //si.Address = bitBuffer.ReadString(); // resolved hostname } else { bitBuffer.SeekBytes(1); // network version } mainWindowInterface.SetServerProperty(this, "Title", bitBuffer.ReadString()); // server name mainWindowInterface.SetServerProperty(this, "Map", bitBuffer.ReadString()); // map String gameFolder = bitBuffer.ReadString(); mainWindowInterface.SetServerProperty(this, "GameFolder", gameFolder); // game folder String gameName = bitBuffer.ReadString(); // game name if (sourceEngine) { bitBuffer.SeekBytes(2); // app id } mainWindowInterface.SetServerProperty(this, "NumSpectators", bitBuffer.ReadByte()); // num spectators mainWindowInterface.SetServerProperty(this, "MaxSpectators", bitBuffer.ReadByte()); // max spectators bitBuffer.SeekBytes(1); // goldsrc: network version, source: num bots bitBuffer.SeekBytes(1); // dedicated bitBuffer.SeekBytes(1); // os mainWindowInterface.SetServerProperty(this, "PasswordProtected", (bitBuffer.ReadByte() == 1)); // determine game name Game game = GameManager.Find((sourceEngine ? Game.Engines.Source : Game.Engines.HalfLife), gameFolder); if (game == null) { mainWindowInterface.SetServerProperty(this, "GameName", gameName); } else { mainWindowInterface.SetServerProperty(this, "GameName", game.Name); } }
private UInt32 ParseUnsignedInt(BitBuffer bitBuffer, Entry e) { return bitBuffer.ReadUnsignedBits((Int32)e.nBits) / (UInt32)e.Divisor; }
public void ProcessFirstGameDataFrame(ref Byte[] frameData) { // A svc_director message preceeds svc_spawnbaseline in newer HLTV demos. The director message is used to initialise and reset the HUD. Since old HLTV demos omit this message, it can be added here (and set the perspective to first-person too). if (demo.Perspective != Demo.Perspectives.Hltv || haveParsedDirectorMessage) { return; } const Byte DRC_CMD_START = 1; const Byte DRC_CMD_MODE = 3; const Byte OBS_IN_EYE = 4; // Create a svc_director message to initialise the HUD. BitWriter directorMessage = new BitWriter(); directorMessage.WriteByte((Byte)HalfLifeDemoParser.MessageId.svc_director); directorMessage.WriteByte(1); // length directorMessage.WriteByte(DRC_CMD_START); // Create a svc_director message to set the perspective to first-person. directorMessage.WriteByte((Byte)HalfLifeDemoParser.MessageId.svc_director); directorMessage.WriteByte(2); // length directorMessage.WriteByte(DRC_CMD_MODE); directorMessage.WriteByte(OBS_IN_EYE); // Insert the new messages. BitBuffer bitBuffer = new BitBuffer(frameData); bitBuffer.InsertBytes(directorMessage.Data); frameData = bitBuffer.Data; }
private Object ParseEntry(BitBuffer bitBuffer, Entry e) { Boolean signed = ((e.Flags & EntryFlags.Signed) != 0); if ((e.Flags & EntryFlags.Byte) != 0) { if (signed) { return (SByte)ParseInt(bitBuffer, e); } else { return (Byte)ParseUnsignedInt(bitBuffer, e); } } if ((e.Flags & EntryFlags.Short) != 0) { if (signed) { return (Int16)ParseInt(bitBuffer, e); } else { return (UInt16)ParseUnsignedInt(bitBuffer, e); } } if ((e.Flags & EntryFlags.Integer) != 0) { if (signed) { return (Int32)ParseInt(bitBuffer, e); } else { return (UInt32)ParseUnsignedInt(bitBuffer, e); } } if ((e.Flags & EntryFlags.Float) != 0 || (e.Flags & EntryFlags.TimeWindow8) != 0 || (e.Flags & EntryFlags.TimeWindowBig) != 0) { Boolean negative = false; Int32 bitsToRead = (Int32)e.nBits; if (signed) { negative = bitBuffer.ReadBoolean(); bitsToRead--; } return (Single)bitBuffer.ReadUnsignedBits(bitsToRead) / e.Divisor * (negative ? -1.0f : 1.0f); } if ((e.Flags & EntryFlags.Angle) != 0) { return (Single)(bitBuffer.ReadUnsignedBits((Int32)e.nBits) * (360.0f / (Single)(1 << (Int32)e.nBits))); } if ((e.Flags & EntryFlags.String) != 0) { return bitBuffer.ReadString(); } throw new ApplicationException(String.Format("Unknown delta entry type {0}.", e.Flags)); }
private Int32 ParseInt(BitBuffer bitBuffer, Entry e) { Boolean negative = bitBuffer.ReadBoolean(); return (Int32)bitBuffer.ReadUnsignedBits((Int32)e.nBits - 1) / (Int32)e.Divisor * (negative ? -1 : 1); }
public void ReadDelta(BitBuffer bitBuffer, HalfLifeDelta delta, out Byte[] bitmaskBytes) { // read bitmask UInt32 nBitmaskBytes = bitBuffer.ReadUnsignedBits(3); // TODO: error check nBitmaskBytes against nEntries if (nBitmaskBytes == 0) { bitmaskBytes = null; return; } bitmaskBytes = new Byte[nBitmaskBytes]; for (Int32 i = 0; i < nBitmaskBytes; i++) { bitmaskBytes[i] = bitBuffer.ReadByte(); } for (Int32 i = 0; i < nBitmaskBytes; i++) { for (Int32 j = 0; j < 8; j++) { Int32 index = j + i * 8; if (index == entryList.Count) { return; } if ((bitmaskBytes[i] & (1 << j)) != 0) { Object value = ParseEntry(bitBuffer, entryList[index]); if (delta != null) { delta.SetEntryValue(index, value); } } } } }
public void ReadDelta(BitBuffer bitBuffer, HalfLifeDelta delta) { Byte[] bitmaskBytes; ReadDelta(bitBuffer, delta, out bitmaskBytes); }
public void ParseGameDataMessages(Byte[] frameData, Function <Byte, Byte> userMessageCallback) { Int64 gameDataStartOffset = fileStream.Position - frameData.Length; // read game data frame into memory bitBuffer = new BitBuffer(frameData); readingGameData = true; try { BeginMessageLog(gameDataStartOffset, frameData); // start parsing messages while (true) { Int32 messageFrameOffset = bitBuffer.CurrentByte; Byte messageId = bitBuffer.ReadByte(); String messageName = Enum.GetName(typeof(MessageId), messageId); if (messageName == null) // a user message, presumably { messageName = FindMessageIdString(messageId); } LogMessage(messageId, messageName, messageFrameOffset); MessageHandler messageHandler = FindMessageHandler(messageId); // Handle the conversion of user message id's. // Used by demo writing to convert to the current network protocol. if (messageId > 64 && userMessageCallback != null) { Byte newMessageId = userMessageCallback(messageId); if (newMessageId != messageId) { // write the new id to the bitbuffer bitBuffer.SeekBytes(-1); bitBuffer.RemoveBytes(1); bitBuffer.InsertBytes(new Byte[] { newMessageId }); } } // unknown message if (messageHandler == null) { throw new ApplicationException(String.Format("Cannot find message handler for message id \"[{0}] {1}\"", messageId, messageName)); } // callback takes priority over length if (messageHandler.Callback != null) { messageHandler.Callback(); } else if (messageHandler.Length != -1) { Seek(messageHandler.Length); } else { // user messages if (messageId >= 64) { // All non-engine user messages start with a byte that is the number of bytes in the message remaining. Byte length = bitBuffer.ReadByte(); Seek(length); } else { throw new ApplicationException(String.Format("Unknown message id \"{0}\"", messageId)); } } // Check if we've reached the end of the frame, or if any of the messages have called SkipGameDataFrame (readingGameData will be false). if (bitBuffer.CurrentByte == bitBuffer.Length || !readingGameData) { break; } } } finally { readingGameData = false; } }
// Counter-Strike and DOD specific. public virtual void ConvertClCorpseMessageCallback(Int32 gameVersion, BitBuffer bitBuffer) { }