//==================================================================================================== // // Name: UNLOZE Forum Integration. // Author: .George & zaCade (Original by Botox) // Description: Handles forum access ingame. // //==================================================================================================== #include #include #include #include //#define UNLOZE_APIKEY here #include #pragma newdecls required /* STRINGS */ char G_sGroup[MAXPLAYERS+1][64]; char G_sName[MAXPLAYERS+1][32]; /* BOOLS */ bool G_bPreAdminChecked[MAXPLAYERS+1]; bool G_bResponseFailed[MAXPLAYERS+1]; bool G_bResponsePassed[MAXPLAYERS+1]; //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Plugin myinfo = { name = "UNLOZE Forum Integration", author = ".George & zaCade (Original by Botox)", description = "Handles forum access ingame", version = "1.2.1" }; //---------------------------------------------------------------------------------------------------- // Purpose: Late load //---------------------------------------------------------------------------------------------------- public void OnPluginStart() { /* Late load */ for(int client = 1; client <= MaxClients; client++) { if(IsClientConnected(client)) OnClientConnected(client); if(IsClientAuthorized(client) && !IsFakeClient(client)) { char sSteamID32[32]; if(GetClientAuthId(client, AuthId_Steam2, sSteamID32, sizeof(sSteamID32))) OnClientAuthorized(client, sSteamID32); } } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { CreateNative("AsyncHasSteamIDReservedSlot", Native_AsyncHasSteamIDReservedSlot); CreateNative("GetClientForumName", Native_GetClientForumName); RegPluginLibrary("UNLOZE_ForumIntegration"); return APLRes_Success; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnRebuildAdminCache(AdminCachePart part) { if (part != AdminCache_Admins) return; CreateTimer(1.0, OnRebuildAdminCachePost, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action OnRebuildAdminCachePost(Handle hTimer) { for (int client = 1; client <= MaxClients; client++) { if(G_bResponsePassed[client] && G_bPreAdminChecked[client]) ApplyGroupFlags(client); } return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientConnected(int client) { G_bPreAdminChecked[client] = false; G_bResponseFailed[client] = false; G_bResponsePassed[client] = false; G_sGroup[client][0] = 0; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientDisconnect(int client) { G_bPreAdminChecked[client] = false; G_bResponseFailed[client] = false; G_bResponsePassed[client] = false; G_sGroup[client][0] = 0; G_sName[client][0] = 0; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientAuthorized(int client, const char[] sSteamID32) { if (IsFakeClient(client)) return; char sSteamID64[32]; SteamID32toSteamID64(sSteamID32, sSteamID64, sizeof(sSteamID64)); char sRequest[256]; FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64); int iSerial = GetClientSerial(client); char sPath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_forum_%d.tmp", iSerial); HTTPRequest hRequest = new HTTPRequest(sRequest); hRequest.DownloadFile(sPath, OnClientAuthorized_OnDownloaded, iSerial); } //---------------------------------------------------------------------------------------------------- // Purpose: ripexts HTTPResponse only exposes the body as decoded JSON, unlike // SteamWorks' GetHTTPResponseBodyCallback which handed back a plain char[]. // Rather than change private_api.php's response format, we use // HTTPRequest.DownloadFile() to write the raw "GROUP\r\nNAME" body to a temp // file and parse it back exactly like the original SteamWorks version did. //---------------------------------------------------------------------------------------------------- public void OnClientAuthorized_OnDownloaded(HTTPStatus status, any iSerial) { char sPath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_forum_%d.tmp", iSerial); int client = GetClientFromSerial(iSerial); if (!client) //Player disconnected. { DeleteFile(sPath); return; } if (status != HTTPStatus_OK) { DeleteFile(sPath); G_bResponseFailed[client] = true; if (G_bPreAdminChecked[client]) NotifyPostAdminCheck(client); return; } char sGroup[64], sName[32]; sGroup[0] = '\0'; sName[0] = '\0'; File hFile = OpenFile(sPath, "r"); if (hFile != null) { hFile.ReadLine(sGroup, sizeof(sGroup)); hFile.ReadLine(sName, sizeof(sName)); delete hFile; } DeleteFile(sPath); TrimString(sGroup); StripQuotes(sGroup); TrimString(sName); StripQuotes(sName); LogMessage("reached sData with status 200: %s", sGroup); if (strlen(sName) > 0) strcopy(G_sName[client], sizeof(G_sName[]), sName); if (!StrEqual(sGroup, "NOGROUP")) { strcopy(G_sGroup[client], sizeof(G_sGroup[]), sGroup); G_bResponsePassed[client] = true; if (G_bPreAdminChecked[client]) NotifyPostAdminCheck(client); } else G_bResponseFailed[client] = true; //users with just a forum name did not pass the VIP check! so the response "failed" (but we store their forum name for later!) } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action OnClientPreAdminCheck(int client) { G_bPreAdminChecked[client] = true; if (G_bResponsePassed[client] || G_bResponseFailed[client]) return Plugin_Continue; RunAdminCacheChecks(client); return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnClientPostAdminFilter(int client) { ApplyGroupFlags(client); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock void ApplyGroupFlags(int client) { if (!G_bResponsePassed[client]) return; AdminId AdmID; GroupId GrpID; if ((AdmID = GetUserAdmin(client)) == INVALID_ADMIN_ID) { LogMessage("Creating new admin for %L", client); AdmID = CreateAdmin(); SetUserAdmin(client, AdmID, true); } if ((GrpID = FindAdmGroup(G_sGroup[client])) != INVALID_GROUP_ID) { if (AdminInheritGroup(AdmID, GrpID)) { LogMessage("%L added to group %s", client, G_sGroup[client]); } } else { LogMessage("%L group not found %s", client, G_sGroup[client]); } } public int Native_GetClientForumName(Handle plugin, int numParams) { int len = GetNativeCell(2); int client = GetNativeCell(1); SetNativeString(2, G_sName[client], len+1); return 0; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public int Native_AsyncHasSteamIDReservedSlot(Handle plugin, int numParams) { char sSteamID32[32]; GetNativeString(1, sSteamID32, sizeof(sSteamID32)); Function callback; //https://github.com/alliedmodders/sourcepawn/issues/778 callback = GetNativeFunction(2); any data; data = GetNativeCell(3); char sSteamID64[32]; SteamID32toSteamID64(sSteamID32, sSteamID64, sizeof(sSteamID64)); char sRequest[256]; FormatEx(sRequest, sizeof(sRequest), "https://unloze.com/api/private_api.php?api_key=%s&steam_id=%s", UNLOZE_APIKEY, sSteamID64); DataPack hDataPack = new DataPack(); hDataPack.WriteString(sSteamID32); hDataPack.WriteFunction(callback); hDataPack.WriteCell(plugin); hDataPack.WriteCell(data); // The DataPack handle value is unique among currently-open handles, so it // doubles as a collision-free key for the temp file this specific // in-flight request will use. char sPath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_reslot_%d.tmp", view_as(hDataPack)); HTTPRequest hRequest = new HTTPRequest(sRequest); hRequest.DownloadFile(sPath, Native_AsyncHasSteamIDReservedSlot_OnDownloaded, hDataPack); return 0; } //---------------------------------------------------------------------------------------------------- // Purpose: same DownloadFile-to-tempfile approach as OnClientAuthorized_OnDownloaded // above - keeps private_api.php's raw "GROUP\r\nNAME" text response untouched. //---------------------------------------------------------------------------------------------------- public void Native_AsyncHasSteamIDReservedSlot_OnDownloaded(HTTPStatus status, DataPack hDataPack) { char sPath[PLATFORM_MAX_PATH]; BuildPath(Path_SM, sPath, sizeof(sPath), "data/unloze_reslot_%d.tmp", view_as(hDataPack)); char sData[32] = "NOGROUP"; if (status == HTTPStatus_OK) { File hFile = OpenFile(sPath, "r"); if (hFile != null) { hFile.ReadLine(sData, sizeof(sData)); delete hFile; } } DeleteFile(sPath); hDataPack.Reset(); char sSteamID32[32]; hDataPack.ReadString(sSteamID32, sizeof(sSteamID32)); Function callback; callback = hDataPack.ReadFunction(); Handle plugin; plugin = hDataPack.ReadCell(); any data; data = hDataPack.ReadCell(); char splitData[32]; TrimString(sData); StripQuotes(sData); SplitString(sData, "\r\n", splitData, sizeof(splitData)); int result; if (StrEqual(splitData, "Game-Donator", false)) result = 1; else result = 0; Call_StartFunction(plugin, callback); Call_PushString(sSteamID32); Call_PushCell(result); Call_PushCell(data); Call_Finish(); delete hDataPack; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock bool SteamID32toSteamID64(const char[] sSteamID32, char[] sSteamID64, int iSize) { if (strlen(sSteamID32) < 11 || strncmp(sSteamID32[0], "STEAM_", 6)) { sSteamID64[0] = 0; return false; } int iUpper = 765611979; int isSteam64ID = StringToInt(sSteamID32[10]) * 2 + 60265728 + sSteamID32[8] - 48; int iDiv = isSteam64ID / 100000000; int iIdx = 9 - (iDiv ? (iDiv / 10 + 1) : 0); iUpper += iDiv; IntToString(isSteam64ID, sSteamID64[iIdx], iSize - iIdx); iIdx = sSteamID64[9]; IntToString(iUpper, sSteamID64, iSize); sSteamID64[9] = iIdx; return true; } stock bool IsValidClient(int client) { if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client)) return true; return false; }