-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathGameState.cs
159 lines (132 loc) · 4.81 KB
/
GameState.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LiveSplit.ComponentUtil;
using LiveSplit.SourceSplit.GameSpecific;
namespace LiveSplit.SourceSplit
{
// change back to struct if we ever need to give a copy of the state
// to the ui thread
class GameState
{
public const int ENT_INDEX_PLAYER = 1;
public Process GameProcess;
public GameOffsets GameOffsets;
public HostState HostState;
public HostState PrevHostState;
public SignOnState SignOnState;
public SignOnState PrevSignOnState;
public ServerState ServerState;
public ServerState PrevServerState;
public string CurrentMap;
public string GameDir;
public float IntervalPerTick;
public int RawTickCount;
public int TickBase;
public int TickCount;
public float TickTime;
public FL PlayerFlags;
public FL PrevPlayerFlags;
public Vector3f PlayerPosition;
public Vector3f PrevPlayerPosition;
public int PlayerViewEntityIndex;
public int PrevPlayerViewEntityIndex;
public int PlayerParentEntityHandle;
public int PrevPlayerParentEntityHandle;
public CEntInfoV2 PlayerEntInfo;
public GameSupport GameSupport;
public int UpdateCount;
public GameState(Process game, GameOffsets offsets)
{
this.GameProcess = game;
this.GameOffsets = offsets;
}
public CEntInfoV2 GetEntInfoByIndex(int index)
{
Debug.Assert(this.GameOffsets.EntInfoSize > 0);
CEntInfoV2 ret;
IntPtr addr = this.GameOffsets.GlobalEntityListPtr + ((int)this.GameOffsets.EntInfoSize * index);
if (this.GameOffsets.EntInfoSize == CEntInfoSize.HL2)
{
CEntInfoV1 v1;
this.GameProcess.ReadValue(addr, out v1);
ret = CEntInfoV2.FromV1(v1);
}
else
{
this.GameProcess.ReadValue(addr, out ret);
}
return ret;
}
// warning: expensive - 7ms on i5
// do not call frequently!
public IntPtr GetEntityByName(string name)
{
const int MAX_ENTS = 2048; // TODO: is portal2's max higher?
for (int i = 0; i < MAX_ENTS; i++)
{
CEntInfoV2 info = this.GetEntInfoByIndex(i);
if (info.EntityPtr == IntPtr.Zero)
continue;
IntPtr namePtr;
this.GameProcess.ReadPointer(info.EntityPtr + this.GameOffsets.BaseEntityTargetNameOffset, false, out namePtr);
if (namePtr == IntPtr.Zero)
continue;
string n;
this.GameProcess.ReadString(namePtr, ReadStringType.ASCII, 32, out n); // TODO: find real max len
if (n == name)
return info.EntityPtr;
}
return IntPtr.Zero;
}
}
struct GameOffsets
{
public IntPtr CurTimePtr;
public IntPtr TickCountPtr => this.CurTimePtr + 12;
public IntPtr IntervalPerTickPtr => this.TickCountPtr + 4;
public IntPtr SignOnStatePtr;
public IntPtr CurMapPtr;
public IntPtr GlobalEntityListPtr;
public IntPtr GameDirPtr;
public IntPtr HostStatePtr;
// note: only valid during host states: NewGame, ChangeLevelSP, ChangeLevelMP
public IntPtr HostStateLevelNamePtr => this.HostStatePtr + (4*8); // note: this may not work pre-ep1 (ancient engine)
public IntPtr ServerStatePtr;
public CEntInfoSize EntInfoSize;
public int BaseEntityFlagsOffset;
public int BaseEntityEFlagsOffset => this.BaseEntityFlagsOffset > 0 ? this.BaseEntityFlagsOffset - 4 : -1;
public int BaseEntityAbsOriginOffset;
public int BaseEntityTargetNameOffset;
public int BaseEntityParentHandleOffset;
public int BasePlayerViewEntity;
}
[StructLayout(LayoutKind.Sequential)]
struct CEntInfoV1
{
public int m_pEntity;
public int m_SerialNumber;
public int m_pPrev;
public int m_pNext;
}
[StructLayout(LayoutKind.Sequential)]
struct CEntInfoV2
{
public int m_pEntity;
public int m_SerialNumber;
public int m_pPrev;
public int m_pNext;
public int m_targetname;
public int m_classname;
public IntPtr EntityPtr => (IntPtr)this.m_pEntity;
public static CEntInfoV2 FromV1(CEntInfoV1 v1)
{
var ret = new CEntInfoV2();
ret.m_pEntity = v1.m_pEntity;
ret.m_SerialNumber = v1.m_SerialNumber;
ret.m_pPrev = v1.m_pPrev;
ret.m_pNext = v1.m_pNext;
return ret;
}
}
}