diff --git a/CSGSI.sln b/CSGSI.sln
index 6a48b06..c1e13ce 100644
--- a/CSGSI.sln
+++ b/CSGSI.sln
@@ -1,6 +1,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio 14
+VisualStudioVersion = 14.0.24720.0
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSGSI", "CSGSI\CSGSI.csproj", "{8D55804C-2CF1-4A65-9C3E-8E41CD809535}"
EndProject
Global
diff --git a/CSGSI/CSGSI.csproj b/CSGSI/CSGSI.csproj
index 5719041..5a4deaa 100644
--- a/CSGSI/CSGSI.csproj
+++ b/CSGSI/CSGSI.csproj
@@ -45,10 +45,19 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/CSGSI/GSIListener.cs b/CSGSI/GSIListener.cs
deleted file mode 100644
index 98721cc..0000000
--- a/CSGSI/GSIListener.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using System.Threading;
-using System.Security.Principal;
-
-namespace CSGSI
-{
- ///
- /// A class that listens for HTTP POST requests and keeps track of previous game states
- ///
- public static class GSIListener
- {
- private const int MAX_GAMESTATES = 10;
-
- private static AutoResetEvent waitForConnection = new AutoResetEvent(false);
- private static List GameStates = new List();
-
- ///
- /// The most recently received GameState object
- ///
- public static GameState CurrentGameState
- {
- get
- {
- if (GameStates.Count > 0)
- return GameStates[GameStates.Count - 1];
- else
- return null;
- }
- }
-
- private static int m_Port;
- private static bool m_Running = false;
- private static HttpListener listener;
-
- ///
- /// Gets the port that is currently listening
- ///
- public static int Port { get { return m_Port; } }
-
- ///
- /// Gets a bool determining if the listening process is running
- ///
- public static bool Running { get { return m_Running; } }
-
- ///
- /// Occurs after a new GameState has been received
- ///
- public static event EventHandler NewGameState = delegate { };
-
- ///
- /// Starts listening for HTTP POST requests on the specified port
- /// !!! Fails if the application is started without administrator privileges !!!
- ///
- /// The port to listen on
- /// Returns true if the listener could be started, false otherwise
- public static bool Start(int port)
- {
- if (!m_Running && UacHelper.IsProcessElevated)
- {
- m_Port = port;
- listener = new HttpListener();
- listener.Prefixes.Add("http://127.0.0.1:" + port + "/");
- Thread listenerThread = new Thread(new ThreadStart(Run));
- m_Running = true;
- listenerThread.Start();
- return true;
- }
- return false;
- }
-
- ///
- /// Stops listening for HTTP POST requests
- ///
- public static void Stop()
- {
- m_Running = false;
- }
-
- private static void Run()
- {
- try
- {
- listener.Start();
- }
- catch (HttpListenerException)
- {
- m_Running = false;
- return;
- }
- while (m_Running)
- {
- listener.BeginGetContext(ReceiveGameState, listener);
- waitForConnection.WaitOne();
- waitForConnection.Reset();
- }
- listener.Stop();
- }
-
- private static void ReceiveGameState(IAsyncResult result)
- {
- HttpListenerContext context = listener.EndGetContext(result);
- HttpListenerRequest request = context.Request;
- string JSON;
-
- waitForConnection.Set();
-
- using (Stream inputStream = request.InputStream)
- {
- using (StreamReader sr = new StreamReader(inputStream))
- {
- JSON = sr.ReadToEnd();
- }
- }
- using (HttpListenerResponse response = context.Response)
- {
- response.StatusCode = (int)HttpStatusCode.OK;
- response.StatusDescription = "OK";
- response.Close();
- }
-
- GameState gs = new GameState(JSON);
- GameStates.Add(gs);
- NewGameState(gs, EventArgs.Empty);
-
- while (GameStates.Count > MAX_GAMESTATES)
- GameStates.RemoveAt(0);
- }
- }
-}
diff --git a/CSGSI/GameState.cs b/CSGSI/GameState.cs
index 6045225..0895652 100644
--- a/CSGSI/GameState.cs
+++ b/CSGSI/GameState.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using CSGSI.Nodes;
namespace CSGSI
{
///
@@ -12,86 +13,132 @@ namespace CSGSI
///
public class GameState
{
- private JObject m_Data;
+ private JObject _Data;
- private GameStateNode m_Provider;
- private GameStateNode m_Map;
- private GameStateNode m_Round;
- private GameStateNode m_Player;
- private GameStateNode m_Auth;
- private GameStateNode m_Added;
- private GameStateNode m_Previously;
+ private ProviderNode _Provider;
+ private MapNode _Map;
+ private RoundNode _Round;
+ private PlayerNode _Player;
+ private AllPlayersNode _AllPlayers;
+ private GameState _Previously;
+ private GameState _Added;
+ private AuthNode _Auth;
- ///
- /// The "provider" subnode
- ///
- public GameStateNode Provider { get { return m_Provider; } }
+ public ProviderNode Provider
+ {
+ get
+ {
+ if (_Provider == null)
+ {
+ _Provider = new ProviderNode(_Data["provider"]?.ToString() ?? "");
+ }
- ///
- /// The "map" subnode
- ///
- public GameStateNode Map { get { return m_Map; } }
+ return _Provider;
+ }
+ }
+ public MapNode Map
+ {
+ get
+ {
+ if (_Map == null)
+ {
+ _Map = new MapNode(_Data["map"]?.ToString() ?? "");
+ }
- ///
- /// The "round" subnode
- ///
- public GameStateNode Round { get { return m_Round; } }
+ return _Map;
+ }
+ }
+ public RoundNode Round
+ {
+ get
+ {
+ if (_Round == null)
+ {
+ _Round = new RoundNode(_Data["round"]?.ToString() ?? "");
+ }
- ///
- /// The "player" subnode
- ///
- public GameStateNode Player { get { return m_Player; } }
+ return _Round;
+ }
+ }
+ public PlayerNode Player
+ {
+ get
+ {
+ if (_Player == null)
+ {
+ _Player = new PlayerNode(_Data["player"]?.ToString() ?? "");
+ }
- ///
- /// The "auth" subnode
- ///
- public GameStateNode Auth { get { return m_Auth; } }
+ return _Player;
+ }
+ }
+ public AllPlayersNode AllPlayers
+ {
+ get
+ {
+ if (_AllPlayers == null)
+ {
+ _AllPlayers = new AllPlayersNode(_Data["allplayers"]?.ToString() ?? "");
+ }
- ///
- /// The "added" subnode
- ///
- public GameStateNode Added { get { return m_Added; } }
+ return _AllPlayers;
+ }
+ }
+ public GameState Previously
+ {
+ get
+ {
+ if (_Previously == null)
+ {
+ _Previously = new GameState(_Data["previously"]?.ToString() ?? "");
+ }
- ///
- /// The "previously" subnode
- ///
- public GameStateNode Previously { get { return m_Previously; } }
+ return _Previously;
+ }
+ }
+ public GameState Added
+ {
+ get
+ {
+ if (_Added == null)
+ {
+ _Added = new GameState(_Data["added"]?.ToString() ?? "");
+ }
+ return _Added;
+ }
+ }
+ public AuthNode Auth
+ {
+ get
+ {
+ if(_Auth == null)
+ {
+ _Auth = new AuthNode(_Data["auth"]?.ToString() ?? "");
+ }
+
+ return _Auth;
+ }
+ }
- private string m_JSON;
///
/// The JSON string that was used to generate this object
///
- public string JSON { get { return m_JSON; } }
-
+ public readonly string JSON;
+
///
/// Initialises a new GameState object using a JSON string
///
///
public GameState(string JSONstring)
{
- m_JSON = JSONstring;
+ if(JSONstring.Equals(""))
+ {
+ JSONstring = "{}";
+ }
- if (!JSONstring.Equals(""))
- m_Data = JObject.Parse(JSONstring);
-
- m_Provider = (HasRootNode("provider") ? new GameStateNode(m_Data["provider"]) : GameStateNode.Empty());
- m_Map = (HasRootNode("map") ? new GameStateNode(m_Data["map"]) : GameStateNode.Empty());
- m_Round = (HasRootNode("round") ? new GameStateNode(m_Data["round"]) : GameStateNode.Empty());
- m_Player = (HasRootNode("player") ? new GameStateNode(m_Data["player"]) : GameStateNode.Empty());
- m_Auth = (HasRootNode("auth") ? new GameStateNode(m_Data["auth"]) : GameStateNode.Empty());
- m_Added = (HasRootNode("added") ? new GameStateNode(m_Data["added"]) : GameStateNode.Empty());
- m_Previously = (HasRootNode("previously") ? new GameStateNode(m_Data["previously"]) : GameStateNode.Empty());
- }
-
- ///
- /// Determines if the specified node exists in this GameState object
- ///
- ///
- /// Returns true if the specified node exists, false otherwise
- public bool HasRootNode(string rootnode)
- {
- return (m_Data != null && m_Data[rootnode] != null);
+ JSON = JSONstring;
+ _Data = JObject.Parse(JSONstring);
}
}
}
diff --git a/CSGSI/GameStateListener.cs b/CSGSI/GameStateListener.cs
new file mode 100644
index 0000000..cc50d24
--- /dev/null
+++ b/CSGSI/GameStateListener.cs
@@ -0,0 +1,151 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Threading;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+
+namespace CSGSI
+{
+ public delegate void NewGameStateHandler(GameState gs);
+
+ ///
+ /// A class that listens for HTTP POST requests
+ ///
+ public class GameStateListener
+ {
+ private AutoResetEvent waitForConnection = new AutoResetEvent(false);
+ private GameState m_CurrentGameState;
+ private int m_Port;
+ private bool m_Running = false;
+ private HttpListener m_Listener;
+
+ ///
+ /// The most recently received GameState
+ ///
+ public GameState CurrentGameState
+ {
+ get
+ {
+ return m_CurrentGameState;
+ }
+ }
+
+ ///
+ /// Gets the port that this GameStateListener instance is listening to
+ ///
+ public int Port { get { return m_Port; } }
+
+ ///
+ /// Gets a value indicating if the listening process is running
+ ///
+ public bool Running { get { return m_Running; } }
+
+ ///
+ /// Occurs after a new GameState has been received
+ ///
+ public event NewGameStateHandler NewGameState = delegate { };
+
+ ///
+ /// A GameStateListener that listens for connections to http://localhost:<Port>/
+ ///
+ ///
+ public GameStateListener(int Port)
+ {
+ m_Port = Port;
+ m_Listener = new HttpListener();
+ m_Listener.Prefixes.Add("http://localhost:" + Port + "/");
+ }
+
+ ///
+ /// A GameStateListener that listens for connections to the specified URI
+ ///
+ /// The URI to listen to
+ public GameStateListener(string URI)
+ {
+ if (!URI.EndsWith("/"))
+ URI += "/";
+
+ Regex URIPattern = new Regex("^https?:\\/\\/.+:([0-9]*)\\/$", RegexOptions.IgnoreCase);
+ Match PortMatch = URIPattern.Match(URI);
+ if (!PortMatch.Success)
+ {
+ throw new ArgumentException("Not a valid URI: " + URI);
+ }
+ m_Port = Convert.ToInt32(PortMatch.Groups[1].Value);
+
+ m_Listener = new HttpListener();
+ m_Listener.Prefixes.Add(URI);
+ }
+
+ ///
+ /// Starts listening for HTTP POST requests on the specified port
+ ///
+ /// The port to listen on
+ /// Returns true on success
+ public bool Start()
+ {
+ if (!m_Running)
+ {
+ Thread m_ListenerThread = new Thread(new ThreadStart(Run));
+ try
+ {
+ m_Listener.Start();
+ }
+ catch (HttpListenerException)
+ {
+ return false;
+ }
+ m_Running = true;
+ m_ListenerThread.Start();
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Stops listening for HTTP POST requests
+ ///
+ public void Stop()
+ {
+ m_Running = false;
+ }
+
+ private void Run()
+ {
+ while (m_Running)
+ {
+ m_Listener.BeginGetContext(ReceiveGameState, m_Listener);
+ waitForConnection.WaitOne();
+ waitForConnection.Reset();
+ }
+ m_Listener.Stop();
+ }
+
+ private void ReceiveGameState(IAsyncResult result)
+ {
+ HttpListenerContext context = m_Listener.EndGetContext(result);
+ HttpListenerRequest request = context.Request;
+ string JSON;
+
+ waitForConnection.Set();
+
+ using (Stream inputStream = request.InputStream)
+ {
+ using (StreamReader sr = new StreamReader(inputStream))
+ {
+ JSON = sr.ReadToEnd();
+ }
+ }
+ using (HttpListenerResponse response = context.Response)
+ {
+ response.StatusCode = (int)HttpStatusCode.OK;
+ response.StatusDescription = "OK";
+ response.Close();
+ }
+ m_CurrentGameState = new GameState(JSON);
+ NewGameState(m_CurrentGameState);
+ }
+ }
+}
diff --git a/CSGSI/GameStateNode.cs b/CSGSI/GameStateNode.cs
deleted file mode 100644
index 433bc5d..0000000
--- a/CSGSI/GameStateNode.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Newtonsoft.Json.Linq;
-
-namespace CSGSI
-{
- ///
- /// A sub node of a GameState object
- ///
- public class GameStateNode
- {
- private JToken m_Data;
-
- private GameStateNode()
- {
- m_Data = JToken.Parse("{}");
- }
-
- ///
- /// Initializes a new GameStateNode using a JToken object
- ///
- ///
- public GameStateNode(JToken node)
- {
- m_Data = node;
- }
-
- ///
- /// Get the value of a specific subnode of this GameStateNode
- ///
- /// The name of the subnode to get the value of
- /// The string value of the specified subnode
- public string GetValue(string node)
- {
- if (m_Data[node] == null)
- return "";
-
- return m_Data[node].ToString();
- }
-
- ///
- /// Get a specific subnode as a new GameStateNode
- ///
- /// The name of the subnode
- /// A new GameStateNode object containing the subnode
- public GameStateNode GetNode(string node)
- {
- if (m_Data[node] == null)
- return GameStateNode.Empty();
-
- return new GameStateNode(m_Data[node]);
- }
-
- ///
- /// An empty GameStateNode to substitute for a null value
- ///
- ///
- public static GameStateNode Empty()
- {
- return new GameStateNode();
- }
-
- ///
- /// Get a specific subnode as a new GameStateNode
- ///
- /// The name of the subnode to get the value of
- /// A new GameStateNode object containing the subnode
- public GameStateNode this[string node]
- {
- get { return GetNode(node); }
- }
- }
-}
diff --git a/CSGSI/Nodes/AllPlayersNode.cs b/CSGSI/Nodes/AllPlayersNode.cs
new file mode 100644
index 0000000..6b0d048
--- /dev/null
+++ b/CSGSI/Nodes/AllPlayersNode.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json.Linq;
+
+namespace CSGSI.Nodes
+{
+ public class AllPlayersNode : NodeBase
+ {
+ private readonly List _Players = new List();
+
+ public PlayerNode GetByName(string Name)
+ {
+ PlayerNode pn = _Players.Find(x => x.Name == Name);
+ if (pn != null)
+ return pn;
+
+ return new PlayerNode("");
+ }
+
+ public PlayerNode GetBySteamID(string SteamID)
+ {
+ PlayerNode pn = _Players.Find(x => x.SteamID == SteamID);
+ if (pn != null)
+ return pn;
+
+ return new PlayerNode("");
+ }
+
+ public int Count { get { return _Players.Count; } }
+
+ internal AllPlayersNode(string JSON)
+ : base(JSON)
+ {
+ foreach (JToken jt in m_Data.Children())
+ {
+ PlayerNode pn = new PlayerNode(jt.First.ToString());
+ pn._SteamID = jt.Value()?.Name ?? "";
+ _Players.Add(pn);
+ }
+ }
+
+ ///
+ /// Gets the player with index <index>
+ ///
+ ///
+ ///
+ public PlayerNode this[int index]
+ {
+ get
+ {
+ if (index > _Players.Count - 1)
+ {
+ return new PlayerNode("");
+ }
+
+ return _Players[index];
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return _Players.GetEnumerator();
+ }
+ }
+}
diff --git a/CSGSI/Nodes/AuthNode.cs b/CSGSI/Nodes/AuthNode.cs
new file mode 100644
index 0000000..614d11f
--- /dev/null
+++ b/CSGSI/Nodes/AuthNode.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CSGSI.Nodes
+{
+ public class AuthNode : NodeBase
+ {
+ public readonly string Token;
+
+ internal AuthNode(string JSON)
+ : base(JSON)
+ {
+ Token = GetString("token");
+ }
+
+ }
+}
diff --git a/CSGSI/Nodes/MapNode.cs b/CSGSI/Nodes/MapNode.cs
new file mode 100644
index 0000000..0a843f3
--- /dev/null
+++ b/CSGSI/Nodes/MapNode.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace CSGSI.Nodes
+{
+ public class MapNode : NodeBase
+ {
+ public readonly MapMode Mode;
+ public readonly string Name;
+ public readonly MapPhase Phase;
+ public readonly int Round;
+ public readonly int TeamCT;
+ public readonly int TeamT;
+
+ internal MapNode(string JSON)
+ : base(JSON)
+ {
+ Mode = GetEnum("mode");
+ Phase = GetEnum("phase");
+ Round = GetInt32("round");
+ }
+ }
+
+ public enum MapPhase
+ {
+ Undefined,
+ Warmup,
+ Live,
+ Intermission,
+ GameOver
+ }
+
+ public enum MapMode
+ {
+ Undefined,
+ Casual,
+ Competitive,
+ DeathMatch,
+ ///
+ /// Gun Game
+ ///
+ GunGameProgressive,
+ ///
+ /// Arms Race & Demolition
+ ///
+ GunGameTRBomb,
+ Custom
+ }
+}
diff --git a/CSGSI/Nodes/MatchStatsNode.cs b/CSGSI/Nodes/MatchStatsNode.cs
new file mode 100644
index 0000000..2d38f78
--- /dev/null
+++ b/CSGSI/Nodes/MatchStatsNode.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace CSGSI.Nodes
+{
+ public class MatchStatsNode : NodeBase
+ {
+ public readonly int Kills;
+ public readonly int Assists;
+ public readonly int Deaths;
+ public readonly int MVPs;
+ public readonly int Score;
+
+ internal MatchStatsNode(string JSON)
+ : base(JSON)
+ {
+ Kills = GetInt32("kills");
+ Assists = GetInt32("assists");
+ Deaths = GetInt32("deaths");
+ MVPs = GetInt32("mvps");
+ Score = GetInt32("score");
+ }
+ }
+}
diff --git a/CSGSI/Nodes/NodeBase.cs b/CSGSI/Nodes/NodeBase.cs
new file mode 100644
index 0000000..8ff571b
--- /dev/null
+++ b/CSGSI/Nodes/NodeBase.cs
@@ -0,0 +1,46 @@
+using System;
+using Newtonsoft.Json.Linq;
+
+namespace CSGSI.Nodes
+{
+ public class NodeBase
+ {
+ protected string m_JSON;
+ protected JObject m_Data;
+
+ public string JSON
+ {
+ get { return m_JSON; }
+ }
+
+ internal NodeBase(string JSON)
+ {
+ if (JSON.Equals(""))
+ {
+ JSON = "{}";
+ }
+ m_Data = JObject.Parse(JSON);
+ m_JSON = JSON;
+ }
+
+ internal string GetString(string Name)
+ {
+ return m_Data?[Name]?.ToString() ?? "";
+ }
+
+ internal int GetInt32(string Name)
+ {
+ return Convert.ToInt32(m_Data[Name]?.ToString() ?? "-1");
+ }
+
+ internal T GetEnum(string Name)
+ {
+ return (T)Enum.Parse(typeof(T), (m_Data[Name]?.ToString().Replace(" ", String.Empty) ?? "Undefined"), true);
+ }
+
+ internal bool GetBool(string Name)
+ {
+ return m_Data?[Name]?.ToObject() ?? false;
+ }
+ }
+}
diff --git a/CSGSI/Nodes/PlayerNode.cs b/CSGSI/Nodes/PlayerNode.cs
new file mode 100644
index 0000000..bfc753e
--- /dev/null
+++ b/CSGSI/Nodes/PlayerNode.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CSGSI.Nodes
+{
+ public class PlayerNode : NodeBase
+ {
+ internal string _SteamID;
+ public string SteamID { get { return _SteamID; } }
+ public readonly string Name;
+ public readonly string Team;
+ public readonly PlayerActivity Activity;
+ public readonly WeaponsNode Weapons;
+ public readonly MatchStatsNode MatchStats;
+ public readonly PlayerStateNode State;
+
+ internal PlayerNode(string JSON)
+ : base(JSON)
+ {
+ _SteamID = GetString("steamid");
+ Name = GetString("name");
+ Team = GetString("team");
+ State = new PlayerStateNode(m_Data?.SelectToken("state")?.ToString() ?? "{}");
+ Weapons = new WeaponsNode(m_Data?.SelectToken("weapons")?.ToString() ?? "{}");
+ MatchStats = new MatchStatsNode(m_Data?.SelectToken("match_stats")?.ToString() ?? "{}");
+ Activity = GetEnum("activity");
+ }
+ }
+
+ public enum PlayerActivity
+ {
+ Undefined,
+ Menu,
+ Playing,
+ ///
+ /// Console is open
+ ///
+ TextInput
+ }
+}
diff --git a/CSGSI/Nodes/PlayerStateNode.cs b/CSGSI/Nodes/PlayerStateNode.cs
new file mode 100644
index 0000000..be76b0a
--- /dev/null
+++ b/CSGSI/Nodes/PlayerStateNode.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace CSGSI.Nodes
+{
+ public class PlayerStateNode : NodeBase
+ {
+ public readonly int Health;
+ public readonly int Armor;
+ public readonly bool Helmet;
+ public readonly int Flashed;
+ public readonly int Smoked;
+ public readonly int Burning;
+ public readonly int Money;
+ public readonly int RoundKills;
+ public readonly int RoundKillHS;
+
+ internal PlayerStateNode(string JSON)
+ : base(JSON)
+ {
+ Health = GetInt32("health");
+ Armor = GetInt32("armor");
+ Helmet = GetBool("helmet");
+ Flashed = GetInt32("flashed");
+ Smoked = GetInt32("smoked");
+ Burning = GetInt32("burning");
+ Money = GetInt32("money");
+ RoundKills = GetInt32("round_kills");
+ RoundKillHS = GetInt32("round_killhs");
+ }
+ }
+}
diff --git a/CSGSI/Nodes/ProviderNode.cs b/CSGSI/Nodes/ProviderNode.cs
new file mode 100644
index 0000000..2f6416d
--- /dev/null
+++ b/CSGSI/Nodes/ProviderNode.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CSGSI.Nodes
+{
+ public class ProviderNode : NodeBase
+ {
+ public readonly string Name;
+ public readonly int AppID;
+ public readonly int Version;
+ public readonly string SteamID;
+ public readonly string TimeStamp;
+
+ internal ProviderNode(string JSON)
+ : base(JSON)
+ {
+ Name = GetString("name");
+ AppID = GetInt32("appid");
+ Version = GetInt32("version");
+ SteamID = GetString("steamid");
+ TimeStamp = GetString("timestamp");
+ }
+ }
+}
diff --git a/CSGSI/Nodes/RoundNode.cs b/CSGSI/Nodes/RoundNode.cs
new file mode 100644
index 0000000..9561327
--- /dev/null
+++ b/CSGSI/Nodes/RoundNode.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace CSGSI.Nodes
+{
+ public class RoundNode : NodeBase
+ {
+ public readonly RoundPhase Phase;
+ public readonly BombState Bomb;
+ public readonly RoundWinTeam WinTeam;
+
+ internal RoundNode(string JSON)
+ : base(JSON)
+ {
+ Phase = GetEnum("phase");
+ Bomb = GetEnum("bomb");
+ WinTeam = GetEnum("win_team");
+ }
+ }
+
+ public enum RoundPhase
+ {
+ Undefined,
+ Live,
+ Over,
+ FreezeTime
+ }
+
+ public enum BombState
+ {
+ Undefined,
+ Planted,
+ Exploded,
+ Defused
+ }
+
+ public enum RoundWinTeam
+ {
+ Undefined,
+ T,
+ CT
+ }
+}
diff --git a/CSGSI/Nodes/WeaponNode.cs b/CSGSI/Nodes/WeaponNode.cs
new file mode 100644
index 0000000..343fe51
--- /dev/null
+++ b/CSGSI/Nodes/WeaponNode.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CSGSI.Nodes
+{
+ public class WeaponNode : NodeBase
+ {
+ public readonly string Name;
+ public readonly string Paintkit;
+ public readonly WeaponType Type;
+ public readonly int AmmoClip;
+ public readonly int AmmoClipMax;
+ public readonly int AmmoReserve;
+ public readonly WeaponState State;
+
+ internal WeaponNode(string JSON)
+ : base(JSON)
+ {
+ Name = GetString("name");
+ Paintkit = GetString("paintkit");
+ Type = GetEnum("type");
+ AmmoClip = GetInt32("ammo_clip");
+ AmmoClipMax = GetInt32("ammo_clip_max");
+ AmmoReserve = GetInt32("ammo_clip_reserve");
+ State = GetEnum("state");
+ }
+ }
+
+ public enum WeaponType
+ {
+ Undefined,
+ Rifle,
+ SniperRifle,
+ SubmachineGun,
+ Shotgun,
+ MachineGun,
+ Pistol,
+ Knife,
+ Grenade,
+ C4
+ }
+
+ public enum WeaponState
+ {
+ Undefined,
+ Active,
+ Holstered,
+ Reloading
+ }
+}
diff --git a/CSGSI/Nodes/WeaponsNode.cs b/CSGSI/Nodes/WeaponsNode.cs
new file mode 100644
index 0000000..174abe8
--- /dev/null
+++ b/CSGSI/Nodes/WeaponsNode.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using Newtonsoft.Json.Linq;
+using System.Linq;
+
+using System;
+
+namespace CSGSI.Nodes
+{
+ public class WeaponsNode : NodeBase
+ {
+ private List _Weapons = new List();
+
+ public int Count { get { return _Weapons.Count; } }
+ public WeaponNode ActiveWeapon
+ {
+ get
+ {
+ foreach(WeaponNode w in _Weapons)
+ {
+ if (w.State == WeaponState.Active || w.State == WeaponState.Reloading)
+ return w;
+ }
+
+ return new WeaponNode("");
+ }
+ }
+
+ internal WeaponsNode(string JSON)
+ : base(JSON)
+ {
+ foreach(JToken jt in m_Data.Children())
+ {
+ _Weapons.Add(new WeaponNode(jt.First.ToString()));
+ }
+ }
+
+ ///
+ /// Gets the weapon with index <index>
+ ///
+ ///
+ ///
+ public WeaponNode this[int index]
+ {
+ get
+ {
+ if (index > _Weapons.Count - 1)
+ {
+ return new WeaponNode("");
+ }
+
+ return _Weapons[index];
+ }
+ }
+ }
+}
diff --git a/CSGSI/UacHelper.cs b/CSGSI/UacHelper.cs
deleted file mode 100644
index 9be9e60..0000000
--- a/CSGSI/UacHelper.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using Microsoft.Win32;
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Security.Principal;
-
-namespace CSGSI
-{
- //stolen in it's entirety from http://stackoverflow.com/a/4497572 :O
- //this class is necessary since HttpListener.Start() will throw an exception if the application isn't started with admin privileges
- internal static class UacHelper
- {
- private const string uacRegistryKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
- private const string uacRegistryValue = "EnableLUA";
-
- private static uint STANDARD_RIGHTS_READ = 0x00020000;
- private static uint TOKEN_QUERY = 0x0008;
- private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
-
- [DllImport("advapi32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
-
- [DllImport("advapi32.dll", SetLastError = true)]
- public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);
-
- public enum TOKEN_INFORMATION_CLASS
- {
- TokenUser = 1,
- TokenGroups,
- TokenPrivileges,
- TokenOwner,
- TokenPrimaryGroup,
- TokenDefaultDacl,
- TokenSource,
- TokenType,
- TokenImpersonationLevel,
- TokenStatistics,
- TokenRestrictedSids,
- TokenSessionId,
- TokenGroupsAndPrivileges,
- TokenSessionReference,
- TokenSandBoxInert,
- TokenAuditPolicy,
- TokenOrigin,
- TokenElevationType,
- TokenLinkedToken,
- TokenElevation,
- TokenHasRestrictions,
- TokenAccessInformation,
- TokenVirtualizationAllowed,
- TokenVirtualizationEnabled,
- TokenIntegrityLevel,
- TokenUIAccess,
- TokenMandatoryPolicy,
- TokenLogonSid,
- MaxTokenInfoClass
- }
-
- public enum TOKEN_ELEVATION_TYPE
- {
- TokenElevationTypeDefault = 1,
- TokenElevationTypeFull,
- TokenElevationTypeLimited
- }
-
- public static bool IsUacEnabled
- {
- get
- {
- RegistryKey uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
- bool result = uacKey.GetValue(uacRegistryValue).Equals(1);
- return result;
- }
- }
-
- public static bool IsProcessElevated
- {
- get
- {
- if (IsUacEnabled)
- {
- IntPtr tokenHandle;
- if (!OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_READ, out tokenHandle))
- {
- throw new ApplicationException("Could not get process token. Win32 Error Code: " + Marshal.GetLastWin32Error());
- }
-
- TOKEN_ELEVATION_TYPE elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
-
- int elevationResultSize = Marshal.SizeOf((int)elevationResult);
- uint returnedSize = 0;
- IntPtr elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
-
- bool success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType, elevationTypePtr, (uint)elevationResultSize, out returnedSize);
- if (success)
- {
- elevationResult = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(elevationTypePtr);
- bool isProcessAdmin = elevationResult == TOKEN_ELEVATION_TYPE.TokenElevationTypeFull;
- return isProcessAdmin;
- }
- else
- {
- throw new ApplicationException("Unable to determine the current elevation.");
- }
- }
- else
- {
- WindowsIdentity identity = WindowsIdentity.GetCurrent();
- WindowsPrincipal principal = new WindowsPrincipal(identity);
- bool result = principal.IsInRole(WindowsBuiltInRole.Administrator);
- return result;
- }
- }
- }
- }
-}
diff --git a/README.md b/README.md
index f084006..20ef11a 100644
--- a/README.md
+++ b/README.md
@@ -1,78 +1,83 @@
# CSGSI
A simple C# library to interface with Counter-Strike: Global Offensive *Game State Integration*
-# **!! Development ceased indefinitely !!**
-#### If you're looking for an actively developed alternative, see [master117's library](https://bitbucket.org/master117/csgogameobserversdk).
-
-
+## Table of Contents
+[What is Game State Integration](#whatis)
+[About CSGSI](#about)
+[Installation](#installation)
+[Usage](#usage)
+[Layout](#layout)
+[Null value handling](#nullvaluehandling)
+[Example program](#example)
+
+
+
## What is Game State Integration
-
+
[This wiki page by Valve](https://developer.valvesoftware.com/wiki/Counter-Strike:_Global_Offensive_Game_State_Integration) explains the concept of GSI.
+
+## About CSGSI
+
+This library provides means to listen to a specific port for the game clients HTTP POST request. Once a request is received, the game state is parsed and can then be analyzed.
+
+CSGSI uses Newtonsoft's [JSON.Net Framework](http://www.newtonsoft.com/json) to parse JSON.
+
+Once a `GameStateListener` instance is running, it continuously listens for incoming HTTP requests.
+Every time a request is received, it's JSON content is used to construct a new `GameState` object.
+The `GameState` class represents the entire game state as it was sent by the client.
+It also provides access to all rootnodes (see Usage).
+
+
## Installation
+
1. Get the [latest binaries](https://github.com/rakijah/CSGSI/releases/latest)
2. Get the [JSON Framework .dll by Newtonsoft](https://github.com/JamesNK/Newtonsoft.Json/releases)
3. Extract Newtonsoft.Json.dll from `Bin\Net45\Newtonsoft.Json.dll`
4. Add a reference to both CSGSI.dll and Newtonsoft.Json.dll in your project
-## About CSGSI
-
-This library provides means to listen to a specific port for the game clients HTTP POST request. Once a request is received, the game state is parsed and can then be analyzed to display the players' current condition, create bomb timers etc...
+
+## Usage
+
-CSGSI uses Newtonsoft's [JSON.Net Framework](http://www.newtonsoft.com/json) to parse JSON.
+Create a `GameStateListener` instance by providing a port or passing a specific URI:
-The main class `GSIListener` continuously listens for incoming HTTP requests.
-Once a request is made, it's JSON content is used to construct a new `GameState` object.
-The `GameState` class represents the entire game state as it was sent by the client.
-It also provides access to all rootnodes (see Usage).
-These rootnodes and every subnode of them are of type `GameStateNode`, which encapsulates the underlying JSON data.
-
-## Usage
+ GameStateListener gsl = new GameStateListener(3000); //http://localhost:3000/
+ GameStateListener gsl = new GameStateListener("http://127.0.0.1:81/");
-Use `GSIListener.Start(int port)` to start listening for HTTP POST requests from the game client. This method will return *false* if starting the listener fails.
+**Please note**: If your application needs to listen to a URI other than `http://localhost:*/` (for example `http://192.168.2.2:100/`), you need to ensure that it is run with administrator privileges.
+In this case, `http://127.0.0.1:*/` is **not** equivalent to `http://localhost:*/`.
-**!! This step fails if your application is not run with administrator privileges !!**
-**Listening to a URI/port is not possible with regular privileges**
+Use `GameStateListener.Start()` to start listening for HTTP POST requests from the game client. This method will return `false` if starting the listener fails (most likely due to insufficient privileges).
-The GSIListener class provides the `NewGameState` event that occurs after a new game state has been received:
+The GameStateListener class provides the `NewGameState` event that occurs after a new game state has been received:
```
-GSIListener.NewGameState += new EventHandler(OnNewGameState);
-...
+gsl.NewGameState += new EventHandler(OnNewGameState);
-private static void OnNewGameState(object sender, EventArgs e)
+private static void OnNewGameState(GameState gs)
{
- GameState gs = (GameState) sender; //the newest GameState
- //...
+ //gs contains the current GameState
}
```
-The GameState object provides access to these rootnodes that may be transmitted by the game:
+The `GameState` object provides access to these rootnodes that may be transmitted by the game:
* gs.Provider
* gs.Map
* gs.Round
* gs.Player
+* gs.AllPlayers
* gs.Auth
* gs.Added
* gs.Previously
-The rootnodes `allplayers_*` have not yet been implemented and `GameState` currently lacks a `GetRootNode` method (even though `HasRootNode` already exists).
-
-##### Example:
-
-```
-GameStateNode playerNode = gs.Player;
-```
-
-You can then access further subnodes by calling `playerNode.GetNode(string node);` (returns a `GameStateNode` object).
-You can retrieve a nodes value (as a string) by calling `playerNode.GetValue(string node);`
-
##### Example:
```
-playerNode.GetNode("state").GetValue("health");
+PlayerNode player = gs.Player;
+int Health = player.State.Health;
//100
-playerNode.GetNode("weapons").GetValue("weapon_0");
+string activeWep = player.Weapons.ActiveWeapon.JSON
//{
// "name": "weapon_knife",
// "paintkit": ...
@@ -80,43 +85,129 @@ playerNode.GetNode("weapons").GetValue("weapon_0");
//}
```
-If the node you are trying to access using `GetValue` does not exist, it returns an empty string (`""`).
+
+## Layout
+
+```
+GameState
+ .Provider
+ .Name
+ .AppID
+ .Version
+ .SteamID
+ .TimeStamp
+ .Map
+ .Mode
+ .Name
+ .Phase
+ .Round
+ .TeamCT
+ .TeamT
+ .Round
+ .Phase
+ .Bomb
+ .WinTeam
+ .Player
+ .SteamID
+ .Name
+ .Team
+ .Activity
+ .Weapons
+ .ActiveWeapon
+ []
+ .Name
+ .Paintkit
+ .Type
+ .AmmoClip
+ .AmmoClipMax
+ .AmmoReserve
+ .State
+ MatchStats
+ .Kills
+ .Assists
+ .Deaths
+ .MVPs
+ .Score
+ State
+ .Health
+ .Armor
+ .Helmet
+ .Flashed
+ .Smoked
+ .Burning
+ .Money
+ .RoundKills
+ .RoundKillHS
+ .AllPlayers
+ []
+ Player
+ .Previously
+ GameState
+ .Added
+ GameState
+ .Auth
+ .Token
+```
+
+##### Examples:
+`gs.Player.Weapons.ActiveWeapon.AmmoClip`
-## Example program
+`gs.AllPlayers[0].State.Health`
+
+
+## Null value handling
+
+
+In case the JSON did not contain the requested information, these values will be returned:
+
+Type|Default value
+----|-------------
+int|-1
+string| String.Empty
-This snippet will start listening for HTTP POST requests and print out the transmitted JSON data:
+All Enums have a value `enum.Undefined` that serves the same purpose.
+
+
+## Example program
+
+Prints "Bomb has been planted", every time you plant the bomb:
```
using System;
using CSGSI;
+using CSGSI.Nodes;
-namespace GSI_Test
+namespace CSGSI_Test
{
class Program
{
+ static GameStateListener gsl;
static void Main(string[] args)
{
- //subscribe to the NewGameState event
- GSIListener.NewGameState += new EventHandler(OnNewGameState);
-
- //start listening on http://127.0.0.1:3000/
- if (GSIListener.Start(3000))
+ gsl = new GameStateListener(3000);
+ gsl.NewGameState += new NewGameStateHandler(OnNewGameState);
+ if (!gsl.Start())
{
- Console.WriteLine("Listening...");
- }
- else
- {
- Console.WriteLine("Error starting GSIListener.");
+ Environment.Exit(0);
}
+ Console.WriteLine("Listening...");
}
- private static void OnNewGameState(object sender, EventArgs e)
+ static bool IsPlanted = false;
+
+ static void OnNewGameState(GameState gs)
{
- //the newest GameState object is provided as the sender
- GameState gs = (GameState) sender;
-
- //gs.JSON returns the JSON string that was used to create this GameState object
- Console.WriteLine(gs.JSON);
+ if(!IsPlanted &&
+ gs.Round.Phase == RoundPhase.Live &&
+ gs.Round.Bomb == BombState.Planted &&
+ gs.Previously.Round.Bomb == BombState.Undefined)
+ {
+ Console.WriteLine("Bomb has been planted.");
+ IsPlanted = true;
+ }else if(IsPlanted && gs.Round.Phase == RoundPhase.FreezeTime)
+ {
+ IsPlanted = false;
+ }
}
}
}
@@ -125,24 +216,28 @@ namespace GSI_Test
You will also need to create a custom `gamestate_integration_*.cfg` in `/csgo/cfg`, for example:
`gamestate_integration_test.cfg`:
```
-"CSGSI Test"
+"CSGSI Example"
{
- "uri" "http://127.0.0.1:3000"
- "timeout" "5.0"
- "buffer" "0.1"
- "throttle" "0.5"
- "heartbeat" "60.0"
- "data"
- {
- "provider" "1"
- "map" "1"
- "round" "1"
- "player_id" "1"
- "player_weapons" "1"
- "player_match_stats" "1"
- "player_state" "1"
- }
+ "uri" "http://localhost:3000"
+ "timeout" "5.0"
+ "auth"
+ {
+ "token" "CSGSI Test"
+ }
+ "data"
+ {
+ "provider" "1"
+ "map" "1"
+ "round" "1"
+ "player_id" "1"
+ "player_weapons" "1"
+ "player_match_stats" "1"
+ "player_state" "1"
+ "allplayers_id" "1"
+ "allplayers_state" "1"
+ "allplayers_match_stats" "1"
+ }
}
```
-After starting the application and the game, this setup should output something like this:
-![output example](http://i.imgur.com/baTaI0a.png)
\ No newline at end of file
+
+**Please note**: In order to run this test application without explicit administrator privileges, you need to use the URI `http://localhost:` in this configuration file.
diff --git a/old readmes/1.0.md b/old readmes/1.0.md
new file mode 100644
index 0000000..f084006
--- /dev/null
+++ b/old readmes/1.0.md
@@ -0,0 +1,148 @@
+# CSGSI
+A simple C# library to interface with Counter-Strike: Global Offensive *Game State Integration*
+
+# **!! Development ceased indefinitely !!**
+#### If you're looking for an actively developed alternative, see [master117's library](https://bitbucket.org/master117/csgogameobserversdk).
+
+
+## What is Game State Integration
+
+[This wiki page by Valve](https://developer.valvesoftware.com/wiki/Counter-Strike:_Global_Offensive_Game_State_Integration) explains the concept of GSI.
+
+## Installation
+1. Get the [latest binaries](https://github.com/rakijah/CSGSI/releases/latest)
+2. Get the [JSON Framework .dll by Newtonsoft](https://github.com/JamesNK/Newtonsoft.Json/releases)
+3. Extract Newtonsoft.Json.dll from `Bin\Net45\Newtonsoft.Json.dll`
+4. Add a reference to both CSGSI.dll and Newtonsoft.Json.dll in your project
+
+## About CSGSI
+
+This library provides means to listen to a specific port for the game clients HTTP POST request. Once a request is received, the game state is parsed and can then be analyzed to display the players' current condition, create bomb timers etc...
+
+CSGSI uses Newtonsoft's [JSON.Net Framework](http://www.newtonsoft.com/json) to parse JSON.
+
+The main class `GSIListener` continuously listens for incoming HTTP requests.
+Once a request is made, it's JSON content is used to construct a new `GameState` object.
+The `GameState` class represents the entire game state as it was sent by the client.
+It also provides access to all rootnodes (see Usage).
+These rootnodes and every subnode of them are of type `GameStateNode`, which encapsulates the underlying JSON data.
+
+## Usage
+
+Use `GSIListener.Start(int port)` to start listening for HTTP POST requests from the game client. This method will return *false* if starting the listener fails.
+
+**!! This step fails if your application is not run with administrator privileges !!**
+**Listening to a URI/port is not possible with regular privileges**
+
+The GSIListener class provides the `NewGameState` event that occurs after a new game state has been received:
+```
+GSIListener.NewGameState += new EventHandler(OnNewGameState);
+...
+
+private static void OnNewGameState(object sender, EventArgs e)
+{
+ GameState gs = (GameState) sender; //the newest GameState
+ //...
+}
+```
+
+The GameState object provides access to these rootnodes that may be transmitted by the game:
+
+* gs.Provider
+* gs.Map
+* gs.Round
+* gs.Player
+* gs.Auth
+* gs.Added
+* gs.Previously
+
+The rootnodes `allplayers_*` have not yet been implemented and `GameState` currently lacks a `GetRootNode` method (even though `HasRootNode` already exists).
+
+##### Example:
+
+```
+GameStateNode playerNode = gs.Player;
+```
+
+You can then access further subnodes by calling `playerNode.GetNode(string node);` (returns a `GameStateNode` object).
+You can retrieve a nodes value (as a string) by calling `playerNode.GetValue(string node);`
+
+##### Example:
+```
+playerNode.GetNode("state").GetValue("health");
+//100
+
+playerNode.GetNode("weapons").GetValue("weapon_0");
+//{
+// "name": "weapon_knife",
+// "paintkit": ...
+// ...
+//}
+```
+
+If the node you are trying to access using `GetValue` does not exist, it returns an empty string (`""`).
+
+## Example program
+
+This snippet will start listening for HTTP POST requests and print out the transmitted JSON data:
+
+```
+using System;
+using CSGSI;
+
+namespace GSI_Test
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ //subscribe to the NewGameState event
+ GSIListener.NewGameState += new EventHandler(OnNewGameState);
+
+ //start listening on http://127.0.0.1:3000/
+ if (GSIListener.Start(3000))
+ {
+ Console.WriteLine("Listening...");
+ }
+ else
+ {
+ Console.WriteLine("Error starting GSIListener.");
+ }
+ }
+
+ private static void OnNewGameState(object sender, EventArgs e)
+ {
+ //the newest GameState object is provided as the sender
+ GameState gs = (GameState) sender;
+
+ //gs.JSON returns the JSON string that was used to create this GameState object
+ Console.WriteLine(gs.JSON);
+ }
+ }
+}
+```
+
+You will also need to create a custom `gamestate_integration_*.cfg` in `/csgo/cfg`, for example:
+`gamestate_integration_test.cfg`:
+```
+"CSGSI Test"
+{
+ "uri" "http://127.0.0.1:3000"
+ "timeout" "5.0"
+ "buffer" "0.1"
+ "throttle" "0.5"
+ "heartbeat" "60.0"
+ "data"
+ {
+ "provider" "1"
+ "map" "1"
+ "round" "1"
+ "player_id" "1"
+ "player_weapons" "1"
+ "player_match_stats" "1"
+ "player_state" "1"
+ }
+}
+```
+After starting the application and the game, this setup should output something like this:
+![output example](http://i.imgur.com/baTaI0a.png)
\ No newline at end of file