diff --git a/Admin/IW4M ADMIN.csproj b/Admin/IW4M ADMIN.csproj
index ebbc76fdf..07aee0cfe 100644
--- a/Admin/IW4M ADMIN.csproj
+++ b/Admin/IW4M ADMIN.csproj
@@ -43,6 +43,7 @@
DEBUG;TRACE
prompt
4
+ true
x86
@@ -52,6 +53,7 @@
TRACE
prompt
0
+ true
LocalIntranet
@@ -108,6 +110,7 @@
+
diff --git a/Admin/IW4_GameStructs.cs b/Admin/IW4_GameStructs.cs
new file mode 100644
index 000000000..7811ef54d
--- /dev/null
+++ b/Admin/IW4_GameStructs.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+
+namespace IW4MAdmin
+{
+ [StructLayout(LayoutKind.Explicit)]
+ public struct netadr_t
+ {
+ [FieldOffset(0x0)]
+ Int32 type;
+
+ [FieldOffset(0x4)]
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
+ public Byte[] ip;
+
+ [FieldOffset(0x8)]
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
+ public Byte[] ipx;
+
+ [FieldOffset(0x12)]
+ public short port;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct client_s
+ {
+ [FieldOffset(0x0)]
+ public Int32 state;
+
+ [FieldOffset(0x28)]
+ public netadr_t adr;
+
+ [FieldOffset(0x65C)]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
+ public String connectInfoString;
+
+ [FieldOffset(0x20EA4)]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst=400)] // doubt this is the correct size
+ public String lastUserCmd;
+
+ [FieldOffset(0x212A4)]
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
+ public String name;
+
+ [FieldOffset(0x212C0)]
+ public int snapNum;
+
+ [FieldOffset(0x212C8)]
+ public short ping;
+
+ [FieldOffset(0x41AF0)]
+ public int isBot;
+
+ [FieldOffset(0x43F00)]
+ public UInt64 steamid;
+ };
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct dvar_t
+ {
+ [FieldOffset(0)]
+ public IntPtr name;
+
+ [FieldOffset(4)]
+ public IntPtr description;
+
+ [FieldOffset(8)]
+ public uint flags;
+
+ [FieldOffset(12)]
+ short type;
+
+ [FieldOffset(16)]
+ public IntPtr current;
+
+ [FieldOffset(32)]
+ public IntPtr latched;
+
+ [FieldOffset(48)]
+ public IntPtr _default;
+
+ [FieldOffset(65)]
+ public IntPtr min;
+
+ [FieldOffset(68)]
+ public IntPtr max;
+ }
+
+ // not iw4
+ public struct dvar
+ {
+ public String name;
+
+ public String description;
+
+ public int flags;
+
+ short type;
+
+ public String current;
+
+ public String latched;
+
+ public String _default;
+
+ public int min;
+
+ public int max;
+ }
+
+
+
+ class Helpers
+ {
+ public static String NET_AdrToString(netadr_t a)
+ {
+ // not worrying about NA_TYPE
+ StringBuilder s = new StringBuilder(64);
+ s.AppendFormat("{0}.{1}.{2}.{3}:{4}", a.ip[0], a.ip[1], a.ip[2], a.ip[3], a.port);
+ return s.ToString();
+ }
+
+ public static unsafe T ReadStruct(byte[] buffer) where T : struct
+ {
+ fixed (byte* b = buffer)
+ return (T)Marshal.PtrToStructure(new IntPtr(b), typeof(T));
+ }
+ }
+
+}
diff --git a/Admin/Main.cs b/Admin/Main.cs
index fa5b80f2c..042f8d7c5 100644
--- a/Admin/Main.cs
+++ b/Admin/Main.cs
@@ -1,7 +1,11 @@
-using System;
+#define USINGMEMORY
+using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Net;
namespace IW4MAdmin
{
@@ -12,7 +16,8 @@ namespace IW4MAdmin
static String RCON;
static public double Version = 0.9;
static public double latestVersion;
- static public List Servers;//
+ static public List Servers;
+ static public bool usingMemory = true;
static void Main(string[] args)
{
@@ -26,8 +31,14 @@ namespace IW4MAdmin
Console.WriteLine(" Version " + Version + " (unable to retrieve latest)");
Console.WriteLine("=====================================================");
- Servers = checkConfig();
- foreach (Server IW4M in Servers)
+ List viableServers = getServers();
+
+ if (viableServers.Count < 1)
+ viableServers = checkConfig(); // fall back to config
+
+ Servers = viableServers;
+
+ foreach (Server IW4M in viableServers)
{
//Threading seems best here
Server SV = IW4M;
@@ -88,7 +99,7 @@ namespace IW4MAdmin
Config = new file("config\\servers.cfg", true);
Config.Write(IP + ':' + Port + ':' + RCON);
Config.Close();
- Servers.Add(new Server(IP, Port, RCON));
+ Servers.Add(new Server(IP, Port, RCON, 0));
}
else
@@ -102,11 +113,43 @@ namespace IW4MAdmin
Console.WriteLine("You have an error in your server.cfg");
continue;
}
- Servers.Add(new Server(server_line[0], newPort, server_line[2]));
+ Servers.Add(new Server(server_line[0], newPort, server_line[2],0));
}
}
return Servers;
}
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool ReadProcessMemory(int hProcess,
+ int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
+
+ static List getServers()
+ {
+ List Servers = new List();
+ foreach ( Process P in Process.GetProcessesByName("iw4m"))
+ {
+ IntPtr Handle = OpenProcess(0x0010, false, P.Id);
+ int numberRead = 0;
+ Byte[] dediStuff = new Byte[1];
+ ReadProcessMemory((int)Handle, 0x5DEC04, dediStuff, 1, ref numberRead);
+
+ if (dediStuff[0] == 0)
+ {
+ Console.WriteLine("Viable IW4M Instance found with PID #" + P.Id);
+
+ dvar net_ip = Utilities.getDvar(0x64A1DF8, (int)Handle);
+ dvar net_port = Utilities.getDvar(0x64A3004, (int)Handle);
+ dvar rcon_password = Utilities.getDvar(0x111FF634, (int)Handle);
+
+ Servers.Add(new Server(Dns.GetHostAddresses(net_ip.current)[1].ToString(), Convert.ToInt32(net_port.current), rcon_password.current, (int)Handle));
+ }
+ }
+ return Servers;
+
+ }
}
}
diff --git a/Admin/Server.cs b/Admin/Server.cs
index 72193231e..e0aa65919 100644
--- a/Admin/Server.cs
+++ b/Admin/Server.cs
@@ -1,10 +1,12 @@
-
+//#define USINGMEMORY
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Threading; //SLEEP
using System.IO;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
namespace IW4MAdmin
@@ -13,8 +15,9 @@ namespace IW4MAdmin
{
const int FLOOD_TIMEOUT = 300;
- public Server(string address, int port, string password)
+ public Server(string address, int port, string password, int H)
{
+ Handle = H;
IP = address;
Port = port;
rcon_pass = password;
@@ -334,7 +337,7 @@ namespace IW4MAdmin
{
if (players[cNum] == null)
{
- Log.Write("Error - Disconnecting client slot is already empty!", Log.Level.Debug);
+ //Log.Write("Error - Disconnecting client slot is already empty!", Log.Level.Debug);
return false;
}
@@ -509,7 +512,7 @@ namespace IW4MAdmin
}
- //process new event every 100 milliseconds
+ //process new event every 50 milliseconds
private void manageEventQueue()
{
while (isRunning)
@@ -519,10 +522,16 @@ namespace IW4MAdmin
processEvent(events.Peek());
events.Dequeue();
}
- Utilities.Wait(0.1);
+ Utilities.Wait(0.05);
}
}
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
+
//Starts the monitoring process
public void Monitor()
{
@@ -551,11 +560,11 @@ namespace IW4MAdmin
Utilities.Wait(10);
return;
}
-
+#if DEBUG == false
//Thread to handle polling server for IP's
Thread statusUpdate = new Thread(new ThreadStart(pollServer));
statusUpdate.Start();
-
+#endif
//Handles new events in a fashionable manner
Thread eventQueue = new Thread(new ThreadStart(manageEventQueue));
eventQueue.Start();
@@ -576,6 +585,8 @@ namespace IW4MAdmin
while (isRunning)
{
+
+
#if DEBUG == false
try
#endif
@@ -613,6 +624,27 @@ namespace IW4MAdmin
}
}
+#if DEBUG
+ if ((DateTime.Now - lastPoll).Milliseconds > 750)
+ {
+ int numberRead = 0;
+
+ for (int i = 0; i < players.Count; i++)
+ {
+ Byte[] buff = new Byte[681872]; // struct size ( 0.68MB :( )
+ ReadProcessMemory((int)Handle, 0x31D9390 + (buff.Length)*(i), buff, buff.Length, ref numberRead); // svs_clients start + current client
+
+ client_s eachClient = (client_s)Helpers.ReadStruct(buff);
+
+ if (eachClient.state == 0)
+ removePlayer(i);
+ else if (eachClient.state > 1)
+ addPlayer(new Player(eachClient.name, eachClient.steamid.ToString("x16"), i, 0, i, null, 0, Helpers.NET_AdrToString(eachClient.adr).Split(':')[0]));
+ }
+
+ lastPoll = DateTime.Now;
+ }
+#endif
if (l_size != logFile.getSize())
{
@@ -658,7 +690,7 @@ namespace IW4MAdmin
}
oldLines = lines;
l_size = logFile.getSize();
- Thread.Sleep(1);
+ Thread.Sleep(250);
}
#if DEBUG == false
catch (Exception E)
@@ -672,7 +704,6 @@ namespace IW4MAdmin
isRunning = false;
RCONQueue.Abort();
eventQueue.Abort();
-
}
private void pollServer()
@@ -748,6 +779,30 @@ namespace IW4MAdmin
}
//Vital RCON commands to establish log file and server name. May need to cleanup in the future
+#if USINGMEMORY
+ private bool initializeBasics()
+ {
+ dvar map = Utilities.getDvar(0x2098DDC, Handle);
+
+ String mapOut;
+
+ mapname = map.current;
+
+ dvar sv_hostname = Utilities.getDvar(0x2098D98, Handle);
+ hostname = sv_hostname.current;
+
+ dvar shortversion = Utilities.getDvar(0x1AD79D0, Handle);
+ IW_Ver = shortversion.current;
+
+ dvar party_maxplayers = Utilities.getDvar(0x1080998, Handle);
+ maxClients = Convert.ToInt32(party_maxplayers.current);
+
+ dvar g_gametype = Utilities.getDvar(0x1AD7934, Handle);
+ Gametype = g_gametype.current;
+
+ // skipping website b/c dynamically allocated ( we will pick it up on maprotate )
+ }
+#else
private bool intializeBasics()
{
try
@@ -922,11 +977,11 @@ namespace IW4MAdmin
return false;
}
}
-
+#endif
//Process any server event
public bool processEvent(Event E)
{
- /*if (E.Type == Event.GType.Connect) // this is anow handled by rcon status :(
+ /*if (E.Type == Event.GType.Connect) // this is anow handled by memory :)
{
if (E.Origin == null)
Log.Write("Connect event triggered, but no client is detected!", Log.Level.Debug);
@@ -1089,7 +1144,8 @@ namespace IW4MAdmin
{
try
{
- mapname = maps.Find(m => m.Name.Equals(mapname)).Alias;
+ Map newMap = maps.Find(m => m.Name.Equals(newMapName));
+ mapname = newMap.Alias;
}
catch (Exception)
@@ -1151,7 +1207,14 @@ namespace IW4MAdmin
public void Tell(String Message, Player Target)
{
if (Target.getClientNum() > -1)
+ {
+#if DEBUG
+ RCON.addRCON("tell " + Target.getClientNum() + " " + Message + "^7");
+
+#else
RCON.addRCON("tellraw " + Target.getClientNum() + " " + Message + "^7"); // I fixed tellraw :>
+#endif
+ }
}
public void Kick(String Message, Player Target)
@@ -1460,6 +1523,7 @@ namespace IW4MAdmin
private Dictionary Macros;
private Moserware.TrueSkill Skills;
private DateTime lastWebChat;
+ private int Handle;
//Will probably move this later
diff --git a/Admin/Utilities.cs b/Admin/Utilities.cs
index 213dcfd6c..516a73a66 100644
--- a/Admin/Utilities.cs
+++ b/Admin/Utilities.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
+using System.Runtime.InteropServices;
namespace IW4MAdmin
{
@@ -314,6 +315,81 @@ namespace IW4MAdmin
return "a very long time";
}
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
+
+ public static dvar getDvar(int Location, int Handle)
+ {
+ int numberRead = 0;
+ Byte[] Buff = new Byte[72];
+ Byte[] Ptr = new Byte[4];
+
+ ReadProcessMemory(Handle, Location, Ptr, Ptr.Length, ref numberRead); // get location of dvar
+
+ ReadProcessMemory(Handle, (int)BitConverter.ToUInt32(Ptr, 0), Buff, Buff.Length, ref numberRead); // read dvar memory
+
+ dvar_t dvar_raw = Helpers.ReadStruct(Buff); // get the dvar struct
+
+ dvar dvar_actual = new dvar(); // gotta convert to something readable
+
+ dvar_actual.name = getStringFromPointer((int)dvar_raw.name, Handle);
+ dvar_actual.description = getStringFromPointer((int)dvar_raw.description, Handle);
+
+ if ((int)dvar_raw._default > short.MaxValue)
+ dvar_actual._default = getStringFromPointer((int)dvar_raw._default, Handle);
+ else
+ dvar_actual._default = dvar_raw._default.ToString();
+
+ if ((int)dvar_raw.current > short.MaxValue)
+ dvar_actual.current = getStringFromPointer((int)dvar_raw.current, Handle);
+ else
+ dvar_actual.current = dvar_raw.current.ToString();
+
+ if ((int)dvar_raw.latched > short.MaxValue)
+ dvar_actual.latched = getStringFromPointer((int)dvar_raw.latched, Handle);
+ else
+ dvar_actual.latched = dvar_raw.latched.ToString();
+
+ dvar_actual.flags = getIntFromPointer((int)dvar_raw.flags, Handle);
+ dvar_actual.max = getIntFromPointer((int)dvar_raw.max, Handle);
+ dvar_actual.min = getIntFromPointer((int)dvar_raw.min, Handle);
+
+ // done!
+
+ return dvar_actual;
+ }
+
+ public static String getStringFromPointer(int Location, int Handle)
+ {
+ int numberRead = 0;
+ Byte[] Buff = new Byte[256];
+
+ ReadProcessMemory(Handle, Location, Buff, Buff.Length, ref numberRead);
+
+ StringBuilder str = new StringBuilder();
+ for ( int i = 0; i < Buff.Length; i++)
+ {
+ if (Buff[i] == 0)
+ break;
+
+ str.Append((char)Buff[i]);
+ }
+ return str.ToString();
+ }
+
+ public static int getIntFromPointer(int Location, int Handle)
+ {
+ int numberRead = 0;
+ Byte[] Buff = new Byte[4];
+
+ ReadProcessMemory(Handle, Location, Buff, Buff.Length, ref numberRead);
+
+ return BitConverter.ToInt32(Buff, 0);
+ }
+
public static String timesConnected(int connection)
{
String Prefix = String.Empty;
diff --git a/Admin/version.txt b/Admin/version.txt
index 1853511c5..4102f2667 100644
--- a/Admin/version.txt
+++ b/Admin/version.txt
@@ -2,6 +2,7 @@
CHANGELOG:
-fixed issue with `history` timelime
-fixed issue with mapname not being updated
+-now reads memory for player info! ( experimental debug only )
VERSION: 0.9
CHANGELOG: