Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:Portal-2-Multiplayer-Mod/Portal-2-Mu…
Browse files Browse the repository at this point in the history
…ltiplayer-Mod-Plugin into dev
  • Loading branch information
0xNULLderef committed Oct 26, 2024
2 parents d7790ee + 5193848 commit af6f770
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 113 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/ms-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: MSBuild

on:
push

permissions:
contents: read

jobs:
build:
runs-on: windows-latest

steps:
- uses: actions/checkout@v4

- name: Add MSBuild into PATH
uses: microsoft/setup-msbuild@v2
with:
# Version of Visual Studio to search; defaults to latest if not specified
vs-version: "latest"
# The preferred processor architecture of MSBuild. Can be either "x86", "x64", or "arm64". "x64" is only available from Visual Studio version 17.0 and later.
msbuild-architecture: "x86"

- name: Build
working-directory: ${{env.GITHUB_WORKSPACE}}
# Add additional options to the MSBuild command line here (like platform or verbosity level).
# See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
run: msbuild /m /p:Configuration="Release" .

- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: p2mm
path: Release/p2mm.dll
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Improved C/C++ .gitignore file by Yousha
# https://gist.github.com/Yousha/3830712334ac30a90eb6041b932b68d7

copy.bat

##### Windows
# Windows thumbnail cache files
Thumbs.db
Expand Down Expand Up @@ -1043,4 +1045,4 @@ dkms.conf
# Raspberry Pi Pico Object file
*.uf2
# Raspberry Pi Pico disassembler file
*.dis
*.dis
7 changes: 7 additions & 0 deletions globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "engine/iserverplugin.h"
#include "public/localize/ilocalize.h"
#include "public/steam/steamclientpublic.h"
#include "public/filesystem.h"
#include "tier2/fileutils.h"

#include "scanner.hpp"

Expand All @@ -31,6 +33,10 @@ class CPortal_Player;

#define CURRENTMAPNAME STRING(gpGlobals->mapname)

// Used for autocomplete console commands.
#define COMMAND_COMPLETION_MAXITEMS 64
#define COMMAND_COMPLETION_ITEM_LENGTH 64

//---------------------------------------------------------------------------------
// Any ConVars or CON_COMMANDS that need to be globally available
//---------------------------------------------------------------------------------
Expand All @@ -47,6 +53,7 @@ extern IScriptVM* g_pScriptVM;
extern IServerTools* g_pServerTools;
extern IGameEventManager2* gameeventmanager_;
extern IServerPluginHelpers* pluginHelpers;
extern IFileSystem* g_pFileSystem;

void P2MMLog(int level, bool dev, const char* pMsgFormat, ...);

Expand Down
137 changes: 103 additions & 34 deletions p2mm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ IScriptVM* g_pScriptVM = NULL; // Access VScript interface
IServerTools* g_pServerTools = NULL; // Access to interface from engine to tools for manipulating entities
IGameEventManager2* gameeventmanager_ = NULL; // Access game events interface
IServerPluginHelpers* pluginHelpers = NULL; // Access interface for plugin helper functions
IFileSystem* g_pFileSystem = NULL; // Access interface for Valve's file system interface
#ifndef GAME_DLL
#define gameeventmanager gameeventmanager_
#endif
Expand Down Expand Up @@ -105,30 +106,95 @@ ConVar p2mm_developer("p2mm_developer", "0", FCVAR_NONE, "Enable for P2:MM devel
ConVar p2mm_spewgameeventinfo("p2mm_spewgameevents", "0", FCVAR_NONE, "Log information from called game events in the console, p2mm_developer must also be on. Can cause lots of console spam.");

// ConCommands
CON_COMMAND(p2mm_startsession, "Starts up a P2:MM session with a requested map.")

std::vector<std::string> m_Maps; // List of maps for the p2mm_command auto complete.

void updateMapsList() {
m_Maps.clear();
char relativePath[MAX_PATH];
CUtlVector<CUtlString> outList;
AddFilesToList(outList, "maps", "GAME", "bsp");

FOR_EACH_VEC(outList, i)
{
// Get each map and get their relative path to each SearchPath and make slashes forward slashes.
// Then turn relativePath into a std::string to easily manipulate.
const char* curmap = outList[i];
g_pFileSystem->FullPathToRelativePathEx(curmap, "GAME", relativePath, sizeof(relativePath));
V_FixSlashes(relativePath, '/');
std::string fixedRelativePath(relativePath);

// Remove the "maps/" out of the string.
fixedRelativePath.erase(0, strlen("maps/"));

// Remove the .bsp extension at the end.
size_t bspPos = fixedRelativePath.rfind(".bsp");
if (bspPos != std::string::npos) {
fixedRelativePath.erase(bspPos);
}
// Remove "workshop/" from the front of the string.
//size_t workshopPos = fixedRelativePath.rfind("workshop/");
//size_t lastSlashPos = fixedRelativePath.find_last_of("/");
//if (workshopPos != std::string::npos && lastSlashPos != std::string::npos) {
// fixedRelativePath.erase(workshopPos, lastSlashPos + 1 - strlen("workshop/"));
//}

// Push the map string on to the list to display avaliable options for the command.
m_Maps.push_back(std::move(fixedRelativePath));
P2MMLog(0, true, m_Maps.back().c_str());
}
}

static int p2mm_startsession_CompletionFunc(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH])
{
// Make sure the CON_COMMAND was executed correctly
// If the map list is empty, generate it.
if (m_Maps.empty()) {
updateMapsList();
}

// Assemble together the current state of the inputted command.
const char* concommand = "p2mm_startsession ";
const char* match = (V_strstr(partial, concommand) == partial) ? partial + V_strlen(concommand) : partial;

// Go through the map list searching for matches with the assembled inputted command.
int numMatchedMaps = 0;
for (const std::string map : m_Maps) {
if (numMatchedMaps >= COMMAND_COMPLETION_MAXITEMS) break;

if (V_strstr(map.c_str(), match)) {
V_snprintf(commands[numMatchedMaps++], COMMAND_COMPLETION_ITEM_LENGTH, "%s%s", concommand, map.c_str());
P2MMLog(0, true, map.c_str());
}
}

return numMatchedMaps;
}

CON_COMMAND_F_COMPLETION(p2mm_startsession, "Starts up a P2:MM session with a requested map.", 0, p2mm_startsession_CompletionFunc)
{
// Make sure the CON_COMMAND was executed correctly.
if (args.ArgC() < 2 || FStrEq(args.Arg(1), ""))
{
P2MMLog(1, false, "p2mm_startsession called incorrectly! Usage: 'p2mm_startsession (map to start) '");
return;
}

// A check done by the menu to request to use the last recorded map in the p2mm_lastmap ConVar
std::string requestedMap = args.Arg(1);
P2MMLog(0, true, "Requested Map: %s", requestedMap.c_str());
// A check done by the menu to request to use the last recorded map in the p2mm_lastmap ConVar.
const char* requestedMap = args.Arg(1);
P2MMLog(0, true, "Requested Map: %s", requestedMap);
P2MMLog(0, true, "p2mm_lastmap: %s", p2mm_lastmap.GetString());
if (FSubStr(requestedMap.c_str(), "P2MM_LASTMAP"))
if (FSubStr(requestedMap, "P2MM_LASTMAP"))
{
if (!engineServer->IsMapValid(p2mm_lastmap.GetString()))
{
// Running disconnect causes the music to stop, we don't want that to happen, so here we start it again.
// Running disconnect to make the error screen appear causes the music to stop, don't let that to happen, so here it is start it again.

// Get the current act so we can start the right main menu music
int iAct = ConVarRef("ui_lastact_played").GetInt();
if (iAct > 5) { iAct = 5; } else if (iAct < 1) { iAct = 1; }

// Put the command to start the music and the act number together
if (iAct > 5) iAct = 5;
else if (iAct < 1) iAct = 1;

// Put the command to start the music and the act number together.
char completePVCmd[sizeof("playvol \"#music/mainmenu/portal2_background0%d\" 0.35") + sizeof(iAct)];
V_snprintf(completePVCmd, sizeof(completePVCmd), "playvol \"#music/mainmenu/portal2_background0%i\" 0.35", iAct);

Expand All @@ -138,38 +204,37 @@ CON_COMMAND(p2mm_startsession, "Starts up a P2:MM session with a requested map."
return;
}
requestedMap = p2mm_lastmap.GetString();
P2MMLog(0, true, "P2MM_LASTMAP called! Running Last Map: %s", requestedMap.c_str());
P2MMLog(0, true, "P2MM_LASTMAP called! Running Last Map: \"%s\"", requestedMap);
}
p2mm_lastmap.SetValue(""); // Set last map ConVar to blank so it doesn't trigger in situations where we don't want it to trigger
p2mm_lastmap.SetValue(""); // Set last map ConVar to blank so it doesn't trigger in situations where we don't want it to trigger.

// Check if the supplied map is a valid map
if (!engineServer->IsMapValid(requestedMap.c_str()))
// Check if the supplied map is a valid map.
if (!engineServer->IsMapValid(requestedMap))
{
P2MMLog(1, false, "p2mm_startsession was given a non-valid map or one that doesn't exist! %s", requestedMap.c_str());
P2MMLog(1, false, "p2mm_startsession was given a non-valid map or one that doesn't exist! \"%s\"", requestedMap);
return;
}

// Check if the user requested it to start in splitscreen or not
std::string mapString = "";
if (p2mm_splitscreen.GetBool()) { mapString = "ss_map "; } else { mapString = "map "; }
// Check if the user requested it to start in splitscreen or not.
std::string mapString = p2mm_splitscreen.GetBool() ? "ss_map " : "map ";
P2MMLog(0, true, "Map String: %s", mapString.c_str());

// Set first run flag on and set the last map ConVar value so the system
// Set first run flag on and set the last map ConVar value so the system.
// can change from mp_coop_community_hub to the requested map.
// Also set m_bSeenFirstRunPrompt back to false so the prompt can be triggered again.
g_P2MMServerPlugin.m_bFirstMapRan = true;
g_P2MMServerPlugin.m_bSeenFirstRunPrompt = false;
if (!FSubStr(requestedMap.c_str(), "mp_coop"))
if (!FSubStr(requestedMap, "mp_coop"))
{
P2MMLog(0, true, "'mp_coop' not found, singleplayer map being run. Full ExecuteClientCmd: \"%s\"", std::string(mapString + "mp_coop_community_hub").c_str());
P2MMLog(0, true, "requestedMap: \"%s\"", requestedMap.c_str());
p2mm_lastmap.SetValue(requestedMap.c_str());
P2MMLog(0, true, "requestedMap: \"%s\"", requestedMap);
p2mm_lastmap.SetValue(requestedMap);
engineClient->ExecuteClientCmd(std::string(mapString + "mp_coop_community_hub").c_str());
}
else
{
P2MMLog(0, true, "'mp_coop' found, multiplayer map being run. Full ExecuteClientCmd: \"%s\"", std::string(mapString + requestedMap).c_str());
P2MMLog(0, true, "requestedMap: \"%s\"", requestedMap.c_str());
P2MMLog(0, true, "requestedMap: \"%s\"", requestedMap);
engineClient->ExecuteClientCmd(std::string(mapString + requestedMap).c_str());
}
}
Expand Down Expand Up @@ -229,11 +294,9 @@ const char* CP2MMServerPlugin::GetPluginDescription(void)
const char* (__cdecl* GetBallBotModel_orig)(bool bLowRes);
const char* __cdecl GetBallBotModel_hook(bool bLowRes)
{
return "models/player/chell/player.mdl";
return "models/player/eggbot/eggbot.mdl";

if (strcmp(GFunc::GetGameMainDir(), "portal_stories") == 0)
if (FStrEq(GFunc::GetGameMainDir(), "portal_stories"))
{
P2MMLog(0, false, "BLUE");
return "models/portal_stories/player/mel.mdl";
}

Expand All @@ -243,12 +306,9 @@ const char* __cdecl GetBallBotModel_hook(bool bLowRes)
const char* (__cdecl* GetEggBotModel_orig)(bool bLowRes);
const char* __cdecl GetEggBotModel_hook(bool bLowRes)
{
return "models/player/chell/player.mdl";
return "models/player/ballbot/ballbot.mdl";


if (strcmp(GFunc::GetGameMainDir(), "portal_stories") == 0)
if (FStrEq(GFunc::GetGameMainDir(), "portal_stories"))
{
P2MMLog(0, false, "ORANGE");
return "models/player/chell/player.mdl";
}

Expand Down Expand Up @@ -336,6 +396,14 @@ bool CP2MMServerPlugin::Load(CreateInterfaceFn interfaceFactory, CreateInterface
this->m_bNoUnload = true;
return false;
}

g_pFileSystem = (IFileSystem*)interfaceFactory(FILESYSTEM_INTERFACE_VERSION, 0);
if (!g_pFileSystem)
{
P2MMLog(1, false, "Unable to load g_pFileSystem!");
this->m_bNoUnload = true;
return false;
}

gpGlobals = playerinfomanager->GetGlobalVars();
MathLib_Init(2.2f, 2.2f, 0.0f, 2.0f);
Expand Down Expand Up @@ -417,10 +485,10 @@ bool CP2MMServerPlugin::Load(CreateInterfaceFn interfaceFactory, CreateInterface

MH_EnableHook(MH_ALL_HOOKS);

P2MMLog(0, false, "Loaded plugin!");
P2MMLog(0, false, "Loaded plugin! Horray!");
m_bPluginLoaded = true;
} catch(std::exception& ex) {
P2MMLog(0, false, "Failed to load plugin! Exception: (%s)", ex.what());
P2MMLog(0, false, "Failed to load plugin! :( Exception: (%s)", ex.what());
this->m_bNoUnload = true;
return false;
}
Expand Down Expand Up @@ -480,6 +548,7 @@ void CP2MMServerPlugin::Unload(void)
MH_Uninitialize();

m_bPluginLoaded = false;
P2MMLog(0, false, "Plugin unloaded! Goodbye!");
}

void CP2MMServerPlugin::SetCommandClient(int index)
Expand Down
12 changes: 7 additions & 5 deletions p2mm.sln
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C++ Express 2010
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "p2mm", "p2mm.vcxproj", "{531796FA-476D-47F1-84FA-CD3613D68536}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{531796FA-476D-47F1-84FA-CD3613D68536}.Debug|Win32.ActiveCfg = Debug|Win32
{531796FA-476D-47F1-84FA-CD3613D68536}.Debug|Win32.Build.0 = Debug|Win32
{531796FA-476D-47F1-84FA-CD3613D68536}.Release|Win32.ActiveCfg = Release|Win32
{531796FA-476D-47F1-84FA-CD3613D68536}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1419AF7F-D3CD-4504-8192-DB23461E2072}
EndGlobalSection
EndGlobal
Loading

0 comments on commit af6f770

Please sign in to comment.