Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
420a56c8c4 | ||
|
|
5e262c573a | ||
|
|
df4b1b0aa7 | ||
|
|
dfc25da57a | ||
|
|
4c7ec14d52 | ||
|
|
0aab8e0bd6 | ||
|
|
86c714fb1b | ||
|
|
3a126db081 | ||
|
|
0b5306f689 | ||
|
|
00c4ee1521 | ||
|
|
aa115c645e | ||
|
|
723d6e07b9 | ||
|
|
8d83d5ecca | ||
|
|
58a4c9df84 | ||
|
|
ddf6e96660 | ||
|
|
2711a463a2 | ||
|
|
736afc5d1f | ||
|
|
9aa1fb0474 | ||
|
|
670c7c5b02 | ||
|
|
a1fd39932a | ||
|
|
db47788db9 | ||
|
|
1c172cbfc7 |
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname $0)/../.."
|
||||||
|
|
||||||
|
if [[ $PLATFORM == Linux* ]]; then
|
||||||
|
$CC --version
|
||||||
|
$CXX --version
|
||||||
|
|
||||||
|
apt install -y python3-pip
|
||||||
|
|
||||||
|
# buildbot/generate_header.py is ran by ambuild and we want git to not fail due to user-perms (because docker)
|
||||||
|
git config --global --add safe.directory $PWD
|
||||||
|
else
|
||||||
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
|
fi
|
||||||
|
|
||||||
|
python -m pip install $CACHE_PATH/ambuild
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
python ../configure.py --enable-auto-versioning --enable-optimize --sdks="$SDKS" --mms-path="$CACHE_PATH/metamod-source" --hl2sdk-root="$CACHE_PATH" --sm-path="$CACHE_PATH/sourcemod"
|
||||||
|
ambuild
|
||||||
+37
-42
@@ -11,9 +11,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-22.04, windows-2022]
|
python-version: ['3.14']
|
||||||
|
os: [ubuntu-latest, windows-2022]
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-latest
|
||||||
cc: clang-11
|
cc: clang-11
|
||||||
cxx: clang++-11
|
cxx: clang++-11
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
@@ -38,32 +39,19 @@ jobs:
|
|||||||
# This will be used in our cache key
|
# This will be used in our cache key
|
||||||
echo "SDKS_KEY=${SDKS_VAR//[[:blank:]]/}" >> $GITHUB_ENV
|
echo "SDKS_KEY=${SDKS_VAR//[[:blank:]]/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Linux dependencies
|
- uses: actions/setup-python@v6
|
||||||
if: startsWith(runner.os, 'Linux')
|
if: startsWith(matrix.os, 'windows-')
|
||||||
run: |
|
name: Setup Python ${{ matrix.python-version }}
|
||||||
sudo dpkg --add-architecture i386
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
|
||||||
gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \
|
|
||||||
libc6-dev libc6-dev-i386 linux-libc-dev \
|
|
||||||
linux-libc-dev:i386 lib32z1-dev ${{ matrix.cc }}
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
name: Setup Python 3.9
|
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install Python dependencies
|
- uses: actions/checkout@v6
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip setuptools wheel
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
name: Repository checkout
|
name: Repository checkout
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: extension
|
path: extension
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v5
|
||||||
name: Cache dependencies
|
name: Cache dependencies
|
||||||
env:
|
env:
|
||||||
cache-name: connect-cache
|
cache-name: connect-cache
|
||||||
@@ -105,31 +93,38 @@ jobs:
|
|||||||
# But maybe others aren't (also probably unnecessary because git actions but paranoia)
|
# But maybe others aren't (also probably unnecessary because git actions but paranoia)
|
||||||
git config --global advice.detachedHead true
|
git config --global advice.detachedHead true
|
||||||
|
|
||||||
- name: Setup AMBuild
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd "${{ env.CACHE_PATH }}"
|
|
||||||
python -m pip install ./ambuild
|
|
||||||
|
|
||||||
- name: Select clang compiler
|
|
||||||
if: startsWith(runner.os, 'Linux')
|
|
||||||
run: |
|
|
||||||
echo "CC=${{ matrix.cc }}" >> $GITHUB_ENV
|
|
||||||
echo "CXX=${{ matrix.cxx }}" >> $GITHUB_ENV
|
|
||||||
${{ matrix.cc }} --version
|
|
||||||
${{ matrix.cxx }} --version
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: extension
|
working-directory: extension
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
export PLATFORM="${{ runner.os }}"
|
||||||
cd build
|
|
||||||
python ../configure.py --enable-auto-versioning --enable-optimize --sdks="${{ env.SDKS }}" --mms-path="${{ env.CACHE_PATH }}/metamod-source" --hl2sdk-root="${{ env.CACHE_PATH }}" --sm-path="${{ env.CACHE_PATH }}/sourcemod"
|
|
||||||
ambuild
|
|
||||||
|
|
||||||
PLATFORM="${{ runner.os }}"
|
if [[ $PLATFORM == Linux* ]]; then
|
||||||
FILENAME="$(cat ./includes/filename_versioning.txt)"
|
# docker instead of podman because:
|
||||||
|
# - we don't need a job to `sudo apt install podman`
|
||||||
|
# - `sudo apt install -y python3-pip` didn't work inside podman for me?
|
||||||
|
docker run \
|
||||||
|
-e PLATFORM="$PLATFORM" \
|
||||||
|
-e PROJECT=${{ env.PROJECT }} \
|
||||||
|
-e SDKS="${{ env.SDKS }}" \
|
||||||
|
-e MMSOURCE_VERSION=${{ env.MMSOURCE_VERSION }} \
|
||||||
|
-e SOURCEMOD_VERSION=${{ env.MMSOURCE_SOURCEMOD_VERSION }} \
|
||||||
|
-e CACHE_PATH=${{ env.CACHE_PATH }} \
|
||||||
|
-e SDKS_KEY=${{ env.SDKS_KEY }} \
|
||||||
|
-e CC=${{ matrix.cc }} \
|
||||||
|
-e CXX=${{ matrix.cxx }} \
|
||||||
|
--mount type=bind,source=${{ env.CACHE_PATH }},target=${{ env.CACHE_PATH }} \
|
||||||
|
--mount type=bind,source=$PWD,target=$PWD \
|
||||||
|
"registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest" \
|
||||||
|
"$PWD/.github/workflows/build.sh"
|
||||||
|
|
||||||
|
# need to fix the owner because docker root-ed stuff?
|
||||||
|
sudo chown -R $USER .
|
||||||
|
else
|
||||||
|
bash ./.github/workflows/build.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILENAME="$(cat build/includes/filename_versioning.txt)"
|
||||||
ZIP_FILENAME="${{ env.PROJECT }}-${FILENAME}-${PLATFORM,}.zip"
|
ZIP_FILENAME="${{ env.PROJECT }}-${FILENAME}-${PLATFORM,}.zip"
|
||||||
|
|
||||||
echo "ZIP_FILENAME=${ZIP_FILENAME}" >> $GITHUB_ENV
|
echo "ZIP_FILENAME=${ZIP_FILENAME}" >> $GITHUB_ENV
|
||||||
@@ -165,7 +160,7 @@ jobs:
|
|||||||
echo "Upload successful!"
|
echo "Upload successful!"
|
||||||
|
|
||||||
- name: Upload a Build Artifact
|
- name: Upload a Build Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
# Artifact name
|
# Artifact name
|
||||||
name: ${{ matrix.os }}_${{ matrix.cc }}_${{ env.ZIP_FILENAME }}
|
name: ${{ matrix.os }}_${{ matrix.cc }}_${{ env.ZIP_FILENAME }}
|
||||||
|
|||||||
@@ -2,3 +2,5 @@ build
|
|||||||
Containerfile
|
Containerfile
|
||||||
.venv
|
.venv
|
||||||
safetyhook
|
safetyhook
|
||||||
|
.env
|
||||||
|
secret.h
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Connect - A safer OnClientPreConnect forward
|
||||||
|
|
||||||
|
This extension provides a OnClientPreConnect forward (similar to CBaseServer's), but does proper checking to prevent malicious people spoofing SteamIDs.
|
||||||
|
|
||||||
|
If you are currently using CBaseServer for reserved slots, it's possible for a client to spoof an admin's SteamID and cause someone to be kicked from the server (although they would be later denied, so they couldn't actually join the game).
|
||||||
|
This extension does these checks before OnClientPreConnect is fired, so this isn't possible.
|
||||||
|
|
||||||
|
There are some additional features such as being able to change the password provided before it's checked (see included example plugin) and the ability to reject the client with a reason (like SourceMod's later OnClientConnect forward).
|
||||||
|
|
||||||
|
Only the Source 2009 engine is supported, as it's the only one that's been updated to use the new authentication system.
|
||||||
|
|
||||||
|
# Provided forwards
|
||||||
|
|
||||||
|
```
|
||||||
|
public bool OnClientPreConnectEx
|
||||||
|
(
|
||||||
|
const char[] name,
|
||||||
|
char password[255],
|
||||||
|
const char[] ip,
|
||||||
|
const char[] steamID,
|
||||||
|
char rejectReason[255]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`return false;` to disallow the client from joining, and change `rejectReason` to what you want them to be shown when denied.
|
||||||
|
|
||||||
|
Note that this function is called before the client has a client index on the server.
|
||||||
|
|
||||||
|
|
||||||
|
# [Builds](https://builds.limetech.io/?project=connect)
|
||||||
|
|
||||||
|
|
||||||
|
# helpful commands for building on the unloze machine
|
||||||
|
|
||||||
|
## the environment
|
||||||
|
export PATH=":/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin:/usr/X11/bin:/home/gameservers/.local/bin":/sbin:/bin:
|
||||||
|
export SOURCEMOD=/home/gameservers/automate/sourcemod_1.13.0.7207
|
||||||
|
export MMSOURCE=/home/gameservers/automate/mmsource-1.13
|
||||||
|
|
||||||
|
## building
|
||||||
|
rm -r -f build; mkdir build; cd build; python ../configure.py --sdks css --targets=x86 --enable-optimize; ambuild; cd ..
|
||||||
@@ -40,7 +40,7 @@ for cxx_task in Extension.extensions:
|
|||||||
pdblog.write(cxx_task.debug.path + '\n')
|
pdblog.write(cxx_task.debug.path + '\n')
|
||||||
pdblog.close()
|
pdblog.close()
|
||||||
|
|
||||||
CopyFiles('connect.games.txt', folders['addons/sourcemod/gamedata'])
|
CopyFiles('connect2.games.txt', folders['addons/sourcemod/gamedata'])
|
||||||
CopyFiles('connect.inc', folders['addons/sourcemod/scripting/include'])
|
CopyFiles('connect.inc', folders['addons/sourcemod/scripting/include'])
|
||||||
CopyFiles('connect.sp', folders['addons/sourcemod/scripting'])
|
CopyFiles('connect.sp', folders['addons/sourcemod/scripting'])
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ parser.options.add_argument('--enable-auto-versioning', action='store_false', de
|
|||||||
default=True, help='Enables the auto versioning script')
|
default=True, help='Enables the auto versioning script')
|
||||||
parser.options.add_argument('-s', '--sdks', default='all', dest='sdks',
|
parser.options.add_argument('-s', '--sdks', default='all', dest='sdks',
|
||||||
help='Build against specified SDKs; valid args are "all", "present", or '
|
help='Build against specified SDKs; valid args are "all", "present", or '
|
||||||
'space-delimited list of engine names (default: %default)')
|
'space-delimited list of engine names (default: %(default)s)')
|
||||||
parser.options.add_argument('--targets', type=str, dest='targets', default=None,
|
parser.options.add_argument('--targets', type=str, dest='targets', default=None,
|
||||||
help="Override the target architecture (use commas to separate multiple targets).")
|
help="Override the target architecture (use commas to separate multiple targets).")
|
||||||
parser.Configure()
|
parser.Configure()
|
||||||
+10
@@ -4,7 +4,17 @@
|
|||||||
|
|
||||||
#define _connect_included
|
#define _connect_included
|
||||||
|
|
||||||
|
enum EConnect
|
||||||
|
{
|
||||||
|
k_OnClientPreConnectEx_Reject = 0,
|
||||||
|
k_OnClientPreConnectEx_Accept = 1,
|
||||||
|
k_OnClientPreConnectEx_Async = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
forward bool OnClientPreConnectEx(const char[] name, char password[255], const char[] ip, const char[] steamID, char rejectReason[255]);
|
forward bool OnClientPreConnectEx(const char[] name, char password[255], const char[] ip, const char[] steamID, char rejectReason[255]);
|
||||||
|
native bool ClientPreConnectEx(const char[] sSteam32ID, EConnect RetVal, char sRejectReason[255]);
|
||||||
|
native bool SteamClientAuthenticated(const char[] sSteam32ID);
|
||||||
|
|
||||||
public Extension __ext_Connect =
|
public Extension __ext_Connect =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,6 +68,18 @@
|
|||||||
"windows" "\x55\x8B\xEC\x83\xEC\x1C\x53\x57\x33\xD2"
|
"windows" "\x55\x8B\xEC\x83\xEC\x1C\x53\x57\x33\xD2"
|
||||||
"windows64" "\x4C\x8B\xDC\x49\x89\x5B\x2A\x49\x89\x6B\x2A\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x60\x48\x8B\x05\x2A\x2A\x2A\x2A\x48\x8D\x1D"
|
"windows64" "\x4C\x8B\xDC\x49\x89\x5B\x2A\x49\x89\x6B\x2A\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x60\x48\x8B\x05\x2A\x2A\x2A\x2A\x48\x8D\x1D"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"CSteam3Server__OnValidateAuthTicketResponse"
|
||||||
|
{
|
||||||
|
"library" "engine"
|
||||||
|
"linux" "@_ZN13CSteam3Server28OnValidateAuthTicketResponseEP28ValidateAuthTicketResponse_t"
|
||||||
|
}
|
||||||
|
|
||||||
|
"CBaseClient__SetSteamID"
|
||||||
|
{
|
||||||
|
"library" "engine"
|
||||||
|
"linux" "@_ZN11CBaseClient10SetSteamIDERK8CSteamID"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+617
-74
@@ -19,8 +19,13 @@
|
|||||||
|
|
||||||
#include "extension.h"
|
#include "extension.h"
|
||||||
#include "CDetour/detours.h"
|
#include "CDetour/detours.h"
|
||||||
|
#include "sm_namehashset.h"
|
||||||
#include "steam/steamclientpublic.h"
|
#include "steam/steamclientpublic.h"
|
||||||
|
#include "steam/isteamclient.h"
|
||||||
|
#include <iclient.h>
|
||||||
|
#include <ISDKTools.h>
|
||||||
|
#include <iserver.h>
|
||||||
|
#include "secret.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
Connect g_connect;
|
Connect g_connect;
|
||||||
@@ -28,9 +33,19 @@ Connect g_connect;
|
|||||||
SMEXT_LINK(&g_connect);
|
SMEXT_LINK(&g_connect);
|
||||||
|
|
||||||
ConVar connectVersion("connect_version", SMEXT_CONF_VERSION, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY, SMEXT_CONF_DESCRIPTION " Version");
|
ConVar connectVersion("connect_version", SMEXT_CONF_VERSION, FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY, SMEXT_CONF_DESCRIPTION " Version");
|
||||||
|
ConVar g_SvLogging("sv_connect_logging", "0", FCVAR_NOTIFY, "Log connection checks.");
|
||||||
|
ConVar g_SvNoSteam("sv_nosteam", "0", FCVAR_NOTIFY, "Disable steam validation and force steam authentication.");
|
||||||
|
ConVar g_SvForceSteam("sv_forcesteam", "0", FCVAR_NOTIFY, "Force steam authentication.");
|
||||||
|
ConVar g_SvGameDesc("sv_gamedesc_override", "default", FCVAR_NOTIFY, "Overwrite the default game description. Set to 'default' to keep default description.");
|
||||||
|
ConVar g_SvMapName("sv_mapname_override", "default", FCVAR_NOTIFY, "Overwrite the map name. Set to 'default' to keep default name.");
|
||||||
|
ConVar g_SvSetSteamIDIPS("sv_set_steam_id_ips", "default", FCVAR_NOTIFY, "IP's allowed for webclient nosteamers");
|
||||||
|
ConVar g_SvWebclientName("sv_webclient_name", "", FCVAR_NOTIFY, "Generated name for the currently connecting webclient player.");
|
||||||
|
|
||||||
|
|
||||||
IGameConfig *g_pGameConf = NULL;
|
IGameConfig *g_pGameConf = NULL;
|
||||||
IForward *g_pConnectForward = NULL;
|
IForward *g_pConnectForward = NULL;
|
||||||
|
IServer *iserver = NULL;
|
||||||
|
ISDKTools *g_pSDKTools = NULL;
|
||||||
|
|
||||||
class IClient;
|
class IClient;
|
||||||
class CBaseServer;
|
class CBaseServer;
|
||||||
@@ -42,34 +57,14 @@ typedef enum EAuthProtocol
|
|||||||
k_EAuthProtocolSteam = 3,
|
k_EAuthProtocolSteam = 3,
|
||||||
} EAuthProtocol;
|
} EAuthProtocol;
|
||||||
|
|
||||||
#if SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2
|
/*
|
||||||
typedef enum EBeginAuthSessionResult
|
|
||||||
{
|
|
||||||
k_EBeginAuthSessionResultOK = 0, // Ticket is valid for this game and this steamID.
|
k_EBeginAuthSessionResultOK = 0, // Ticket is valid for this game and this steamID.
|
||||||
k_EBeginAuthSessionResultInvalidTicket = 1, // Ticket is not valid.
|
k_EBeginAuthSessionResultInvalidTicket = 1, // Ticket is not valid.
|
||||||
k_EBeginAuthSessionResultDuplicateRequest = 2, // A ticket has already been submitted for this steamID
|
k_EBeginAuthSessionResultDuplicateRequest = 2, // A ticket has already been submitted for this steamID
|
||||||
k_EBeginAuthSessionResultInvalidVersion = 3, // Ticket is from an incompatible interface version
|
k_EBeginAuthSessionResultInvalidVersion = 3, // Ticket is from an incompatible interface version
|
||||||
k_EBeginAuthSessionResultGameMismatch = 4, // Ticket is not for this game
|
k_EBeginAuthSessionResultGameMismatch = 4, // Ticket is not for this game
|
||||||
k_EBeginAuthSessionResultExpiredTicket = 5, // Ticket has expired
|
k_EBeginAuthSessionResultExpiredTicket = 5, // Ticket has expired
|
||||||
} EBeginAuthSessionResult;
|
*/
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct netadr_s
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
NA_NULL = 0,
|
|
||||||
NA_LOOPBACK,
|
|
||||||
NA_BROADCAST,
|
|
||||||
NA_IP,
|
|
||||||
} netadrtype_t;
|
|
||||||
|
|
||||||
public:
|
|
||||||
netadrtype_t type;
|
|
||||||
unsigned char ip[4];
|
|
||||||
unsigned short port;
|
|
||||||
} netadr_t;
|
|
||||||
|
|
||||||
const char *CSteamID::Render() const
|
const char *CSteamID::Render() const
|
||||||
{
|
{
|
||||||
@@ -157,10 +152,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
CDetour* detourCBaseServer__ConnectClient = nullptr;
|
CDetour* detourCBaseServer__ConnectClient = nullptr;
|
||||||
bool g_bSuppressBeginAuthSession = false;
|
CDetour *g_Detour_CSteam3Server__OnValidateAuthTicketResponse = NULL;
|
||||||
CSteamID g_lastClientSteamID;
|
CDetour *g_Detour_CBaseClient__SetSteamID = NULL;
|
||||||
const void* g_lastAuthTicket;
|
|
||||||
int g_lastcbAuthTicket;
|
|
||||||
|
|
||||||
char passwordBuffer[255];
|
char passwordBuffer[255];
|
||||||
|
|
||||||
@@ -177,27 +171,254 @@ CSteam3Server *Steam3Server()
|
|||||||
return g_pSteam3ServerFunc();
|
return g_pSteam3ServerFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ValidateAuthTicketResponse_t
|
||||||
|
{
|
||||||
|
enum { k_iCallback = k_iSteamUserCallbacks + 43 };
|
||||||
|
CSteamID m_SteamID;
|
||||||
|
EAuthSessionResponse m_eAuthSessionResponse;
|
||||||
|
CSteamID m_OwnerSteamID; // different from m_SteamID if borrowed
|
||||||
|
//in dewey voice "can a nigga borrow a m_SteamID?"
|
||||||
|
//the teacher "How is a nigga gonna borrow a m_SteamID? nigga is you gonna give it back?"
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConnectClientStorage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void* pThis;
|
||||||
|
|
||||||
|
netadr_t address;
|
||||||
|
int nProtocol;
|
||||||
|
int iChallenge;
|
||||||
|
int iClientChallenge;
|
||||||
|
int nAuthProtocol;
|
||||||
|
char pchName[256];
|
||||||
|
char pchPassword[256];
|
||||||
|
char pCookie[256];
|
||||||
|
int cbCookie;
|
||||||
|
IClient *pClient;
|
||||||
|
|
||||||
|
uint64 ullSteamID;
|
||||||
|
ValidateAuthTicketResponse_t ValidateAuthTicketResponse;
|
||||||
|
bool GotValidateAuthTicketResponse;
|
||||||
|
bool SteamLegal;
|
||||||
|
bool SteamAuthFailed;
|
||||||
|
bool ManualRetry;
|
||||||
|
bool Webclient;
|
||||||
|
bool SuppressBeginAuthSession;
|
||||||
|
|
||||||
|
void *pvTicket;
|
||||||
|
int cbTicket;
|
||||||
|
|
||||||
|
ConnectClientStorage() { }
|
||||||
|
ConnectClientStorage(netadr_t address, int nProtocol, int iChallenge, int iClientChallenge, int nAuthProtocol, const char *pchName, const char *pchPassword, const char *pCookie, int cbCookie)
|
||||||
|
{
|
||||||
|
this->address = address;
|
||||||
|
this->nProtocol = nProtocol;
|
||||||
|
this->iChallenge = iChallenge;
|
||||||
|
this->iClientChallenge = iClientChallenge;
|
||||||
|
this->nAuthProtocol = nAuthProtocol;
|
||||||
|
|
||||||
|
V_strncpy(this->pchName, pchName, 255);
|
||||||
|
V_strncpy(this->pchPassword, pchPassword, 255);
|
||||||
|
memcpy(this->pCookie, pCookie, cbCookie);
|
||||||
|
|
||||||
|
this->cbCookie = cbCookie;
|
||||||
|
this->pClient = NULL;
|
||||||
|
this->GotValidateAuthTicketResponse = false;
|
||||||
|
this->SteamLegal = false;
|
||||||
|
this->SteamAuthFailed = false;
|
||||||
|
this->ManualRetry = false;
|
||||||
|
this->Webclient = false;
|
||||||
|
this->SuppressBeginAuthSession = false;
|
||||||
|
|
||||||
|
// Calculate and store the ticket pointer
|
||||||
|
this->pvTicket = (void *)((intptr_t)this->pCookie + sizeof(uint64));
|
||||||
|
this->cbTicket = cbCookie - sizeof(uint64);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
StringHashMap<ConnectClientStorage> g_ConnectClientStorage;
|
||||||
|
|
||||||
SH_DECL_MANUALHOOK3(MHook_BeginAuthSession, 0, 0, 0, EBeginAuthSessionResult, const void *, int, CSteamID);
|
SH_DECL_MANUALHOOK3(MHook_BeginAuthSession, 0, 0, 0, EBeginAuthSessionResult, const void *, int, CSteamID);
|
||||||
EBeginAuthSessionResult Hook_BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID)
|
EBeginAuthSessionResult Hook_BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID)
|
||||||
{
|
{
|
||||||
if (!g_bSuppressBeginAuthSession)
|
ConnectClientStorage Storage;
|
||||||
|
g_ConnectClientStorage.retrieve(steamID.Render(), &Storage);
|
||||||
|
if (!Storage.SuppressBeginAuthSession)
|
||||||
{
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "inside Hook_ BeginAuthSession 1. %s", steamID.Render());
|
||||||
RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultOK);
|
RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultOK);
|
||||||
}
|
}
|
||||||
g_bSuppressBeginAuthSession = false;
|
Storage.SuppressBeginAuthSession = false;
|
||||||
|
g_ConnectClientStorage.replace(steamID.Render(), Storage);
|
||||||
|
|
||||||
if (strcmp(steamID.Render(), g_lastClientSteamID.Render()) == 0
|
//g_pSM->LogMessage(myself, "inside Hook_ BeginAuthSession 2. %s", steamID.Render());
|
||||||
&& g_lastAuthTicket == pAuthTicket
|
|
||||||
&& g_lastcbAuthTicket == cbAuthTicket)
|
|
||||||
{
|
|
||||||
// Let the server know everything is fine
|
|
||||||
// g_pSM->LogMessage(myself, "You alright ;)");
|
|
||||||
RETURN_META_VALUE(MRES_SUPERCEDE, k_EBeginAuthSessionResultOK);
|
RETURN_META_VALUE(MRES_SUPERCEDE, k_EBeginAuthSessionResultOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_META_VALUE(MRES_IGNORED, k_EBeginAuthSessionResultDuplicateRequest);
|
|
||||||
|
//ai generated function
|
||||||
|
uint64 GenerateSteamIDFromName(const char *pchName)
|
||||||
|
{
|
||||||
|
uint64 hash = 14695981039346656037ULL;
|
||||||
|
for (const char *p = pchName; *p; p++)
|
||||||
|
{
|
||||||
|
hash ^= (uint64)(unsigned char)*p;
|
||||||
|
hash *= 1099511628211ULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 W = hash & 1;
|
||||||
|
// Range within existing Steam accounts: 1 to 700,000,000
|
||||||
|
uint32 Z = 1 + (uint32)((hash >> 1) % 699999999);
|
||||||
|
|
||||||
|
uint64 steamID64 = 0x0110000100000000ULL | ((uint64)Z << 1) | W;
|
||||||
|
return steamID64;
|
||||||
|
}
|
||||||
|
|
||||||
|
//AI generated function
|
||||||
|
static uint32_t HashString(const char *str)
|
||||||
|
{
|
||||||
|
uint32_t hash = 5381;
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
hash = ((hash << 5) + hash) + (unsigned char)*str;
|
||||||
|
hash = hash & 0x7fffffff;
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
//AI generated function
|
||||||
|
void GenerateNickNameFromPassword(const char *pchPassword, const char *salt, char *outName, size_t outSize)
|
||||||
|
{
|
||||||
|
static const char *adjectives[] = {
|
||||||
|
"Fast","Slow","Red","Blue","Green","Dark","Bright","Wild",
|
||||||
|
"Cool","Hot","Big","Small","Sharp","Brave","Swift","Bold"
|
||||||
|
};
|
||||||
|
static const char *nouns[] = {
|
||||||
|
"Wolf","Eagle","Tiger","Bear","Fox","Lion","Hawk","Shark",
|
||||||
|
"Snake","Raven","Storm","Rock","Fire","Ice","Wind","Steel"
|
||||||
|
};
|
||||||
|
|
||||||
|
char salted[256];
|
||||||
|
V_snprintf(salted, sizeof(salted), "%s%s", pchPassword, salt);
|
||||||
|
uint32_t h1 = HashString(salted);
|
||||||
|
|
||||||
|
char salted1[256];
|
||||||
|
V_snprintf(salted1, sizeof(salted1), "%s1", salted);
|
||||||
|
uint32_t h2 = HashString(salted1);
|
||||||
|
|
||||||
|
char salted2[256];
|
||||||
|
V_snprintf(salted2, sizeof(salted2), "%s2", salted);
|
||||||
|
uint32_t h3 = HashString(salted2);
|
||||||
|
|
||||||
|
V_snprintf(outName, outSize, "%s %s %u",
|
||||||
|
adjectives[h1 % 16],
|
||||||
|
nouns[h2 % 16],
|
||||||
|
h3 % 9999
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(CBaseClient__SetSteamID, void, const CSteamID &, steamID)
|
||||||
|
{
|
||||||
|
char aSteamID[32];
|
||||||
|
V_strncpy(aSteamID, steamID.Render(), 32);
|
||||||
|
ConnectClientStorage storage;
|
||||||
|
if (g_ConnectClientStorage.retrieve(aSteamID, &storage))
|
||||||
|
{
|
||||||
|
if (storage.SteamAuthFailed)
|
||||||
|
{
|
||||||
|
char IpsAllowed[255];
|
||||||
|
V_strncpy(IpsAllowed, g_SvSetSteamIDIPS.GetString(), 255);
|
||||||
|
|
||||||
|
char ipString[30];
|
||||||
|
V_snprintf(ipString, sizeof(ipString), "%u.%u.%u.%u", storage.address.ip[0], storage.address.ip[1], storage.address.ip[2], storage.address.ip[3]);
|
||||||
|
|
||||||
|
if (g_SvLogging.GetInt())
|
||||||
|
smutils->LogMessage(myself, "inside SetSteamID steamAuthFailed. IpsAllowed: %s. ipString: %s", IpsAllowed, ipString);
|
||||||
|
//connects from the IP's designated for the webclients.
|
||||||
|
if (strcmp(IpsAllowed, ipString) == 0)
|
||||||
|
{
|
||||||
|
//changes the storages pchName
|
||||||
|
GenerateNickNameFromPassword(storage.pchPassword, NAME_SALT, storage.pchName, sizeof(storage.pchName));
|
||||||
|
uint64 steamID64 = GenerateSteamIDFromName(storage.pchName);
|
||||||
|
CSteamID newSteamID = CSteamID(steamID64);
|
||||||
|
//smutils->LogMessage(myself, "Generated SteamID64: %llu for name: %s", steamID64, storage.pchName);
|
||||||
|
//smutils->LogMessage(myself, "SteamID render: %s. password. %s", newSteamID.Render(), storage.pchPassword);
|
||||||
|
|
||||||
|
storage.ullSteamID = steamID64;
|
||||||
|
storage.Webclient = true;
|
||||||
|
|
||||||
|
g_ConnectClientStorage.replace(aSteamID, storage);
|
||||||
|
DETOUR_MEMBER_CALL(CBaseClient__SetSteamID)(newSteamID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DETOUR_MEMBER_CALL(CBaseClient__SetSteamID)(steamID);
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(CSteam3Server__OnValidateAuthTicketResponse, int, ValidateAuthTicketResponse_t *, pResponse)
|
||||||
|
{
|
||||||
|
char aSteamID[32];
|
||||||
|
V_strncpy(aSteamID, pResponse->m_SteamID.Render(), 32);
|
||||||
|
bool SteamLegal = pResponse->m_eAuthSessionResponse == k_EAuthSessionResponseOK;
|
||||||
|
bool force = g_SvNoSteam.GetInt() || g_SvForceSteam.GetInt();
|
||||||
|
|
||||||
|
if(!SteamLegal && force)
|
||||||
|
{
|
||||||
|
pResponse->m_eAuthSessionResponse = k_EAuthSessionResponseOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectClientStorage Storage;
|
||||||
|
if (g_ConnectClientStorage.retrieve(aSteamID, &Storage))
|
||||||
|
{
|
||||||
|
if(!Storage.GotValidateAuthTicketResponse)
|
||||||
|
{
|
||||||
|
Storage.GotValidateAuthTicketResponse = true;
|
||||||
|
Storage.ValidateAuthTicketResponse = *pResponse;
|
||||||
|
Storage.SteamLegal = SteamLegal;
|
||||||
|
g_ConnectClientStorage.replace(aSteamID, Storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
//2026 march 17th: resolve (STEAM UserID [xxxxxx] is already in use on this server)
|
||||||
|
//we handle it here in the detour before CheckForDuplicateSteamID() is called.
|
||||||
|
//confirm that this client actually did a manual retry
|
||||||
|
if (Storage.ManualRetry)
|
||||||
|
{
|
||||||
|
if (!Storage.pClient || !Storage.pClient->IsConnected())
|
||||||
|
{
|
||||||
|
return DETOUR_MEMBER_CALL(CSteam3Server__OnValidateAuthTicketResponse)(pResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
USERID_t clientID = Storage.pClient->GetNetworkID();
|
||||||
|
//two scenarios related to nosteam trying to spoof steam players.
|
||||||
|
//1: nosteamer trying to spoof active steam player on the server. should already be handled by MEMBER9 function comparing IP address and rejecting if nosteam
|
||||||
|
//2: nosteamer trying to spoof steam player not on the server right now. handled by PlayerManager_Connection kicking nosteamers with message "Trying to join with a legitimate steamid while not authenticated with steam."
|
||||||
|
for (int i = 0; i < iserver->GetClientCount(); i++)
|
||||||
|
{
|
||||||
|
IClient* pSlot = iserver->GetClient(i);
|
||||||
|
if (pSlot && pSlot->IsConnected() && pSlot != Storage.pClient)
|
||||||
|
{
|
||||||
|
USERID_t slotID = pSlot->GetNetworkID();
|
||||||
|
if (slotID.idtype == IDTYPE_STEAM && clientID.idtype == IDTYPE_STEAM)
|
||||||
|
{
|
||||||
|
if (slotID.steamid == clientID.steamid)
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "Disconnecting idle left over session for steamID: %s", aSteamID);
|
||||||
|
pSlot->Disconnect("Disconnecting dead left over session.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DETOUR_MEMBER_CALL(CSteam3Server__OnValidateAuthTicketResponse)(pResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *g_pLastHookedSteamGameServer = NULL;
|
||||||
|
int g_nBeginAuthSessionOffset = 0;
|
||||||
|
|
||||||
DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient*, netadr_t&, address, int, nProtocol, int, iChallenge, int, iClientChallenge, int, nAuthProtocol, const char *, pchName, const char *, pchPassword, const char *, pCookie, int, cbCookie)
|
DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient*, netadr_t&, address, int, nProtocol, int, iChallenge, int, iClientChallenge, int, nAuthProtocol, const char *, pchName, const char *, pchPassword, const char *, pCookie, int, cbCookie)
|
||||||
{
|
{
|
||||||
if (nAuthProtocol != k_EAuthProtocolSteam)
|
if (nAuthProtocol != k_EAuthProtocolSteam)
|
||||||
@@ -206,6 +427,35 @@ DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient*, netadr_t&, address, in
|
|||||||
return DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie);
|
return DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//g_pSM->LogMessage(myself, "entered MEMBER9");
|
||||||
|
|
||||||
|
if (g_pSteam3Server->m_pSteamGameServer != g_pLastHookedSteamGameServer)
|
||||||
|
{
|
||||||
|
//the hook tends to die, hence its reapplied whenever needed. thats probably very dumb.
|
||||||
|
//does however seem to stop "S3: Client connected with invalid ticket:" for nosteamers.
|
||||||
|
//g_pSM->LogMessage(myself, "Steam GameServer pointer changed! Old: %p, New: %p", g_pLastHookedSteamGameServer, g_pSteam3Server->m_pSteamGameServer);
|
||||||
|
if (g_pLastHookedSteamGameServer != NULL)
|
||||||
|
{
|
||||||
|
// Remove old hook if it exists
|
||||||
|
SH_REMOVE_MANUALHOOK(MHook_BeginAuthSession, g_pLastHookedSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add hook to the new object
|
||||||
|
SH_MANUALHOOK_RECONFIGURE(MHook_BeginAuthSession, g_nBeginAuthSessionOffset, 0, 0);
|
||||||
|
SH_ADD_MANUALHOOK(MHook_BeginAuthSession, g_pSteam3Server->m_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true);
|
||||||
|
|
||||||
|
void** vtable = *((void***)g_pSteam3Server->m_pSteamGameServer);
|
||||||
|
g_pBeginAuthSession.SetAddress(vtable[g_nBeginAuthSessionOffset]);
|
||||||
|
|
||||||
|
int endAuthOffset = 0;
|
||||||
|
if (g_pGameConf->GetOffset("ISteamGameServer__EndAuthSession", &endAuthOffset))
|
||||||
|
{
|
||||||
|
g_pEndAuthSession.SetAddress(vtable[endAuthOffset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pLastHookedSteamGameServer = g_pSteam3Server->m_pSteamGameServer;
|
||||||
|
}
|
||||||
|
|
||||||
if (pCookie == NULL || (size_t)cbCookie < sizeof(uint64))
|
if (pCookie == NULL || (size_t)cbCookie < sizeof(uint64))
|
||||||
{
|
{
|
||||||
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectInvalidSteamCertLen");
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectInvalidSteamCertLen");
|
||||||
@@ -216,62 +466,244 @@ DETOUR_DECL_MEMBER9(CBaseServer__ConnectClient, IClient*, netadr_t&, address, in
|
|||||||
|
|
||||||
char ipString[30];
|
char ipString[30];
|
||||||
V_snprintf(ipString, sizeof(ipString), "%u.%u.%u.%u", address.ip[0], address.ip[1], address.ip[2], address.ip[3]);
|
V_snprintf(ipString, sizeof(ipString), "%u.%u.%u.%u", address.ip[0], address.ip[1], address.ip[2], address.ip[3]);
|
||||||
|
|
||||||
|
char IpsAllowed[255];
|
||||||
|
V_strncpy(IpsAllowed, g_SvSetSteamIDIPS.GetString(), 255);
|
||||||
|
//you think i am kidding but without this extra log message will ipString end up being empty for nosteamers.
|
||||||
|
//probably because of netadr_t& address not being processed yet as is. So these logging messages add enough delay.
|
||||||
|
smutils->LogMessage(myself, "IpsAllowed: %s. ipString: %s", IpsAllowed, ipString);
|
||||||
|
smutils->LogMessage(myself, "IpsAllowed: %s. ipString: %s", IpsAllowed, ipString);
|
||||||
|
smutils->LogMessage(myself, "IpsAllowed: %s. ipString: %s", IpsAllowed, ipString);
|
||||||
|
|
||||||
V_strncpy(passwordBuffer, pchPassword, 255);
|
V_strncpy(passwordBuffer, pchPassword, 255);
|
||||||
uint64 ullSteamID = *(uint64 *)pCookie;
|
uint64 ullSteamID = *(uint64 *)pCookie;
|
||||||
|
|
||||||
void *pvTicket = (void *)((intptr_t)pCookie + sizeof(uint64));
|
CSteamID ClientSteamID = CSteamID(ullSteamID);
|
||||||
int cbTicket = cbCookie - sizeof(uint64);
|
char aSteamID[32];
|
||||||
|
V_strncpy(aSteamID, ClientSteamID.Render(), 32);
|
||||||
|
|
||||||
g_lastClientSteamID = CSteamID(ullSteamID);
|
// If client is in async state remove the old object and fake an async retVal
|
||||||
g_lastcbAuthTicket = cbTicket;
|
// This can happen if the async ClientPreConnectEx takes too long to be called
|
||||||
g_lastAuthTicket = pvTicket;
|
// and the client auto-retries.
|
||||||
|
bool AsyncWaiting = false;
|
||||||
|
bool ExistingSteamid = false;
|
||||||
|
EBeginAuthSessionResult result;
|
||||||
|
ConnectClientStorage Storage(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie);
|
||||||
|
|
||||||
|
if (g_ConnectClientStorage.retrieve(aSteamID, &Storage))
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "entered has clientstorage. %s", aSteamID);
|
||||||
|
ExistingSteamid = true;
|
||||||
|
g_ConnectClientStorage.remove(aSteamID);
|
||||||
|
|
||||||
|
// Only wait for async on auto-retry, manual retries should go through the full chain
|
||||||
|
// Don't want to leave the client waiting forever if something breaks in the async forward
|
||||||
|
if (Storage.iClientChallenge == iClientChallenge)
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "entered async retry. %s", aSteamID);
|
||||||
|
AsyncWaiting = true;
|
||||||
|
|
||||||
|
//reject async nosteamers or async steam player who is not authenticated
|
||||||
|
if (Storage.SteamAuthFailed)
|
||||||
|
{
|
||||||
|
//we are only here in async state when server is 64/64.
|
||||||
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "No slots for nosteamers. server is full");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
//async steam player with a clientstorage, use the ticket from the previous clientstorage.
|
||||||
|
result = g_pBeginAuthSession(steamGameServer, Storage.pvTicket, Storage.cbTicket, ClientSteamID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//client did a manual retry,
|
||||||
|
//create new auth storage with fresh ticket and challenge
|
||||||
|
Storage = ConnectClientStorage(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie);
|
||||||
|
//g_pSM->LogMessage(myself, "entered manual retry. %s", aSteamID);
|
||||||
|
result = g_pBeginAuthSession(steamGameServer, Storage.pvTicket, Storage.cbTicket, ClientSteamID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = g_pBeginAuthSession(steamGameServer, Storage.pvTicket, Storage.cbTicket, ClientSteamID);
|
||||||
|
//g_pSM->LogMessage(myself, "without client storage after beginauth session. %s", aSteamID);
|
||||||
|
}
|
||||||
|
|
||||||
|
//g_pSM->LogMessage(myself, "about to do endAuthSession. %s", aSteamID);
|
||||||
|
g_pEndAuthSession(steamGameServer, ClientSteamID); //xen said engine might start its own authsession, so end ours here.
|
||||||
|
|
||||||
|
bool NoSteam = g_SvNoSteam.GetInt();
|
||||||
|
bool SteamAuthFailed = false;
|
||||||
|
//g_pSM->LogMessage(myself, "g_pBeginAuthSession result: %d. %s", result, aSteamID);
|
||||||
|
|
||||||
// Validate steam ticket
|
|
||||||
EBeginAuthSessionResult result = g_pBeginAuthSession(steamGameServer, pvTicket, cbTicket, g_lastClientSteamID);
|
|
||||||
if (result != k_EBeginAuthSessionResultOK)
|
if (result != k_EBeginAuthSessionResultOK)
|
||||||
|
{
|
||||||
|
if (!NoSteam)
|
||||||
{
|
{
|
||||||
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectSteam");
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "#GameUI_ServerRejectSteam");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Storage.SteamAuthFailed = SteamAuthFailed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ExistingSteamid && !AsyncWaiting)
|
||||||
|
{
|
||||||
|
Storage.ManualRetry = true;
|
||||||
|
// Another player trying to spoof a Steam ID or game crashed?
|
||||||
|
if(memcmp(address.ip, Storage.address.ip, sizeof(address.ip)) != 0)
|
||||||
|
{
|
||||||
|
// Is a nosteam player
|
||||||
|
if(SteamAuthFailed)
|
||||||
|
{
|
||||||
|
//if the nosteam player is trying to spoof a steam player reject the nosteamer.
|
||||||
|
if (!Storage.SteamAuthFailed)
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "about to do rejectconnection 1. %s", aSteamID);
|
||||||
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Steam ID already in use.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
//a nosteamer connecting the first time could be blocked by their ISP and then on the second connect be using a VPN
|
||||||
|
//so if the original nosteamer is not connected and ingame we allow the nosteamer to take over the spot
|
||||||
|
//a nosteamer spoofing another nosteamer that is not ingame is fine too.
|
||||||
|
if (Storage.pClient && Storage.pClient->IsConnected() && Storage.pClient->IsActive())
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "about to do rejectconnection 2. %s", aSteamID);
|
||||||
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Steam ID already in use.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kick existing player
|
||||||
|
if(Storage.pClient)
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "about to do disconnect. %s", aSteamID);
|
||||||
|
Storage.pClient->Disconnect("Same Steam ID connected.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//g_pSM->LogMessage(myself, "about to do rejectconnection 3. %s", aSteamID);
|
||||||
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Please try again later.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char rejectReason[255];
|
char rejectReason[255];
|
||||||
|
cell_t retVal = 1;
|
||||||
|
|
||||||
|
if(AsyncWaiting)
|
||||||
|
{
|
||||||
|
retVal = -1; // Fake async return code when waiting for async call
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
g_pConnectForward->PushString(pchName);
|
g_pConnectForward->PushString(pchName);
|
||||||
g_pConnectForward->PushStringEx(passwordBuffer, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
g_pConnectForward->PushStringEx(passwordBuffer, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
||||||
g_pConnectForward->PushString(ipString);
|
g_pConnectForward->PushString(ipString);
|
||||||
g_pConnectForward->PushString(g_lastClientSteamID.Render());
|
g_pConnectForward->PushString(ClientSteamID.Render());
|
||||||
g_pConnectForward->PushStringEx(rejectReason, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
g_pConnectForward->PushStringEx(rejectReason, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
|
||||||
|
|
||||||
cell_t retVal = 1;
|
|
||||||
g_pConnectForward->Execute(&retVal);
|
g_pConnectForward->Execute(&retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
pchPassword = passwordBuffer;
|
||||||
|
//overwrite it to the storage so the webclient can use its value assigned by nosteam celt plugin inside the SetSteamID detour.
|
||||||
|
V_strncpy(Storage.pchPassword, passwordBuffer, sizeof(Storage.pchPassword));
|
||||||
|
// k_OnClientPreConnectEx_Reject
|
||||||
if (retVal == 0)
|
if (retVal == 0)
|
||||||
{
|
{
|
||||||
g_pEndAuthSession(steamGameServer, g_lastClientSteamID);
|
g_ConnectClientStorage.remove(aSteamID);
|
||||||
|
if (!Storage.SteamAuthFailed)
|
||||||
|
{
|
||||||
|
//calling this on nosteamers does sometimes cause a server explosion.
|
||||||
|
//but not when creating a nosteamer with their first ticket
|
||||||
|
//g_pSM->LogMessage(myself, "k_OnClientPreConnectEx_Reject reached about to do endAuthSession. steamID: %s", aSteamID);
|
||||||
|
g_pEndAuthSession(steamGameServer, ClientSteamID);
|
||||||
|
}
|
||||||
|
//g_pSM->LogMessage(myself, "k_OnClientPreConnectEx_Reject reached about to do g_pRejectConnectionFunc. steamID: %s", aSteamID);
|
||||||
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, rejectReason);
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, rejectReason);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pchPassword = passwordBuffer;
|
Storage.pThis = this;
|
||||||
|
Storage.ullSteamID = ullSteamID;
|
||||||
|
Storage.SteamAuthFailed = SteamAuthFailed;
|
||||||
|
|
||||||
g_bSuppressBeginAuthSession = true;
|
//puts the storage in the StringHashMap
|
||||||
auto client = DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie);
|
if(!g_ConnectClientStorage.replace(aSteamID, Storage))
|
||||||
g_bSuppressBeginAuthSession = false;
|
|
||||||
if (client == nullptr)
|
|
||||||
{
|
{
|
||||||
g_pEndAuthSession(steamGameServer, g_lastClientSteamID);
|
g_pRejectConnectionFunc((CBaseServer*)this, address, iClientChallenge, "Internal error.");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// k_OnClientPreConnectEx_Async
|
||||||
|
if (retVal == -1)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// k_OnClientPreConnectEx_Accept
|
||||||
|
//g_pSM->LogMessage(myself, "about to do DETOUR_MEMBER_CALL. %s", aSteamID);
|
||||||
|
|
||||||
|
Storage.SuppressBeginAuthSession = true;
|
||||||
|
g_ConnectClientStorage.replace(aSteamID, Storage);
|
||||||
|
|
||||||
|
auto client = DETOUR_MEMBER_CALL(CBaseServer__ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, pchPassword, pCookie, cbCookie);
|
||||||
|
|
||||||
|
|
||||||
|
//g_pSM->LogMessage(myself, "finished DETOUR_MEMBER_CALL. %s", aSteamID);
|
||||||
|
|
||||||
|
//doing another retrieve in case of SetSteamID for webclient.
|
||||||
|
ConnectClientStorage storage_setSteamID;
|
||||||
|
g_ConnectClientStorage.retrieve(aSteamID, &storage_setSteamID);
|
||||||
|
if (storage_setSteamID.Webclient)
|
||||||
|
{
|
||||||
|
storage_setSteamID.SuppressBeginAuthSession = false;
|
||||||
|
CSteamID newSteamID = CSteamID(storage_setSteamID.ullSteamID);
|
||||||
|
//smutils->LogMessage(myself, "Generated name after webclient check: %s", storage_setSteamID.pchName);
|
||||||
|
g_SvWebclientName.SetValue(storage_setSteamID.pchName);
|
||||||
|
storage_setSteamID.pClient = client;
|
||||||
|
|
||||||
|
//smutils->LogMessage(myself, "SteamID render after webclient check: %s", newSteamID.Render());
|
||||||
|
g_ConnectClientStorage.replace(newSteamID.Render(), storage_setSteamID);
|
||||||
|
g_ConnectClientStorage.remove(aSteamID);
|
||||||
|
|
||||||
|
if (client) //webclient is always nosteamer.
|
||||||
|
{
|
||||||
|
ValidateAuthTicketResponse_t Response;
|
||||||
|
Response.m_SteamID = newSteamID;
|
||||||
|
Response.m_eAuthSessionResponse = k_EAuthSessionResponseAuthTicketInvalid; //nosteamer monkeys
|
||||||
|
Response.m_OwnerSteamID = Response.m_SteamID;
|
||||||
|
//g_pSM->LogMessage(myself, "about to do OnValidateAuthTicketResponse webclient. %s", newSteamID.Render());
|
||||||
|
DETOUR_MEMBER_MCALL_CALLBACK(CSteam3Server__OnValidateAuthTicketResponse, g_pSteam3Server)(&Response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Storage.SuppressBeginAuthSession = false;
|
||||||
|
Storage.pClient = client;
|
||||||
|
g_ConnectClientStorage.replace(aSteamID, Storage);
|
||||||
|
|
||||||
|
if (client && SteamAuthFailed)
|
||||||
|
{
|
||||||
|
ValidateAuthTicketResponse_t Response;
|
||||||
|
Response.m_SteamID = ClientSteamID;
|
||||||
|
Response.m_eAuthSessionResponse = k_EAuthSessionResponseAuthTicketInvalid; //nosteamer monkeys
|
||||||
|
Response.m_OwnerSteamID = Response.m_SteamID;
|
||||||
|
//g_pSM->LogMessage(myself, "about to do OnValidateAuthTicketResponse. %s", aSteamID);
|
||||||
|
DETOUR_MEMBER_MCALL_CALLBACK(CSteam3Server__OnValidateAuthTicketResponse, g_pSteam3Server)(&Response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//g_pSM->LogMessage(myself, "MEMBER 9 end. %s", aSteamID);
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
||||||
{
|
{
|
||||||
char conf_error[255] = "";
|
char conf_error[255] = "";
|
||||||
if (!gameconfs->LoadGameConfigFile("connect.games", &g_pGameConf, conf_error, sizeof(conf_error)))
|
if (!gameconfs->LoadGameConfigFile("connect2.games", &g_pGameConf, conf_error, sizeof(conf_error)))
|
||||||
{
|
{
|
||||||
if (conf_error[0])
|
if (conf_error[0])
|
||||||
{
|
{
|
||||||
snprintf(error, maxlen, "Could not read connect.games.txt: %s\n", conf_error);
|
snprintf(error, maxlen, "Could not read connect2.games.txt: %s\n", conf_error);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -324,24 +756,24 @@ bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
META_CONPRINTF("ISteamGameServer: %p\n", g_pSteam3Server->m_pSteamGameServer);
|
|
||||||
META_CONPRINTF("ISteamUtils: %p\n", g_pSteam3Server->m_pSteamGameServerUtils);
|
|
||||||
META_CONPRINTF("ISteamMasterServerUpdater: %p\n", g_pSteam3Server->m_pSteamMasterServerUpdater);
|
|
||||||
META_CONPRINTF("ISteamNetworking: %p\n", g_pSteam3Server->m_pSteamGameServerNetworking);
|
|
||||||
META_CONPRINTF("ISteamGameServerStats: %p\n", g_pSteam3Server->m_pSteamGameServerStats);
|
|
||||||
*/
|
|
||||||
void** vtable = *((void***)g_pSteam3Server->m_pSteamGameServer);
|
void** vtable = *((void***)g_pSteam3Server->m_pSteamGameServer);
|
||||||
|
|
||||||
int offset = 0;
|
if (!g_pGameConf->GetOffset("ISteamGameServer__BeginAuthSession", &g_nBeginAuthSessionOffset) || g_nBeginAuthSessionOffset == 0)
|
||||||
if (!g_pGameConf->GetOffset("ISteamGameServer__BeginAuthSession", &offset) || offset == 0)
|
|
||||||
{
|
{
|
||||||
snprintf(error, maxlen, "Failed to find ISteamGameServer__BeginAuthSession offset.\n");
|
snprintf(error, maxlen, "Failed to find ISteamGameServer__BeginAuthSession offset.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
g_pBeginAuthSession.SetAddress(vtable[offset]);
|
g_pBeginAuthSession.SetAddress(vtable[g_nBeginAuthSessionOffset]);
|
||||||
|
|
||||||
offset = 0;
|
SH_MANUALHOOK_RECONFIGURE(MHook_BeginAuthSession, g_nBeginAuthSessionOffset, 0, 0);
|
||||||
|
if (SH_ADD_MANUALHOOK(MHook_BeginAuthSession, g_pSteam3Server->m_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true) == 0)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "Failed to setup ISteamGameServer__BeginAuthSession hook.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_pLastHookedSteamGameServer = g_pSteam3Server->m_pSteamGameServer;
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
if (!g_pGameConf->GetOffset("ISteamGameServer__EndAuthSession", &offset) || offset == 0)
|
if (!g_pGameConf->GetOffset("ISteamGameServer__EndAuthSession", &offset) || offset == 0)
|
||||||
{
|
{
|
||||||
snprintf(error, maxlen, "Failed to find ISteamGameServer__EndAuthSession offset.\n");
|
snprintf(error, maxlen, "Failed to find ISteamGameServer__EndAuthSession offset.\n");
|
||||||
@@ -349,13 +781,6 @@ bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
|||||||
}
|
}
|
||||||
g_pEndAuthSession.SetAddress(vtable[offset]);
|
g_pEndAuthSession.SetAddress(vtable[offset]);
|
||||||
|
|
||||||
SH_MANUALHOOK_RECONFIGURE(MHook_BeginAuthSession, offset, 0, 0);
|
|
||||||
if (SH_ADD_MANUALHOOK(MHook_BeginAuthSession, g_pSteam3Server->m_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true) == 0)
|
|
||||||
{
|
|
||||||
snprintf(error, maxlen, "Failed to setup ISteamGameServer__BeginAuthSession hook.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
|
CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
|
||||||
|
|
||||||
detourCBaseServer__ConnectClient = DETOUR_CREATE_MEMBER(CBaseServer__ConnectClient, "CBaseServer__ConnectClient");
|
detourCBaseServer__ConnectClient = DETOUR_CREATE_MEMBER(CBaseServer__ConnectClient, "CBaseServer__ConnectClient");
|
||||||
@@ -366,6 +791,22 @@ bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
|||||||
}
|
}
|
||||||
detourCBaseServer__ConnectClient->EnableDetour();
|
detourCBaseServer__ConnectClient->EnableDetour();
|
||||||
|
|
||||||
|
g_Detour_CSteam3Server__OnValidateAuthTicketResponse = DETOUR_CREATE_MEMBER(CSteam3Server__OnValidateAuthTicketResponse, "CSteam3Server__OnValidateAuthTicketResponse");
|
||||||
|
if(!g_Detour_CSteam3Server__OnValidateAuthTicketResponse)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "Failed to detour CSteam3Server__OnValidateAuthTicketResponse.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_Detour_CSteam3Server__OnValidateAuthTicketResponse->EnableDetour();
|
||||||
|
|
||||||
|
g_Detour_CBaseClient__SetSteamID = DETOUR_CREATE_MEMBER(CBaseClient__SetSteamID, "CBaseClient__SetSteamID");
|
||||||
|
if(!g_Detour_CBaseClient__SetSteamID)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "Failed to detour CBaseClient__SetSteamID.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_Detour_CBaseClient__SetSteamID->EnableDetour();
|
||||||
|
|
||||||
g_pConnectForward = g_pForwards->CreateForward("OnClientPreConnectEx", ET_LowEvent, 5, NULL, Param_String, Param_String, Param_String, Param_String, Param_String);
|
g_pConnectForward = g_pForwards->CreateForward("OnClientPreConnectEx", ET_LowEvent, 5, NULL, Param_String, Param_String, Param_String, Param_String, Param_String);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -374,7 +815,6 @@ bool Connect::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
|||||||
bool Connect::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
bool Connect::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
||||||
{
|
{
|
||||||
GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION);
|
GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION);
|
||||||
|
|
||||||
ConVar_Register(0, this);
|
ConVar_Register(0, this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -394,6 +834,17 @@ bool Connect::SDK_OnMetamodUnload(char *error, size_t maxlen)
|
|||||||
detourCBaseServer__ConnectClient->DisableDetour();
|
detourCBaseServer__ConnectClient->DisableDetour();
|
||||||
delete detourCBaseServer__ConnectClient;
|
delete detourCBaseServer__ConnectClient;
|
||||||
}
|
}
|
||||||
|
if (g_Detour_CSteam3Server__OnValidateAuthTicketResponse)
|
||||||
|
{
|
||||||
|
g_Detour_CSteam3Server__OnValidateAuthTicketResponse->DisableDetour();
|
||||||
|
delete g_Detour_CSteam3Server__OnValidateAuthTicketResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_Detour_CBaseClient__SetSteamID)
|
||||||
|
{
|
||||||
|
g_Detour_CBaseClient__SetSteamID->DisableDetour();
|
||||||
|
delete g_Detour_CBaseClient__SetSteamID;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -404,3 +855,95 @@ bool Connect::RegisterConCommandBase(ConCommandBase *pCommand)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell_t SteamClientAuthenticated(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
char *pSteamID;
|
||||||
|
pContext->LocalToString(params[1], &pSteamID);
|
||||||
|
|
||||||
|
ConnectClientStorage Storage;
|
||||||
|
if(g_ConnectClientStorage.retrieve(pSteamID, &Storage))
|
||||||
|
{
|
||||||
|
if (g_SvLogging.GetInt())
|
||||||
|
g_pSM->LogMessage(myself, "%s SteamClientAuthenticated: %d", pSteamID, Storage.SteamLegal);
|
||||||
|
|
||||||
|
return Storage.SteamLegal;
|
||||||
|
}
|
||||||
|
if (g_SvLogging.GetInt())
|
||||||
|
g_pSM->LogMessage(myself, "%s SteamClientAuthenticated: FALSE!", pSteamID);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell_t ClientPreConnectEx(IPluginContext *pContext, const cell_t *params)
|
||||||
|
{
|
||||||
|
char *pSteamID;
|
||||||
|
pContext->LocalToString(params[1], &pSteamID);
|
||||||
|
|
||||||
|
int retVal = params[2];
|
||||||
|
|
||||||
|
char *rejectReason;
|
||||||
|
pContext->LocalToString(params[3], &rejectReason);
|
||||||
|
|
||||||
|
ConnectClientStorage Storage;
|
||||||
|
if(!g_ConnectClientStorage.retrieve(pSteamID, &Storage))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(retVal == 0)
|
||||||
|
{
|
||||||
|
g_pSM->LogMessage(myself, "inside ClientPreConnectEx return 0. steamID: %s", pSteamID);
|
||||||
|
g_pRejectConnectionFunc((CBaseServer*)Storage.pThis, Storage.address, Storage.iClientChallenge, rejectReason);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *pClient = DETOUR_MEMBER_MCALL_ORIGINAL(CBaseServer__ConnectClient, Storage.pThis)(Storage.address, Storage.nProtocol, Storage.iChallenge, Storage.iClientChallenge, Storage.nAuthProtocol, Storage.pchName, Storage.pchPassword, Storage.pCookie, Storage.cbCookie);
|
||||||
|
|
||||||
|
if(!pClient)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool force = g_SvNoSteam.GetInt() || g_SvForceSteam.GetInt();
|
||||||
|
|
||||||
|
if(Storage.SteamAuthFailed && force && !Storage.GotValidateAuthTicketResponse)
|
||||||
|
{
|
||||||
|
if (g_SvLogging.GetInt())
|
||||||
|
g_pSM->LogMessage(myself, "%s Force ValidateAuthTicketResponse", pSteamID);
|
||||||
|
|
||||||
|
Storage.ValidateAuthTicketResponse.m_SteamID = CSteamID(Storage.ullSteamID);
|
||||||
|
Storage.ValidateAuthTicketResponse.m_eAuthSessionResponse = k_EAuthSessionResponseOK;
|
||||||
|
Storage.ValidateAuthTicketResponse.m_OwnerSteamID = Storage.ValidateAuthTicketResponse.m_SteamID;
|
||||||
|
Storage.GotValidateAuthTicketResponse = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this is always called in order to verify the client on the server
|
||||||
|
if(Storage.GotValidateAuthTicketResponse)
|
||||||
|
{
|
||||||
|
if (g_SvLogging.GetInt())
|
||||||
|
g_pSM->LogMessage(myself, "%s Replay ValidateAuthTicketResponse", pSteamID);
|
||||||
|
|
||||||
|
DETOUR_MEMBER_MCALL_ORIGINAL(CSteam3Server__OnValidateAuthTicketResponse, g_pSteam3Server)(&Storage.ValidateAuthTicketResponse);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sp_nativeinfo_t MyNatives[] =
|
||||||
|
{
|
||||||
|
{ "ClientPreConnectEx", ClientPreConnectEx },
|
||||||
|
{ "SteamClientAuthenticated", SteamClientAuthenticated },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
void Connect::SDK_OnAllLoaded()
|
||||||
|
{
|
||||||
|
SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools);
|
||||||
|
iserver = g_pSDKTools->GetIServer();
|
||||||
|
if (!iserver) {
|
||||||
|
smutils->LogError(myself, "Failed to get IServer interface from SDKTools!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sharesys->AddNatives(myself, MyNatives);
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,6 +55,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
//virtual void SDK_OnPauseChange(bool paused);
|
//virtual void SDK_OnPauseChange(bool paused);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is called once all known extensions have been loaded.
|
||||||
|
* Note: It is is a good idea to add natives here, if any are provided.
|
||||||
|
*/
|
||||||
|
virtual void SDK_OnAllLoaded();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief this is called when Core wants to know if your extension is working.
|
* @brief this is called when Core wants to know if your extension is working.
|
||||||
*
|
*
|
||||||
@@ -110,6 +117,11 @@ public: \
|
|||||||
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type) = NULL; \
|
ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type) = NULL; \
|
||||||
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name)
|
ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name)
|
||||||
|
|
||||||
|
#define DETOUR_MEMBER_MCALL_CALLBACK(name, classptr) \
|
||||||
|
((name##Class *)classptr->*(&name##Class::name))
|
||||||
|
#define DETOUR_MEMBER_MCALL_ORIGINAL(name, classptr) \
|
||||||
|
((name##Class *)classptr->*(name##Class::name##_Actual))
|
||||||
|
|
||||||
struct mfpDetails {
|
struct mfpDetails {
|
||||||
void *addr;
|
void *addr;
|
||||||
intptr_t adjustor;
|
intptr_t adjustor;
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
#define NAME_SALT "your-salt-here"
|
||||||
@@ -40,8 +40,8 @@
|
|||||||
#define SMEXT_CONF_NAME "Connect"
|
#define SMEXT_CONF_NAME "Connect"
|
||||||
#define SMEXT_CONF_DESCRIPTION "Forward for early connection"
|
#define SMEXT_CONF_DESCRIPTION "Forward for early connection"
|
||||||
#define SMEXT_CONF_VERSION SM_FULL_VERSION
|
#define SMEXT_CONF_VERSION SM_FULL_VERSION
|
||||||
#define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker"
|
#define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker + jenz + zacade"
|
||||||
#define SMEXT_CONF_URL "http://limetech.org/"
|
#define SMEXT_CONF_URL "https://git.unloze.com/UNLOZE/sm-ext-connect"
|
||||||
#define SMEXT_CONF_LOGTAG "CONNECT"
|
#define SMEXT_CONF_LOGTAG "CONNECT"
|
||||||
#define SMEXT_CONF_LICENSE "GPL"
|
#define SMEXT_CONF_LICENSE "GPL"
|
||||||
#define SMEXT_CONF_DATESTRING __DATE__
|
#define SMEXT_CONF_DATESTRING __DATE__
|
||||||
|
|||||||
Reference in New Issue
Block a user