Author SHA1 Message Date
A1m` 07fc524d27 Fixed incorrect use of ReferenceToIndex, which caused some functions to be unstable during client connection. (#2)
While this function works well with the client indexes we pass in, as its name
suggests, we should essentially be passing player references, not client indexes.
The problem is that SourceMod looks for this player in the entity list, but they
might not be there when connecting, or they might be another client or a bot.

This makes the code unstable and confusing, and it works intermittently.
Steam doesn't care whether the player is in the entity list—it works with the
Steam ID. Using ReferenceToIndex here is fundamentally wrong and causes random
-1 errors, especially during OnClientConnect when the entity may not exist yet.

This commit replaces it with a direct client index lookup, which is correct and
reliable because params[1] is already a client index.
2026-07-13 21:39:04 +00:00
Nicholas Hastings 8fd7a1988c Update extension author and repository URL 2026-07-12 22:40:00 -04:00
Nicholas Hastings 65936e7d9c Fix size_t truncation warning in SetHTTPRequestRawPostBodyFromFile 2026-07-12 22:10:00 -04:00
Nicholas Hastings 645103735e Fix streaming HTTP response header and data callbacks 2026-07-12 21:20:00 -04:00
Nicholas Hastings a779d6d961 Expose SetAdvertiseServerActive 2026-07-12 15:17:29 -04:00
Nicholas Hastings c82feb1a4a Add methodmap for SteamWorksHTTPRequest 2026-07-12 13:59:38 -04:00
Nicholas Hastings ceeaf66b95 Add function docs to inc 2026-07-12 13:31:37 -04:00
Nicholas Hastings 8eaea7217c Update EResult and EHTTPStatusCode 2026-07-12 13:16:28 -04:00
Nicholas Hastings 482491616f Update plugins/inc to use transitional syntax (#1)
* Update plugins/inc to use transitional syntax

* Add CI job for validating plugins/inc

* ci: work around broken entrypoint in sourcemod-spcomp image

* ci: only compile Pawn plugins against master includes

* ci: restore per-version compile-plugins matrix now that spcomp images are fixed

* ci: reuse sm.branch as the image tag directly
2026-07-12 17:11:09 +00:00
12 changed files with 1329 additions and 289 deletions
+35
View File
@@ -101,6 +101,41 @@ jobs:
path: build/package/addons path: build/package/addons
if-no-files-found: error if-no-files-found: error
compile-plugins:
name: compile-plugins-sm${{ matrix.sm.label }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sm:
- label: "1.12"
branch: "1.12-dev"
- label: "1.13"
branch: "master"
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Clone SourceMod includes
run: |
git clone --depth 1 -b ${{ matrix.sm.branch }} https://github.com/alliedmodders/sourcemod.git sourcemod
- name: Compile Pawn plugins
run: |
mkdir -p compiled
status=0
for f in Pawn/*.sp; do
echo "::group::${f}"
docker run --rm -v "${{ github.workspace }}:/work" -w /work \
ghcr.io/alliedmodders/sourcemod-spcomp:${{ matrix.sm.branch }} \
-i Pawn/includes -i sourcemod/plugins/include \
"${f}" -o "compiled/$(basename "${f%.sp}.smx")" \
|| status=1
echo "::endgroup::"
done
exit "${status}"
release: release:
name: Release name: Release
needs: build needs: build
+1
View File
@@ -72,6 +72,7 @@ void SteamWorks::SDK_OnUnload()
delete this->pSWHTTPNatives; delete this->pSWHTTPNatives;
delete this->pSWHTTP; delete this->pSWHTTP;
this->pSWHTTP = NULL; /* Requests freed via frame actions may outlive us; let their dtor detect this. */
delete this->pSWGameServer; delete this->pSWGameServer;
delete this->pSWGameData; delete this->pSWGameData;
} }
+42 -14
View File
@@ -190,6 +190,19 @@ static cell_t sm_ClearRules(IPluginContext *pContext, const cell_t *params)
return 1; return 1;
} }
static cell_t sm_SetAdvertiseServerActive(IPluginContext *pContext, const cell_t *params)
{
ISteamGameServer *pServer = GetGSPointer();
if (pServer == NULL)
{
return 0;
}
pServer->SetAdvertiseServerActive(!!params[1]);
return 1;
}
static cell_t sm_ForceHeartbeat(IPluginContext *pContext, const cell_t *params) static cell_t sm_ForceHeartbeat(IPluginContext *pContext, const cell_t *params)
{ {
/* Deprecated no-op: newer Steamworks SDKs removed ISteamGameServer::ForceHeartbeat(); /* Deprecated no-op: newer Steamworks SDKs removed ISteamGameServer::ForceHeartbeat();
@@ -206,11 +219,16 @@ static cell_t sm_UserHasLicenseForApp(IPluginContext *pContext, const cell_t *pa
return k_EUserHasLicenseResultNoAuth; return k_EUserHasLicenseResultNoAuth;
} }
int client = gamehelpers->ReferenceToIndex(params[1]); int client = params[1];
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); /* Man, including GameHelpers and PlayerHelpers for this native :(. */ if (client < 1 || client > playerhelpers->GetMaxClients())
if (pPlayer == NULL || pPlayer->IsConnected() == false)
{ {
return pContext->ThrowNativeError("Client index %d is invalid", params[1]); return pContext->ThrowNativeError("Client index %d is invalid", client);
}
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
if (pPlayer == NULL || !pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client index %d is not connected", client);
} }
CSteamID checkid = CreateCommonCSteamID(pPlayer, params, 3, 4); CSteamID checkid = CreateCommonCSteamID(pPlayer, params, 3, 4);
@@ -232,12 +250,16 @@ static cell_t sm_UserHasLicenseForAppId(IPluginContext *pContext, const cell_t *
static cell_t sm_GetClientSteamID(IPluginContext *pContext, const cell_t *params) static cell_t sm_GetClientSteamID(IPluginContext *pContext, const cell_t *params)
{ {
int client = gamehelpers->ReferenceToIndex(params[1]); int client = params[1];
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); if (client < 1 || client > playerhelpers->GetMaxClients())
if (pPlayer == NULL || pPlayer->IsConnected() == false)
{ {
return pContext->ThrowNativeError("Client index %d is invalid", params[1]); return pContext->ThrowNativeError("Client index %d is invalid", client);
}
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
if (pPlayer == NULL || !pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client index %d is not connected", client);
} }
CSteamID steamId = CreateCommonCSteamID(pPlayer, params, 4, 5); CSteamID steamId = CreateCommonCSteamID(pPlayer, params, 4, 5);
@@ -257,14 +279,19 @@ static cell_t sm_GetUserGroupStatus(IPluginContext *pContext, const cell_t *para
if (pServer == NULL) if (pServer == NULL)
{ {
return false; return 0;
} }
int client = gamehelpers->ReferenceToIndex(params[1]); int client = params[1];
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); /* Man, including GameHelpers and PlayerHelpers for this native :(. */ if (client < 1 || client > playerhelpers->GetMaxClients())
if (pPlayer == NULL || pPlayer->IsConnected() == false)
{ {
return pContext->ThrowNativeError("Client index %d is invalid", params[1]); return pContext->ThrowNativeError("Client index %d is invalid", client);
}
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
if (pPlayer == NULL || !pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client index %d is not connected", client);
} }
CSteamID checkid = CreateCommonCSteamID(pPlayer, params, 3, 4); CSteamID checkid = CreateCommonCSteamID(pPlayer, params, 3, 4);
@@ -295,6 +322,7 @@ static sp_nativeinfo_t gsnatives[] = {
{"SteamWorks_IsConnected", sm_IsConnected}, {"SteamWorks_IsConnected", sm_IsConnected},
{"SteamWorks_SetRule", sm_SetRule}, {"SteamWorks_SetRule", sm_SetRule},
{"SteamWorks_ClearRules", sm_ClearRules}, {"SteamWorks_ClearRules", sm_ClearRules},
{"SteamWorks_SetAdvertiseServerActive", sm_SetAdvertiseServerActive},
{"SteamWorks_ForceHeartbeat", sm_ForceHeartbeat}, {"SteamWorks_ForceHeartbeat", sm_ForceHeartbeat},
{"SteamWorks_HasLicenseForApp", sm_UserHasLicenseForApp}, {"SteamWorks_HasLicenseForApp", sm_UserHasLicenseForApp},
{"SteamWorks_HasLicenseForAppId", sm_UserHasLicenseForAppId}, {"SteamWorks_HasLicenseForAppId", sm_UserHasLicenseForAppId},
+2 -2
View File
@@ -41,8 +41,8 @@
#define SMEXT_CONF_NAME "SteamWorks Extension" #define SMEXT_CONF_NAME "SteamWorks Extension"
#define SMEXT_CONF_DESCRIPTION "Exposes SteamWorks functions to Developers" #define SMEXT_CONF_DESCRIPTION "Exposes SteamWorks functions to Developers"
#define SMEXT_CONF_VERSION "1.2.3" #define SMEXT_CONF_VERSION "1.2.3"
#define SMEXT_CONF_AUTHOR "Kyle Sanderson" #define SMEXT_CONF_AUTHOR "Kyle Sanderson, AlliedModders"
#define SMEXT_CONF_URL "http://AlliedMods.net" #define SMEXT_CONF_URL "https://github.com/alliedmodders/SM-SteamWorks"
#define SMEXT_CONF_LOGTAG "STEAMWORKS" #define SMEXT_CONF_LOGTAG "STEAMWORKS"
#define SMEXT_CONF_LICENSE "GPLv3" #define SMEXT_CONF_LICENSE "GPLv3"
#define SMEXT_CONF_DATESTRING __DATE__ #define SMEXT_CONF_DATESTRING __DATE__
+27 -12
View File
@@ -60,11 +60,16 @@ static cell_t sm_RequestUserStats(IPluginContext *pContext, const cell_t *params
return 0; return 0;
} }
int client = gamehelpers->ReferenceToIndex(params[1]); int client = params[1];
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); /* Man, including GameHelpers and PlayerHelpers for this native :(. */ if (client < 1 || client > playerhelpers->GetMaxClients())
if (pPlayer == NULL || pPlayer->IsConnected() == false)
{ {
return pContext->ThrowNativeError("Client index %d is invalid", params[1]); return pContext->ThrowNativeError("Client index %d is invalid", client);
}
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
if (pPlayer == NULL || !pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client index %d is not connected", client);
} }
CSteamID checkid = CreateCommonCSteamID(pPlayer, params); CSteamID checkid = CreateCommonCSteamID(pPlayer, params);
@@ -80,11 +85,16 @@ static cell_t sm_GetStatCell(IPluginContext *pContext, const cell_t *params)
return 0; return 0;
} }
int client = gamehelpers->ReferenceToIndex(params[1]); int client = params[1];
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); /* Man, including GameHelpers and PlayerHelpers for this native :(. */ if (client < 1 || client > playerhelpers->GetMaxClients())
if (pPlayer == NULL || pPlayer->IsConnected() == false)
{ {
return pContext->ThrowNativeError("Client index %d is invalid", params[1]); return pContext->ThrowNativeError("Client index %d is invalid", client);
}
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
if (pPlayer == NULL || !pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client index %d is not connected", client);
} }
char *pName; char *pName;
@@ -123,11 +133,16 @@ static cell_t sm_GetStatFloat(IPluginContext *pContext, const cell_t *params)
return 0; return 0;
} }
int client = gamehelpers->ReferenceToIndex(params[1]); int client = params[1];
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client); /* Man, including GameHelpers and PlayerHelpers for this native :(. */ if (client < 1 || client > playerhelpers->GetMaxClients())
if (pPlayer == NULL || pPlayer->IsConnected() == false)
{ {
return pContext->ThrowNativeError("Client index %d is invalid", params[1]); return pContext->ThrowNativeError("Client index %d is invalid", client);
}
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
if (pPlayer == NULL || !pPlayer->IsConnected())
{
return pContext->ThrowNativeError("Client index %d is not connected", client);
} }
char *pName; char *pName;
+43 -1
View File
@@ -23,7 +23,9 @@ static ISteamHTTP *GetHTTPPointer()
return g_SteamWorks.pSWGameServer->GetHTTP(); return g_SteamWorks.pSWGameServer->GetHTTP();
} }
SteamWorksHTTP::SteamWorksHTTP() SteamWorksHTTP::SteamWorksHTTP() :
m_CallbackHeadersReceived(this, &SteamWorksHTTP::OnHTTPHeadersReceived),
m_CallbackDataReceived(this, &SteamWorksHTTP::OnHTTPDataReceived)
{ {
this->typeHTTP = handlesys->CreateType("HTTPHandle", this, 0, NULL, NULL, myself->GetIdentity(), NULL); this->typeHTTP = handlesys->CreateType("HTTPHandle", this, 0, NULL, NULL, myself->GetIdentity(), NULL);
} }
@@ -38,6 +40,46 @@ HandleType_t SteamWorksHTTP::GetHTTPHandle(void)
return this->typeHTTP; return this->typeHTTP;
} }
void SteamWorksHTTP::RegisterRequest(SteamWorksHTTPRequest *pRequest)
{
if (pRequest->request != INVALID_HTTPREQUEST_HANDLE)
{
this->m_Requests[pRequest->request] = pRequest;
}
}
void SteamWorksHTTP::UnregisterRequest(SteamWorksHTTPRequest *pRequest)
{
if (pRequest->request != INVALID_HTTPREQUEST_HANDLE)
{
this->m_Requests.erase(pRequest->request);
}
}
SteamWorksHTTPRequest *SteamWorksHTTP::FindRequest(HTTPRequestHandle request)
{
auto it = this->m_Requests.find(request);
return (it == this->m_Requests.end()) ? NULL : it->second;
}
void SteamWorksHTTP::OnHTTPHeadersReceived(HTTPRequestHeadersReceived_t *pParam)
{
SteamWorksHTTPRequest *pRequest = this->FindRequest(pParam->m_hRequest);
if (pRequest != NULL)
{
pRequest->OnHTTPHeadersReceived(pParam);
}
}
void SteamWorksHTTP::OnHTTPDataReceived(HTTPRequestDataReceived_t *pParam)
{
SteamWorksHTTPRequest *pRequest = this->FindRequest(pParam->m_hRequest);
if (pRequest != NULL)
{
pRequest->OnHTTPDataReceived(pParam);
}
}
static void DelayedDeleteSteamWorksHTTPRequest(void *object) static void DelayedDeleteSteamWorksHTTPRequest(void *object)
{ {
SteamWorksHTTPRequest *pRequest = reinterpret_cast<SteamWorksHTTPRequest *>(object); SteamWorksHTTPRequest *pRequest = reinterpret_cast<SteamWorksHTTPRequest *>(object);
+19
View File
@@ -21,6 +21,10 @@
#include "steam_gameserver.h" #include "steam_gameserver.h"
#include "smsdk_ext.h" #include "smsdk_ext.h"
#include <unordered_map>
class SteamWorksHTTPRequest;
class SteamWorksHTTP : class SteamWorksHTTP :
public IHandleTypeDispatch public IHandleTypeDispatch
{ {
@@ -35,8 +39,23 @@ class SteamWorksHTTP :
public: public:
HandleType_t GetHTTPHandle(void); HandleType_t GetHTTPHandle(void);
/* Streaming responses report progress through HTTPRequestHeadersReceived_t /
HTTPRequestDataReceived_t. Steam delivers those as broadcast gameserver
callbacks (not as call results of the streaming API call), so a single
dispatcher here receives them and routes each one to the owning request by
its handle. Requests add/remove themselves as they are created/destroyed. */
void RegisterRequest(SteamWorksHTTPRequest *pRequest);
void UnregisterRequest(SteamWorksHTTPRequest *pRequest);
private:
SteamWorksHTTPRequest *FindRequest(HTTPRequestHandle request);
STEAM_GAMESERVER_CALLBACK(SteamWorksHTTP, OnHTTPHeadersReceived, HTTPRequestHeadersReceived_t, m_CallbackHeadersReceived);
STEAM_GAMESERVER_CALLBACK(SteamWorksHTTP, OnHTTPDataReceived, HTTPRequestDataReceived_t, m_CallbackDataReceived);
private: private:
HandleType_t typeHTTP; HandleType_t typeHTTP;
std::unordered_map<HTTPRequestHandle, SteamWorksHTTPRequest *> m_Requests;
}; };
#include "swhttprequest.h" #include "swhttprequest.h"
+50 -17
View File
@@ -67,6 +67,14 @@ SteamWorksHTTPRequest::SteamWorksHTTPRequest() : request(INVALID_HTTPREQUEST_HAN
SteamWorksHTTPRequest::~SteamWorksHTTPRequest() SteamWorksHTTPRequest::~SteamWorksHTTPRequest()
{ {
/* Requests are freed via a frame action, so on extension unload this can run
after the dispatcher itself has been torn down; pSWHTTP is nulled in that
case (see SDK_OnUnload), so guard against it. */
if (g_SteamWorks.pSWHTTP != NULL)
{
g_SteamWorks.pSWHTTP->UnregisterRequest(this);
}
ISteamHTTP *pHTTP = GetHTTPPointer(); ISteamHTTP *pHTTP = GetHTTPPointer();
if (pHTTP != NULL) if (pHTTP != NULL)
{ {
@@ -101,7 +109,10 @@ void SteamWorksHTTPRequest::OnHTTPRequestCompleted(HTTPRequestCompleted_t *pRequ
this->pCompletedForward->Execute(NULL); this->pCompletedForward->Execute(NULL);
} }
void SteamWorksHTTPRequest::OnHTTPHeadersReceived(HTTPRequestHeadersReceived_t *pRequest, bool bFailed) /* Streaming header/data notifications are success-only callbacks (unlike the
completion call result, they carry no IO-failure flag), so bFailure is always
false here. Failures still surface through the completion callback. */
void SteamWorksHTTPRequest::OnHTTPHeadersReceived(HTTPRequestHeadersReceived_t *pRequest)
{ {
if (this->pHeadersReceivedForward == NULL || this->pHeadersReceivedForward->GetFunctionCount() == 0) if (this->pHeadersReceivedForward == NULL || this->pHeadersReceivedForward->GetFunctionCount() == 0)
{ {
@@ -109,13 +120,13 @@ void SteamWorksHTTPRequest::OnHTTPHeadersReceived(HTTPRequestHeadersReceived_t *
} }
this->pHeadersReceivedForward->PushCell(this->handle); this->pHeadersReceivedForward->PushCell(this->handle);
this->pHeadersReceivedForward->PushCell(bFailed); this->pHeadersReceivedForward->PushCell(false);
this->pHeadersReceivedForward->PushCell(pRequest->m_ulContextValue >> 32); this->pHeadersReceivedForward->PushCell(pRequest->m_ulContextValue >> 32);
this->pHeadersReceivedForward->PushCell((pRequest->m_ulContextValue & 0x00000000FFFFFFFF)); this->pHeadersReceivedForward->PushCell((pRequest->m_ulContextValue & 0x00000000FFFFFFFF));
this->pHeadersReceivedForward->Execute(NULL); this->pHeadersReceivedForward->Execute(NULL);
} }
void SteamWorksHTTPRequest::OnHTTPDataReceived(HTTPRequestDataReceived_t *pRequest, bool bFailed) void SteamWorksHTTPRequest::OnHTTPDataReceived(HTTPRequestDataReceived_t *pRequest)
{ {
if (this->pDataReceivedForward == NULL || this->pDataReceivedForward->GetFunctionCount() == 0) if (this->pDataReceivedForward == NULL || this->pDataReceivedForward->GetFunctionCount() == 0)
{ {
@@ -123,7 +134,7 @@ void SteamWorksHTTPRequest::OnHTTPDataReceived(HTTPRequestDataReceived_t *pReque
} }
this->pDataReceivedForward->PushCell(this->handle); this->pDataReceivedForward->PushCell(this->handle);
this->pDataReceivedForward->PushCell(bFailed); this->pDataReceivedForward->PushCell(false);
this->pDataReceivedForward->PushCell(pRequest->m_cOffset); this->pDataReceivedForward->PushCell(pRequest->m_cOffset);
this->pDataReceivedForward->PushCell(pRequest->m_cBytesReceived); this->pDataReceivedForward->PushCell(pRequest->m_cBytesReceived);
this->pDataReceivedForward->PushCell(pRequest->m_ulContextValue >> 32); this->pDataReceivedForward->PushCell(pRequest->m_ulContextValue >> 32);
@@ -160,6 +171,8 @@ static cell_t sm_CreateHTTPRequest(IPluginContext *pContext, const cell_t *param
pRequest->request = request; pRequest->request = request;
pRequest->handle = handle; pRequest->handle = handle;
g_SteamWorks.pSWHTTP->RegisterRequest(pRequest);
return handle; return handle;
} }
@@ -296,23 +309,14 @@ static cell_t sm_SetCallbacks(IPluginContext *pContext, const cell_t *params)
static void SetCallbacks(SteamAPICall_t &hCall, SteamWorksHTTPRequest *pRequest) static void SetCallbacks(SteamAPICall_t &hCall, SteamWorksHTTPRequest *pRequest)
{ {
/* Only completion is a call result of this send. Header/data streaming
notifications are delivered as broadcast callbacks and routed to the request
by SteamWorksHTTP's dispatcher, so there is nothing to bind to hCall here. */
if (pRequest->pCompletedForward != NULL) if (pRequest->pCompletedForward != NULL)
{ {
pRequest->CompletedCallResult.SetGameserverFlag(); pRequest->CompletedCallResult.SetGameserverFlag();
pRequest->CompletedCallResult.Set(hCall, pRequest, &SteamWorksHTTPRequest::OnHTTPRequestCompleted); pRequest->CompletedCallResult.Set(hCall, pRequest, &SteamWorksHTTPRequest::OnHTTPRequestCompleted);
} }
if (pRequest->pHeadersReceivedForward != NULL)
{
pRequest->HeadersCallResult.SetGameserverFlag();
pRequest->HeadersCallResult.Set(hCall, pRequest, &SteamWorksHTTPRequest::OnHTTPHeadersReceived);
}
if (pRequest->pDataReceivedForward != NULL)
{
pRequest->DataCallResult.SetGameserverFlag();
pRequest->DataCallResult.Set(hCall, pRequest, &SteamWorksHTTPRequest::OnHTTPDataReceived);
}
} }
static cell_t sm_SendHTTPRequestAndStreamResponse(IPluginContext *pContext, const cell_t *params) static cell_t sm_SendHTTPRequestAndStreamResponse(IPluginContext *pContext, const cell_t *params)
@@ -500,7 +504,7 @@ static cell_t sm_SetHTTPRequestRawPostBodyFromFile(IPluginContext *pContext, con
} }
char *pBuffer = new char[size + 1]; char *pBuffer = new char[size + 1];
uint32_t itemsRead = fread(pBuffer, sizeof(char), size, pInputFile); size_t itemsRead = fread(pBuffer, sizeof(char), size, pInputFile);
fclose(pInputFile); fclose(pInputFile);
if (itemsRead != size) if (itemsRead != size)
@@ -706,6 +710,35 @@ static sp_nativeinfo_t httpnatives[] = {
{"SteamWorks_WriteHTTPResponseBodyToFile", sm_WriteHTTPResponseBodyToFile}, {"SteamWorks_WriteHTTPResponseBodyToFile", sm_WriteHTTPResponseBodyToFile},
{"SteamWorks_SendHTTPRequestAndStreamResponse", sm_SendHTTPRequestAndStreamResponse}, {"SteamWorks_SendHTTPRequestAndStreamResponse", sm_SendHTTPRequestAndStreamResponse},
{"SteamWorks_GetHTTPStreamingResponseBodyData", sm_GetHTTPStreamingResponseBodyData}, {"SteamWorks_GetHTTPStreamingResponseBodyData", sm_GetHTTPStreamingResponseBodyData},
/* SteamWorksHTTPRequest methodmap. These reuse the functions above; the implicit
`this` handle arrives as params[1], exactly like the hHandle/hRequest first
parameter of the free-function natives (and the constructor maps to the create
native, whose first argument is likewise params[1]). */
{"SteamWorksHTTPRequest.SteamWorksHTTPRequest", sm_CreateHTTPRequest},
{"SteamWorksHTTPRequest.SetContextValue", sm_SetHTTPRequestContextValue},
{"SteamWorksHTTPRequest.SetNetworkActivityTimeout", sm_SetHTTPRequestNetworkActivityTimeout},
{"SteamWorksHTTPRequest.SetHeaderValue", sm_SetHTTPRequestHeaderValue},
{"SteamWorksHTTPRequest.SetGetOrPostParameter", sm_SetHTTPRequestGetOrPostParameter},
{"SteamWorksHTTPRequest.SetUserAgentInfo", sm_SetHTTPRequestUserAgentInfo},
{"SteamWorksHTTPRequest.SetRequiresVerifiedCertificate", sm_SetHTTPRequestRequiresVerifiedCertificate},
{"SteamWorksHTTPRequest.SetAbsoluteTimeoutMS", sm_SetHTTPRequestAbsoluteTimeoutMS},
{"SteamWorksHTTPRequest.SetCallbacks", sm_SetCallbacks},
{"SteamWorksHTTPRequest.Send", sm_SendHTTPRequest},
{"SteamWorksHTTPRequest.SendAndStreamResponse", sm_SendHTTPRequestAndStreamResponse},
{"SteamWorksHTTPRequest.Defer", sm_DeferHTTPRequest},
{"SteamWorksHTTPRequest.Prioritize", sm_PrioritizeHTTPRequest},
{"SteamWorksHTTPRequest.GetResponseHeaderSize", sm_GetHTTPResponseHeaderSize},
{"SteamWorksHTTPRequest.GetResponseHeaderValue", sm_GetHTTPResponseHeaderValue},
{"SteamWorksHTTPRequest.GetResponseBodySize", sm_GetHTTPResponseBodySize},
{"SteamWorksHTTPRequest.GetResponseBodyData", sm_GetHTTPResponseBodyData},
{"SteamWorksHTTPRequest.GetStreamingResponseBodyData", sm_GetHTTPStreamingResponseBodyData},
{"SteamWorksHTTPRequest.GetDownloadProgressPct", sm_GetHTTPDownloadProgressPct},
{"SteamWorksHTTPRequest.GetWasTimedOut", sm_GetHTTPRequestWasTimedOut},
{"SteamWorksHTTPRequest.SetRawPostBody", sm_SetHTTPRequestRawPostBody},
{"SteamWorksHTTPRequest.SetRawPostBodyFromFile", sm_SetHTTPRequestRawPostBodyFromFile},
{"SteamWorksHTTPRequest.GetResponseBodyCallback", sm_GetHTTPResponseBodyCallback},
{"SteamWorksHTTPRequest.WriteResponseBodyToFile", sm_WriteHTTPResponseBodyToFile},
{NULL, NULL} {NULL, NULL}
}; };
+5 -4
View File
@@ -32,14 +32,15 @@ class SteamWorksHTTPRequest
Handle_t handle; Handle_t handle;
public: public:
/* Completion is a genuine call result of the send API call, so it stays a
CCallResult. Headers/data arrive as broadcast callbacks and are routed here
by SteamWorksHTTP's dispatcher, hence no per-request CCallResult for them. */
void OnHTTPRequestCompleted(HTTPRequestCompleted_t *pRequest, bool bFailed); void OnHTTPRequestCompleted(HTTPRequestCompleted_t *pRequest, bool bFailed);
void OnHTTPHeadersReceived(HTTPRequestHeadersReceived_t *pRequest, bool bFailed); void OnHTTPHeadersReceived(HTTPRequestHeadersReceived_t *pRequest);
void OnHTTPDataReceived(HTTPRequestDataReceived_t *pRequest, bool bFailed); void OnHTTPDataReceived(HTTPRequestDataReceived_t *pRequest);
public: public:
CCallResult<SteamWorksHTTPRequest, HTTPRequestCompleted_t> CompletedCallResult; CCallResult<SteamWorksHTTPRequest, HTTPRequestCompleted_t> CompletedCallResult;
CCallResult<SteamWorksHTTPRequest, HTTPRequestHeadersReceived_t> HeadersCallResult;
CCallResult<SteamWorksHTTPRequest, HTTPRequestDataReceived_t> DataCallResult;
public: public:
IChangeableForward *pCompletedForward; IChangeableForward *pCompletedForward;
+16 -16
View File
@@ -2,12 +2,12 @@
#include <sourcemod> #include <sourcemod>
#include <SteamWorks> #include <SteamWorks>
new g_iPatchVersion = 0; int g_iPatchVersion = 0;
new g_iAppID = 0; int g_iAppID = 0;
new Handle:g_hForward = INVALID_HANDLE; Handle g_hForward = INVALID_HANDLE;
public Plugin:myinfo = public Plugin myinfo =
{ {
name = "SteamWorks Update Check", /* https://www.youtube.com/watch?v=Tq_0ht8HCcM */ name = "SteamWorks Update Check", /* https://www.youtube.com/watch?v=Tq_0ht8HCcM */
author = "Kyle Sanderson", author = "Kyle Sanderson",
@@ -16,15 +16,15 @@ public Plugin:myinfo =
url = "https://AlliedMods.net" url = "https://AlliedMods.net"
}; };
static stock bool:ReadSteamINF(const String:sPath[], &iAppID, &iPatchVersion) static stock bool ReadSteamINF(const char[] sPath, int &iAppID, int &iPatchVersion)
{ {
new Handle:hFile = OpenFile(sPath, "r"); Handle hFile = OpenFile(sPath, "r");
if (hFile == INVALID_HANDLE) if (hFile == INVALID_HANDLE)
{ {
return false; return false;
} }
decl String:sBuffer[256]; char sBuffer[256];
do do
{ {
@@ -36,7 +36,7 @@ static stock bool:ReadSteamINF(const String:sPath[], &iAppID, &iPatchVersion)
TrimString(sBuffer); TrimString(sBuffer);
ReplaceString(sBuffer, sizeof(sBuffer), ".", ""); /* CS:GO uses decimals in steam.inf, WebAPI is Steam| style. */ ReplaceString(sBuffer, sizeof(sBuffer), ".", ""); /* CS:GO uses decimals in steam.inf, WebAPI is Steam| style. */
new iPos = FindCharInString(sBuffer, '='); int iPos = FindCharInString(sBuffer, '=');
if (iPos == -1) if (iPos == -1)
{ {
continue; continue;
@@ -71,7 +71,7 @@ static stock bool:ReadSteamINF(const String:sPath[], &iAppID, &iPatchVersion)
return true; return true;
} }
public OnPluginStart() public void OnPluginStart()
{ {
if (!ReadSteamINF("steam.inf", g_iAppID, g_iPatchVersion) && !ReadSteamINF("../steam.inf", g_iAppID, g_iPatchVersion)) if (!ReadSteamINF("steam.inf", g_iAppID, g_iPatchVersion) && !ReadSteamINF("../steam.inf", g_iAppID, g_iPatchVersion))
{ {
@@ -81,20 +81,20 @@ public OnPluginStart()
g_hForward = CreateGlobalForward("SteamWorks_RestartRequested", ET_Ignore); g_hForward = CreateGlobalForward("SteamWorks_RestartRequested", ET_Ignore);
} }
public OnMapStart() public void OnMapStart()
{ {
CreateTimer(120.0, OnCheckForUpdate, _, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT); CreateTimer(120.0, OnCheckForUpdate, _, TIMER_FLAG_NO_MAPCHANGE|TIMER_REPEAT);
} }
public Action:OnCheckForUpdate(Handle:hTimer) public Action OnCheckForUpdate(Handle hTimer)
{ {
static String:sRequest[256]; static char sRequest[256];
if (sRequest[0] == '\0') if (sRequest[0] == '\0')
{ {
FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamApps/UpToDateCheck/v0001/?appid=%u&version=%u&format=xml", g_iAppID, g_iPatchVersion); FormatEx(sRequest, sizeof(sRequest), "http://api.steampowered.com/ISteamApps/UpToDateCheck/v0001/?appid=%u&version=%u&format=xml", g_iAppID, g_iPatchVersion);
} }
new Handle:hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest); Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
if (!hRequest || !SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) || !SteamWorks_SendHTTPRequest(hRequest)) if (!hRequest || !SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) || !SteamWorks_SendHTTPRequest(hRequest))
{ {
CloseHandle(hRequest); CloseHandle(hRequest);
@@ -103,7 +103,7 @@ public Action:OnCheckForUpdate(Handle:hTimer)
return Plugin_Continue; return Plugin_Continue;
} }
public OnTransferComplete(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode) public void OnTransferComplete(Handle hRequest, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode)
{ {
if (!bFailure && bRequestSuccessful && eStatusCode == k_EHTTPStatusCode200OK) if (!bFailure && bRequestSuccessful && eStatusCode == k_EHTTPStatusCode200OK)
{ {
@@ -113,9 +113,9 @@ public OnTransferComplete(Handle:hRequest, bool:bFailure, bool:bRequestSuccessfu
CloseHandle(hRequest); CloseHandle(hRequest);
} }
public APIWebResponse(const String:sData[]) public void APIWebResponse(const char[] sData)
{ {
new iPos = StrContains(sData, "<required_version>"); int iPos = StrContains(sData, "<required_version>");
if (iPos == -1) if (iPos == -1)
{ {
return; return;
File diff suppressed because it is too large Load Diff
+17 -17
View File
@@ -20,10 +20,10 @@
#include <sourcemod> #include <sourcemod>
#include <SteamWorks> #include <SteamWorks>
new Handle:g_hSteamServersConnected = INVALID_HANDLE; Handle g_hSteamServersConnected = INVALID_HANDLE;
new Handle:g_hSteamServersDisconnected = INVALID_HANDLE; Handle g_hSteamServersDisconnected = INVALID_HANDLE;
public Plugin:myinfo = { public Plugin myinfo = {
name = "SteamWorks Additive Glider", /* SWAG */ name = "SteamWorks Additive Glider", /* SWAG */
author = "Kyle Sanderson", author = "Kyle Sanderson",
description = "Translates SteamTools calls into SteamWorks calls.", description = "Translates SteamTools calls into SteamWorks calls.",
@@ -31,7 +31,7 @@ public Plugin:myinfo = {
url = "http://AlliedMods.net" url = "http://AlliedMods.net"
}; };
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{ {
CreateNative("Steam_IsVACEnabled", native_IsVACEnabled); CreateNative("Steam_IsVACEnabled", native_IsVACEnabled);
CreateNative("Steam_GetPublicIP", native_GetPublicIP); CreateNative("Steam_GetPublicIP", native_GetPublicIP);
@@ -46,50 +46,50 @@ public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
return APLRes_Success; return APLRes_Success;
} }
public native_IsVACEnabled(Handle:plugin, numParams) public int native_IsVACEnabled(Handle plugin, int numParams)
{ {
return SteamWorks_IsVACEnabled(); return SteamWorks_IsVACEnabled();
} }
public native_GetPublicIP(Handle:plugin, numParams) public int native_GetPublicIP(Handle plugin, int numParams)
{ {
new addr[4]; int addr[4];
SteamWorks_GetPublicIP(addr); SteamWorks_GetPublicIP(addr);
SetNativeArray(1, addr, sizeof(addr)); SetNativeArray(1, addr, sizeof(addr));
return 1; return 1;
} }
public native_SetGameDescription(Handle:plugin, numParams) public int native_SetGameDescription(Handle plugin, int numParams)
{ {
decl String:sDesc[PLATFORM_MAX_PATH]; char sDesc[PLATFORM_MAX_PATH];
GetNativeString(1, sDesc, sizeof(sDesc)); GetNativeString(1, sDesc, sizeof(sDesc));
return SteamWorks_SetGameDescription(sDesc); return SteamWorks_SetGameDescription(sDesc);
} }
public native_IsConnected(Handle:plugin, numParams) public int native_IsConnected(Handle plugin, int numParams)
{ {
return SteamWorks_IsConnected(); return SteamWorks_IsConnected();
} }
public native_SetRule(Handle:plugin, numParams) public int native_SetRule(Handle plugin, int numParams)
{ {
decl String:sKey[PLATFORM_MAX_PATH], String:sValue[PLATFORM_MAX_PATH]; char sKey[PLATFORM_MAX_PATH], sValue[PLATFORM_MAX_PATH];
GetNativeString(1, sKey, sizeof(sKey)); GetNativeString(1, sKey, sizeof(sKey));
GetNativeString(2, sValue, sizeof(sValue)); GetNativeString(2, sValue, sizeof(sValue));
return SteamWorks_SetRule(sKey, sValue); return SteamWorks_SetRule(sKey, sValue);
} }
public native_ClearRules(Handle:plugin, numParams) public int native_ClearRules(Handle plugin, int numParams)
{ {
return SteamWorks_ClearRules(); return SteamWorks_ClearRules();
} }
public native_ForceHeartbeat(Handle:plugin, numParams) public int native_ForceHeartbeat(Handle plugin, int numParams)
{ {
return SteamWorks_ForceHeartbeat(); return 0;
} }
public SteamWorks_SteamServersConnected() public void SteamWorks_SteamServersConnected()
{ {
if (GetForwardFunctionCount(g_hSteamServersConnected) == 0) if (GetForwardFunctionCount(g_hSteamServersConnected) == 0)
{ {
@@ -100,7 +100,7 @@ public SteamWorks_SteamServersConnected()
Call_Finish(); Call_Finish();
} }
public SteamWorks_SteamServersDisconnected() public void SteamWorks_SteamServersDisconnected()
{ {
if (GetForwardFunctionCount(g_hSteamServersDisconnected) == 0) if (GetForwardFunctionCount(g_hSteamServersDisconnected) == 0)
{ {