-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvscript.cpp
291 lines (257 loc) · 11.1 KB
/
vscript.cpp
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
//===========================================================================//
//
// Author: Nanoman2525
// Purpose: Portal 2: Multiplayer Mod server plugin
//
//===========================================================================//
#include "scanner.hpp"
#include "modules.hpp"
#include "p2mm.hpp"
#include "irecipientfilter.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern ConVar p2mm_developer;
extern ConVar p2mm_lastmap;
//---------------------------------------------------------------------------------
// Purpose: Logging for the P2MM VScript. The log message must be passed as a string or it will error.
//---------------------------------------------------------------------------------
static void printlP2MM(int level, bool dev, const char* pMsgFormat)
{
va_list argptr;
char szFormattedText[1024];
va_start(argptr, pMsgFormat);
V_vsnprintf(szFormattedText, sizeof(szFormattedText), pMsgFormat, argptr);
va_end(argptr);
char completeMsg[1024];
V_snprintf(completeMsg, sizeof(completeMsg), "(P2:MM VSCRIPT): %s\n", szFormattedText);
if (dev && !p2mm_developer.GetBool())
{
return;
}
switch (level)
{
case 0:
ConColorMsg(P2MM_VSCRIPT_CONSOLE_COLOR, completeMsg);
return;
case 1:
Warning(completeMsg);
return;
default:
Warning("(P2:MM VSCRIPT): printlP2MM level set outside of 0-1, \"%i\", defaulting to ConColorMsg().\n", level);
ConColorMsg(P2MM_VSCRIPT_CONSOLE_COLOR, completeMsg);
return;
}
}
//---------------------------------------------------------------------------------
// Purpose: Returns true is the supplied string is a valid map name.
//---------------------------------------------------------------------------------
static bool IsMapValid(const char* map)
{
return engineServer->IsMapValid(map);
}
//---------------------------------------------------------------------------------
// Purpose: Returns the value of ConVar p2mm_developer.
//---------------------------------------------------------------------------------
static int GetDeveloperLevelP2MM()
{
return p2mm_developer.GetInt();
}
//---------------------------------------------------------------------------------
// Purpose: Sets 'player_held_object_use_view_model' to the supplied integer value.
//---------------------------------------------------------------------------------
static void SetPhysTypeConvar(int newval)
{
g_pCVar->FindVar("player_held_object_use_view_model")->SetValue(newval);
}
//---------------------------------------------------------------------------------
// Purpose: Sets 'portal_max_separation_force' to the supplied integer value.
//---------------------------------------------------------------------------------
static void SetMaxPortalSeparationConvar(int newval)
{
if (engineServer->IsDedicatedServer())
{
P2MMLog(1, true, "SetMaxPortalSeparationConVar can not be set on dedicated servers!");
return;
}
g_pCVar->FindVar("portal_max_separation_force")->SetValue(newval);
}
//---------------------------------------------------------------------------------
// Purpose: Returns true if this is a dedicated server.
//---------------------------------------------------------------------------------
static bool IsDedicatedServer()
{
return engineServer->IsDedicatedServer();
}
//---------------------------------------------------------------------------------
// Purpose: Initializes an entity.
//---------------------------------------------------------------------------------
static void InitializeEntity(HSCRIPT ent)
{
static uintptr_t func = (uintptr_t)Memory::Scanner::Scan<void*>(Memory::Modules::Get("server"), "E8 ?? ?? ?? ?? 8B 4D 18 8B 57 5C", 1);
static auto GetCBaseEntityScriptDesc = reinterpret_cast<ScriptClassDesc_t * (*)()>(*reinterpret_cast<uintptr_t*>(func) + func + sizeof(func));
void* pEntity = reinterpret_cast<void*>(g_pScriptVM->GetInstanceValue(ent, GetCBaseEntityScriptDesc()));;
if (pEntity)
{
g_pServerTools->DispatchSpawn(pEntity);
static auto Activate = *reinterpret_cast<void(__thiscall**)(void*)>(*reinterpret_cast<uintptr_t*>(pEntity) + 148);
Activate(pEntity);
}
}
class CFilter : public IRecipientFilter
{
public:
CFilter() { recipient_count = 0; };
~CFilter() {};
virtual bool IsReliable() const { return false; };
virtual bool IsInitMessage() const { return false; };
virtual int GetRecipientCount() const { return recipient_count; };
virtual int GetRecipientIndex(int slot) const { return ((slot < 0) || (slot >= recipient_count)) ? -1 : recipients[slot]; };
void AddPlayer(int player_index)
{
if (recipient_count > 255)
{
return;
}
recipients[recipient_count] = player_index;
recipient_count++;
}
private:
int recipients[256];
int recipient_count;
};
//---------------------------------------------------------------------------------
// Purpose: Sends a raw message to the chat HUD.
//---------------------------------------------------------------------------------
static void SendToChat(const char* msg, int index)
{
if (!msg)
{
return;
}
bf_write* netmsg;
CFilter recipient_filter;
// Send to all players
if (index == 0)
{
for (int i = 1; i < gpGlobals->maxClients; i++)
{
player_info_t playerinfo;
if (engineServer->GetPlayerInfo(i, &playerinfo))
{
recipient_filter.AddPlayer(i);
}
}
}
else
{
recipient_filter.AddPlayer(index);
}
netmsg = engineServer->UserMessageBegin(&recipient_filter, 3, "SayText2");
netmsg->WriteByte(0);
netmsg->WriteString(msg);
netmsg->WriteByte(1);
engineServer->MessageEnd();
}
//---------------------------------------------------------------------------------
// Purpose: Returns the game directory.
//---------------------------------------------------------------------------------
static const char* GetGameDirectory()
{
return CommandLine()->ParmValue("-game", CommandLine()->ParmValue("-defaultgamedir", "hl2"));
}
//---------------------------------------------------------------------------------
// Purpose: Returns the last map recorded by the launcher's Last Map System.
//---------------------------------------------------------------------------------
static const char* GetLastMap()
{
return p2mm_lastmap.GetString();
}
//---------------------------------------------------------------------------------
// Purpose: Get or set the state of whether the first map was run or not.
// Set false/true = 0/1 | -1 to get state.
//---------------------------------------------------------------------------------
static bool FirstRunState(int state = -1)
{
if (state == 0 || state == 1) {
return g_P2MMServerPlugin.m_bFirstMapRan = !!state;
}
return g_P2MMServerPlugin.m_bFirstMapRan;
}
//---------------------------------------------------------------------------------
// Purpose: Shows the first run prompt if enabled in config.nut.
//---------------------------------------------------------------------------------
static void CallFirstRunPrompt()
{
// Don't display again once the first one is shown.
if (g_P2MMServerPlugin.m_bSeenFirstRunPrompt)
{
P2MMLog(0, true, "First run prompt already shown...");
return;
}
P2MMLog(0, false, "DISPLAYING FIRST RUN PROMPT!");
// Put together KeyValues to pass to CreateMessage.
KeyValues* kv = new KeyValues("firstrunprompt");
kv->SetInt("level", 0);
kv->SetWString("title", g_pLocalize->FindSafe("#P2MM_FirstRunPrompt_t"));
//kv->SetString("title", "Welcome to the Portal 2: Multiplayer Mod!");
kv->SetWString("msg", g_pLocalize->FindSafe("#P2MM_FirstRunPrompt_d"));
/*kv->SetString("msg",
"Welcome to the Portal 2: Multiplayer Mod!\n\n"
"Input '!help' into chat to see a full list of chat commands you can use!\n"
"Hope you enjoy the mod! - Portal 2: Multiplayer Mod Team\n\n"
"This message can be disabled in config.nut located in the local p2mm folder on your system.\n"
"'p2mm/ModFiles/Portal 2/install_dlc/scripts/vscripts/multiplayermod/config.nut'"
);*/
// CreateMessage prompts can only be seen when the pause menu is up, so pause the game.
engineClient->ExecuteClientCmd("gameui_activate");
pluginHelpers->CreateMessage(INDEXENT(1), DIALOG_TEXT, kv, &g_P2MMServerPlugin);
kv->deleteThis();
// Set the plugin variable flag that the host seen the prompt to true so its not reshown.
g_P2MMServerPlugin.m_bSeenFirstRunPrompt = true;
}
void RegisterFuncsAndRun()
{
g_pScriptVM = **Memory::Scanner::Scan<IScriptVM***>(Memory::Modules::Get("server"), "8B 1D ?? ?? ?? ?? 57 85 DB", 2);
if (!g_pScriptVM)
{
P2MMLog(1, false, "Could not run or register our VScript functions!");
return;
}
ScriptRegisterFunction(g_pScriptVM, printlP2MM, "Logging for the P2MM VScript. The log message must be passed as a string or it will error.");
ScriptRegisterFunctionNamed(g_pScriptVM, GFunc::GetPlayerName, "GetPlayerName", "Gets player username by index.");
ScriptRegisterFunctionNamed(g_pScriptVM, GFunc::GetSteamID, "GetSteamID", "Gets the account ID component of player SteamID by index.");
ScriptRegisterFunctionNamed(g_pScriptVM, GFunc::UserIDToPlayerIndex, "UserIDToPlayerIndex", "Gets player entity index by userid.");
ScriptRegisterFunction(g_pScriptVM, IsMapValid, "Returns true is the supplied string is a valid map name.");
ScriptRegisterFunction(g_pScriptVM, GetDeveloperLevelP2MM, "Returns the value of ConVar p2mm_developer.");
ScriptRegisterFunction(g_pScriptVM, SetPhysTypeConvar, "Sets 'player_held_object_use_view_model' to the supplied integer value.");
ScriptRegisterFunction(g_pScriptVM, SetMaxPortalSeparationConvar, "Sets 'portal_max_separation_force' to the supplied integer value.");
ScriptRegisterFunction(g_pScriptVM, IsDedicatedServer, "Returns true if this is a dedicated server.");
ScriptRegisterFunction(g_pScriptVM, InitializeEntity, "Initializes an entity.");
ScriptRegisterFunction(g_pScriptVM, SendToChat, "Sends a raw message to the chat HUD.");
ScriptRegisterFunction(g_pScriptVM, GetGameDirectory, "Returns the game directory.");
ScriptRegisterFunction(g_pScriptVM, GetLastMap, "Returns the last map recorded by the launcher's Last Map system.");
ScriptRegisterFunction(g_pScriptVM, FirstRunState, "Get or set the state of whether the first map was run or not. Set false/true = 0/1 | -1 to get state.");
ScriptRegisterFunction(g_pScriptVM, CallFirstRunPrompt, "Shows the first run prompt if enabled in config.nut.");
ScriptRegisterFunctionNamed(g_pScriptVM, GFunc::GetConVarInt, "GetConVarInt", "Get the integer value of a ConVar.");
ScriptRegisterFunctionNamed(g_pScriptVM, GFunc::GetConVarString, "GetConVarString", "Get the string value of a ConVar.");
// Set all the plugin function check bools to true and start the P2:MM VScript
g_pScriptVM->Run(
"P2MMPluginLoaded <- true;"
"printlP2MMLoaded <- true;"
"GetPlayerNameLoaded <- true;"
"GetSteamIDLoaded <- true;"
"GetPlayerIndexLoaded <- true;"
"IsMapValidLoaded <- true;"
"GetDeveloperLevelP2MMLoaded <- true;"
"SetPhysTypeConvarLoaded <- true;"
"SetMaxPortalSeparationConvarLoaded <- true;"
"IsDedicatedServerLoaded <- true;"
"InitializeEntityLoaded <- true;"
"SendToChatLoaded <- true;"
"GetGameDirectoryLoaded <- true;"
"GetLastMapLoaded <- true;"
"FirstRunStateLoaded <- true;"
"CallFirstRunPromptLoaded <- true;"
"IncludeScript(\"multiplayermod/p2mm\");"
);
}