mirror of
https://github.com/FAUSheppy/skillbird-sourcemod
synced 2025-12-06 07:01:38 +01:00
2020 rewrite
This commit is contained in:
29
scripting/EventHandlers.sp
Normal file
29
scripting/EventHandlers.sp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "Interfaces.sp"
|
||||
|
||||
public Action:Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
|
||||
SubmittEventActiveClients();
|
||||
SubmittEventMapInformation();
|
||||
|
||||
/* generate session id for this round */
|
||||
SetConVarInt(FindConVar("session-id"), GetRandomInt(0, 200000));
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
SubmittEventActiveClients();
|
||||
new team_id = GetEventInt(event, "winner");
|
||||
SubmittEventWinnerTeam(team_id);
|
||||
SubmittEventRoundEnd();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
SubmittEventActiveClients();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Event_PlayerChangedTeam(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
SubmittEventActiveClients();
|
||||
return Plugin_Continue;
|
||||
}
|
||||
44
scripting/IngameMessages.sp
Normal file
44
scripting/IngameMessages.sp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <system2>
|
||||
|
||||
#define DEFAULT_LEN 64
|
||||
#define SNAME_LEN 256
|
||||
|
||||
#define VAR_TARGET_PORT "skillbird_target_port"
|
||||
|
||||
|
||||
public void PrintHttpResponse(bool success, const char[] error, System2HTTPRequest request, System2HTTPResponse response, HTTPRequestMethod method) {
|
||||
if (success) {
|
||||
if(response.StatusCode == 204 || response.StatusCode == 200){
|
||||
char[] content = new char[response.ContentLength + 1];
|
||||
response.GetContent(content, response.ContentLength + 1);
|
||||
PrintToChatAll("%s", content);
|
||||
}else if(response.StatusCode == 404){
|
||||
PrintToChatAll("skillbird:error:player_not_in_db");
|
||||
}else{
|
||||
PrintToChatAll("skillbird:error:scheduled_downtime:database_backup");
|
||||
}
|
||||
} else {
|
||||
PrintToChatAll("skillbird:error: backend unavailiable");
|
||||
}
|
||||
}
|
||||
|
||||
public void DisplayRating(int client){
|
||||
new String:url[SNAME_LEN];
|
||||
new String:clientId[DEFAULT_LEN];
|
||||
GetClientAuthId(client, AuthId_SteamID64, clientId, DEFAULT_LEN);
|
||||
Format(url, sizeof(url), "http://localhost:%d/get-player-rating-msg?id=%s", GetConVarInt(FindConVar(VAR_TARGET_PORT)), clientId);
|
||||
System2HTTPRequest httpRequest = new System2HTTPRequest(PrintHttpResponse, url);
|
||||
httpRequest.GET();
|
||||
}
|
||||
|
||||
public Action Timer_ConnectMessage(Handle timer, int client){
|
||||
DisplayRating(client);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action CommandRating(int client, int args){
|
||||
DisplayRating(client);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
99
scripting/Interfaces.sp
Normal file
99
scripting/Interfaces.sp
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma tabsize 0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <socket>
|
||||
#include <system2>
|
||||
#include <json>
|
||||
|
||||
#define DEFAULT_LEN 64
|
||||
#define SNAME_LEN 256
|
||||
#define ACTIVE_CLIENTS_LENGTH 8192
|
||||
#define VAR_TARGET_PORT "skillbird_target_port"
|
||||
#define VAR_SESSION_ID "session_id"
|
||||
|
||||
public void HttpResponseCallback(bool success, const char[] error, System2HTTPRequest request, System2HTTPResponse response, HTTPRequestMethod method) {
|
||||
}
|
||||
|
||||
public void SubmittViaHTTP(char[] jsonString){
|
||||
|
||||
new String:url[SNAME_LEN];
|
||||
Format(url, sizeof(url), "http://localhost:%d/single-event?session=%d", GetConVarInt(FindConVar(VAR_TARGET_PORT)), GetConVarInt(FindConVar(VAR_SESSION_ID)) );
|
||||
|
||||
System2HTTPRequest httpRequest = new System2HTTPRequest(HttpResponseCallback, url);
|
||||
httpRequest.SetHeader("Content-Type", "application/json");
|
||||
httpRequest.SetData(jsonString);
|
||||
httpRequest.POST();
|
||||
}
|
||||
|
||||
public void SubmittEventActiveClients(){
|
||||
|
||||
/* primary json */
|
||||
JSON_Object obj = new JSON_Object();
|
||||
obj.SetString("etype", "active_players");
|
||||
obj.SetInt("timestamp", GetTime());
|
||||
|
||||
/* generate players */
|
||||
JSON_Array players = new JSON_Array();
|
||||
|
||||
for(new i = 1; i <= MaxClients;i++){
|
||||
if(!IsClientConnected(i)){
|
||||
continue;
|
||||
}
|
||||
|
||||
JSON_Object player = new JSON_Object();
|
||||
|
||||
new String:clientId[DEFAULT_LEN];
|
||||
new String:clientName[SNAME_LEN];
|
||||
GetClientAuthId(i, AuthId_SteamID64, clientId, DEFAULT_LEN, true);
|
||||
GetClientName(i, clientName, SNAME_LEN);
|
||||
|
||||
player.SetString("id", clientId);
|
||||
player.SetString("name", clientName);
|
||||
player.SetInt("team", GetClientTeam(i));
|
||||
players.PushObject(player);
|
||||
}
|
||||
|
||||
/* add players array to primary object */
|
||||
obj.SetObject("players", players);
|
||||
|
||||
char output[2048];
|
||||
obj.Encode(output, sizeof(output));
|
||||
SubmittViaHTTP(output);
|
||||
}
|
||||
|
||||
public void SubmittEventMapInformation(){
|
||||
new String:mapname[DEFAULT_LEN];
|
||||
GetCurrentMap(mapname, sizeof(mapname));
|
||||
|
||||
JSON_Object obj = new JSON_Object();
|
||||
obj.SetString("etype", "map");
|
||||
obj.SetInt("timestamp", GetTime());
|
||||
obj.SetString("map", mapname);
|
||||
|
||||
char output[2048];
|
||||
obj.Encode(output, sizeof(output));
|
||||
SubmittViaHTTP(output);
|
||||
}
|
||||
|
||||
public void SubmittEventWinnerTeam(int team_id){
|
||||
|
||||
JSON_Object obj = new JSON_Object();
|
||||
obj.SetString("etype", "winner");
|
||||
obj.SetInt("timestamp", GetTime());
|
||||
obj.SetInt("winnerTeam", team_id);
|
||||
|
||||
char output[2048];
|
||||
obj.Encode(output, sizeof(output));
|
||||
SubmittViaHTTP(output);
|
||||
}
|
||||
|
||||
public void SubmittEventRoundEnd(){
|
||||
JSON_Object obj = new JSON_Object();
|
||||
obj.SetString("etype", "round_end");
|
||||
obj.SetInt("timestamp", GetTime());
|
||||
|
||||
char output[2048];
|
||||
obj.Encode(output, sizeof(output));
|
||||
SubmittViaHTTP(output);
|
||||
}
|
||||
46
scripting/Skillbird.sp
Normal file
46
scripting/Skillbird.sp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma tabsize 0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include "IngameMessages.sp"
|
||||
#include "EventHandlers.sp"
|
||||
|
||||
#define DEFAULT_LEN 64
|
||||
#define SNAME_LEN 256
|
||||
|
||||
#define VAR_SESSION_ID "session_id"
|
||||
#define VAR_TARGET_PORT "skillbird_target_port"
|
||||
|
||||
/* ---------------------- Plugin ---------------------- */
|
||||
public Plugin:myinfo = {
|
||||
name = "Skillbird",
|
||||
author = "FAUSheppy",
|
||||
description = "Module to interact with the skillbird framework",
|
||||
version = "3.0",
|
||||
url = "https://github.com/FAUSheppy/skillbird-sourcemod"
|
||||
};
|
||||
|
||||
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max){
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public OnPluginStart(){
|
||||
|
||||
/* Hook Events */
|
||||
HookEvent("round_end", Event_RoundEnd);
|
||||
HookEvent("player_team", Event_PlayerChangedTeam);
|
||||
HookEvent("player_disconnect", Event_PlayerDisconnect);
|
||||
|
||||
CreateConVar(VAR_SESSION_ID, "0", "Session in for this round");
|
||||
CreateConVar(VAR_TARGET_PORT, "6200", "Skillbird backend target port", FCVAR_PROTECTED);
|
||||
|
||||
RegConsoleCmd("rating", CommandRating);
|
||||
}
|
||||
|
||||
public OnClientAuthorized(client, const String:auth[]){
|
||||
CreateTimer(5.0, Timer_ConnectMessage, client);
|
||||
}
|
||||
|
||||
public OnMapStart(){
|
||||
SetConVarInt(FindConVar(VAR_SESSION_ID), GetRandomInt(1, 200000));
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#pragma tabsize 0
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <socket>
|
||||
#include "client.sp"
|
||||
#include "utils.sp"
|
||||
#include "query.sp"
|
||||
#define DEFAULT_LEN 64
|
||||
#define SNAME_LEN 256
|
||||
|
||||
/* ---------------------- Globals ---------------------- */
|
||||
static Handle:logfile;
|
||||
|
||||
/* ---------------------- Timers ---------------------- */
|
||||
public Action:Timer_MapStart(Handle:timer){
|
||||
LogActiveClients(logfile, "0x42,map_start_active,");
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Timer_RoundStart(Handle:timer){
|
||||
LogActiveClients(logfile, "0x42,round_start_active,");
|
||||
char map[SNAME_LEN];
|
||||
MapName("0x42,mapname,", map, sizeof(map));
|
||||
LogToOpenFile(logfile, map);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
/* ---------------------- Events ---------------------- */
|
||||
public Action:Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
LogActiveClients(logfile, "0x42,round_start_active,");
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
char eventWTeam[SNAME_LEN];
|
||||
EventWinnerTeam(event, "0x42,winner,", eventWTeam, sizeof(eventWTeam));
|
||||
LogToOpenFile(logfile, eventWTeam);
|
||||
LogActiveClients(logfile, "0x42,round_end_active,");
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
LogClientEventFormat(logfile, event, "0x42,disconnect,");
|
||||
LogActiveClients(logfile, "0x42,dc,");
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action:Event_PlayerChangedTeam(Handle:event, const String:name[], bool:dontBroadcast){
|
||||
LogActiveClients(logfile, "0x42,tc,");
|
||||
LogClientEventFormat(logfile, event, "0x42,teamchange,");
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public OnMapStart(){
|
||||
CreateTimer(5.0, Timer_MapStart);
|
||||
}
|
||||
|
||||
/* ---------------------- Plugin ---------------------- */
|
||||
public Plugin:myinfo = {
|
||||
name = "skillbird-data-collection,
|
||||
author = "FAUSheppy",
|
||||
description = "Module to collect data for the skillbird framework",
|
||||
version = "2.0",
|
||||
url = "https://github.com/FAUSheppy/skillbird-sourcemod"
|
||||
};
|
||||
|
||||
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max){
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public OnPluginStart(){
|
||||
|
||||
/* get and verify server name for output file */
|
||||
char server[SNAME_LEN];
|
||||
ServerName(server, sizeof(server));
|
||||
logfile = OpenFile(server, "at", false, NULL_STRING);
|
||||
if strlen(server) < 1:
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Computed Invalid Servername '%s'", server)
|
||||
|
||||
/* inial line */
|
||||
LogToOpenFile(logfile, "0x42,start");
|
||||
|
||||
/* Hook Events */
|
||||
HookEvent("round_end", Event_RoundEnd);
|
||||
HookEvent("player_team", Event_PlayerChangedTeam);
|
||||
HookEvent("player_disconnect", Event_PlayerDisconnect);
|
||||
}
|
||||
|
||||
public OnPluginEnd(){
|
||||
LogToOpenFile(logfile, "0x42,plugin unloaded");
|
||||
CloseHandle(logfile);
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/* no idea, but removes warnings */
|
||||
#pragma tabsize 0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <socket>
|
||||
#include "query.sp"
|
||||
#include "client.sp"
|
||||
#include "utils.sp"
|
||||
|
||||
#define DEFAULT_LEN 64
|
||||
#define INVALID 0
|
||||
|
||||
#define REBUILD_TEAM true
|
||||
#define NO_REBUILD_TEAM false
|
||||
|
||||
/* ---------------------- Hooks ---------------------- */
|
||||
public OnClientAuthorized(client,const String:auth[]){
|
||||
CreateTimer(5.0, Timer_ConnectMessage, client);
|
||||
CreateTimer(5.0, Timer_QueryTeam, client);
|
||||
}
|
||||
public OnScrambleTeams(){
|
||||
QueryTeam(GetSocket(), "NONE", REBUILD_TEAM);
|
||||
}
|
||||
|
||||
/* ---------------------- Timers ---------------------- */
|
||||
public Action Timer_ConnectMessage(Handle timer, int client){
|
||||
QueryRating(client);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
public Action Timer_QueryTeam(Handle timer, int client){
|
||||
char clientIDString[DEFAULT_LEN];
|
||||
ClientID(client, clientIDString, DEFAULT_LEN);
|
||||
QueryTeam(GetSocket(), clientIDString, false);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
/* ---------------------- Plugin ---------------------- */
|
||||
public Plugin:myinfo = {
|
||||
name = "skillbird-query",
|
||||
author = "FAUSheppy",
|
||||
description = "Module for interaction with the skillbird API",
|
||||
version = "2.0",
|
||||
url = "https://github.com/FAUSheppy/skillbird-sourcemod"
|
||||
};
|
||||
|
||||
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max){
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
public OnPluginStart(){
|
||||
CreateQuerySocket(7040);
|
||||
}
|
||||
|
||||
public OnPluginEnd(){
|
||||
if (GetSocket() == INVALID_HANDLE){
|
||||
return;
|
||||
}
|
||||
CloseHandle(GetSocket());
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#pragma tabsize 0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <socket>
|
||||
|
||||
#define DEFAULT_LEN 64
|
||||
#define SNAME_LEN 256
|
||||
#define ACTIVE_CLIENTS_LENGTH 8192
|
||||
|
||||
public void ClientName(const client,char[] buf,buflen){
|
||||
new String:name[DEFAULT_LEN];
|
||||
if(IsClientConnected(client)){
|
||||
GetClientName(client, name, DEFAULT_LEN);
|
||||
|
||||
/* get fucked if you use my seperators in your name */
|
||||
ReplaceString(name, DEFAULT_LEN,",","$");
|
||||
ReplaceString(name, DEFAULT_LEN,"|","&");
|
||||
ReplaceString(name, DEFAULT_LEN,"0x42","0x21");
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientID(int client, char[] buf, int buflen){
|
||||
if(IsClientConnected(client)){
|
||||
GetClientAuthId(client, AuthId_SteamID64, buf, DEFAULT_LEN, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void ActiveClients(char[] prefix, char[] buf, buflen){
|
||||
new String:str_tmp[ACTIVE_CLIENTS_LENGTH];
|
||||
for(new i = 1; i <= MaxClients;i++){
|
||||
new String:strCliID[DEFAULT_LEN];
|
||||
new String:strCliName[DEFAULT_LEN];
|
||||
ClientID(i, strCliID, DEFAULT_LEN);
|
||||
ClientName(i, strCliName, DEFAULT_LEN);
|
||||
Format(str_tmp, ACTIVE_CLIENTS_LENGTH,
|
||||
"%s|%s|%s|%d,", str_tmp, strCliID, strCliName, GetClientTeam(i));
|
||||
}
|
||||
Format(str_tmp, ACTIVE_CLIENTS_LENGTH, "%s%s", prefix,str_tmp);
|
||||
}
|
||||
|
||||
public void LogActiveClients(Handle:logfile, char[] prefix){
|
||||
char strActiveClients[ACTIVE_CLIENTS_LENGTH];
|
||||
ActiveClients(prefix, strActiveClients, ACTIVE_CLIENTS_LENGTH);
|
||||
LogToOpenFile(logfile, strActiveClients);
|
||||
}
|
||||
|
||||
public void LogClientEventFormat(Handle:logfile, Handle:event, char[] prefix){
|
||||
char strClientEvent[2*DEFAULT_LEN];
|
||||
ClientEventFormat(event, prefix, strClientEvent, 2*DEFAULT_LEN);
|
||||
LogToOpenFile(logfile, strClientEvent);
|
||||
|
||||
}
|
||||
|
||||
public void ClientEventFormat(Handle:event, char[] prefix, char[] buf, buflen){
|
||||
new client = GetClientOfUserId(GetEventInt(event, "userid"));
|
||||
char team[4];
|
||||
Format(team, sizeof(team), "%d", GetClientTeam(client));
|
||||
char tmp[DEFAULT_LEN*2];
|
||||
char strCliID[DEFAULT_LEN];
|
||||
ClientID(client, strCliID, DEFAULT_LEN);
|
||||
Format(tmp, 2*DEFAULT_LEN, "%s%s,%s", prefix, strCliID, team);
|
||||
}
|
||||
@@ -1,487 +0,0 @@
|
||||
// socket extension include file
|
||||
|
||||
#if defined _socket_included
|
||||
#endinput
|
||||
#endif
|
||||
#define _socket_included
|
||||
#include <core>
|
||||
|
||||
enum SocketType {
|
||||
SOCKET_TCP = 1,
|
||||
SOCKET_UDP,
|
||||
SOCKET_RAW
|
||||
}
|
||||
|
||||
#define EMPTY_HOST 1
|
||||
#define NO_HOST 2
|
||||
#define CONNECT_ERROR 3
|
||||
#define SEND_ERROR 4
|
||||
#define BIND_ERROR 5
|
||||
#define RECV_ERROR 6
|
||||
#define LISTEN_ERROR 7
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/******************************************** options ********************************************/
|
||||
/*************************************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Options available for SocketSetOption()
|
||||
*
|
||||
* @note modifying these options is not required for normal operation, you can skip the whole
|
||||
* section in most cases.
|
||||
*/
|
||||
enum SocketOption {
|
||||
/**
|
||||
* If this option is set the socket extension will try to concatenate SocketReceive callbacks.
|
||||
*
|
||||
* This will possibly lower the amount of callbacks passed to SourceMod plugins and improve the
|
||||
* performance. The socket extension will preserve the packet order.
|
||||
*
|
||||
* @note this doesn't prevent multiple callbacks, it only reduces them for high load.
|
||||
* @note this will not truncate packets below 4096 bytes, setting it lower will be ignored
|
||||
* @note set this option if you expect lots of data in a short timeframe
|
||||
* @note don't forget to set your buffer sizes at least to the value passed to this function, but
|
||||
* always at least to 4096
|
||||
*
|
||||
* @param cell_t 0(=default) to disable or max. chunk size including \0 terminator in bytes
|
||||
* @return bool true on success
|
||||
*/
|
||||
ConcatenateCallbacks = 1,
|
||||
/**
|
||||
* If this option is set the socket extension will enforce a mutex lock in the GameFrame() hook.
|
||||
*
|
||||
* This will ensure that callbacks will be processed every gameframe as fast as possible with the
|
||||
* drawback of potentially creating lag. It's not recommended to set this option for most cases.
|
||||
* If this option is not set the gameframe will be skipped if quietly obtaining a lock fails.
|
||||
*
|
||||
* @note combine this with CallbacksPerFrame for best performance
|
||||
* @note this option will affect all sockets from all plugins, use it with caution!
|
||||
*
|
||||
* @param bool whether to force locking or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
ForceFrameLock,
|
||||
/**
|
||||
* This will specify the maximum amount of callbacks processed in every gameframe.
|
||||
*
|
||||
* The default value for this option is 1, setting it higher will possibly increase networking
|
||||
* performance but may cause lag if it's set too high.
|
||||
* The amount of callbacks actually being processed is limited by not being able to quietly obtain
|
||||
* a lock (see ForceFrameLock) and the amount of callbacks in the queue.
|
||||
*
|
||||
* @note this option will affect all sockets from all plugins, use it with caution!
|
||||
*
|
||||
* @param cell_t maximum amount of callbacks per gameframe
|
||||
* @return bool true on success
|
||||
*/
|
||||
CallbacksPerFrame,
|
||||
/**
|
||||
* If this option is set the socket will be allowed to send broadcast messages in case the protocol
|
||||
* supports it. This is a wrapper for setting SO_BROADCAST.
|
||||
*
|
||||
* @param bool whether to allow broadcasting or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketBroadcast,
|
||||
/**
|
||||
* If this option is set SocketBind() will allow reusing local adresses in case the protocol
|
||||
* supports it. This is a wrapper for setting SO_REUSEADDR.
|
||||
*
|
||||
* @param bool whether to allow broadcasting or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketReuseAddr,
|
||||
/**
|
||||
* If this option is set the socket will try to keep the connection alive by periodically sending
|
||||
* messages if the protocol supports it. This is a wrapper for setting SO_KEEPALIVE.
|
||||
*
|
||||
* @param bool whether to allow broadcasting or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketKeepAlive,
|
||||
/**
|
||||
* This option specifies how long a socket will wait if it's being closed and its send buffer is
|
||||
* still filled. This is a wrapper for setting SO_LINGER.
|
||||
*
|
||||
* @param cell_t 0 (=default) to disable or time in s
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketLinger,
|
||||
/**
|
||||
* If this option is set out-of-band data will be inlined into the normal receive stream. This is a
|
||||
* wrapper for setting SO_OOBINLINE.
|
||||
*
|
||||
* @param bool whether to inline out-of-band data or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketOOBInline,
|
||||
/**
|
||||
* This option specifies how large the send buffer will be. This is a wrapper for setting
|
||||
* SO_SNDBUF.
|
||||
*
|
||||
* @param cell_t size in bytes
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketSendBuffer,
|
||||
/**
|
||||
* This option specifies how large the receive buffer will be. This is a wrapper for setting
|
||||
* SO_RCVBUF.
|
||||
*
|
||||
* @param cell_t size in bytes
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketReceiveBuffer,
|
||||
/**
|
||||
* If this option is set outgoing messages will ignore the default routing facilities if the
|
||||
* protocol implementation supports it. The remote site should be directly connected to the sender.
|
||||
* This is a wrapper for setting SO_DONTROUTE.
|
||||
*
|
||||
* @param bool whether to skip default routing or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketDontRoute,
|
||||
/**
|
||||
* This option specifies the minimum amount of data to receive before processing it. This is a
|
||||
* wrapper for setting SO_RCVLOWAT.
|
||||
*
|
||||
* @note this can probably block the extension, use it with caution!
|
||||
*
|
||||
* @param cell_t size in bytes
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketReceiveLowWatermark,
|
||||
/**
|
||||
* This option specifies how long a socket will try to receive data before it times out and
|
||||
* processes the data. This is a wrapper for setting SO_RCVTIMEO.
|
||||
*
|
||||
* @param cell_t 0 (=default) to disable or time in ms
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketReceiveTimeout,
|
||||
/**
|
||||
* This option specifies the minimum amount of data required in the send buffer before starting to
|
||||
* send it. This is a wrapper for setting SO_SNDLOWAT.
|
||||
*
|
||||
* @note this can probably block the extension, use it with caution!
|
||||
*
|
||||
* @param cell_t size in bytes
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketSendLowWatermark,
|
||||
/**
|
||||
* This option specifies how long a socket will try to send data before it times out and
|
||||
* retries it later. This is a wrapper for setting SO_SNDTIMEO.
|
||||
*
|
||||
* @param cell_t 0 (=default) to disable or time in ms
|
||||
* @return bool true on success
|
||||
*/
|
||||
SocketSendTimeout,
|
||||
/**
|
||||
* If this option is set the socket extension will display debugging messages in the server console/logs.
|
||||
*
|
||||
* @param bool whether to enable debugging or not
|
||||
* @return bool true on success
|
||||
*/
|
||||
DebugMode
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/******************************************* callbacks *******************************************/
|
||||
/*************************************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* triggered if a normal sockets finished connecting and is ready to be used
|
||||
*
|
||||
* @param socket The socket handle pointing to the calling socket
|
||||
* @param arg The argument set by SocketSetArg()
|
||||
* @noreturn
|
||||
*/
|
||||
//funcenum SocketConnectCB
|
||||
//{
|
||||
// public(Handle:socket, any:arg)
|
||||
//};
|
||||
|
||||
typeset SocketConnectCB
|
||||
{
|
||||
function void (Handle socket, any arg);
|
||||
};
|
||||
|
||||
/**
|
||||
* triggered if a listening socket received an incoming connection and is ready to be used
|
||||
*
|
||||
* @note The child-socket won't work until receive-, disconnect-, and errorcallback for it are set.
|
||||
*
|
||||
* @param Handle socket The socket handle pointing to the calling listen-socket
|
||||
* @param Handle newSocket The socket handle to the newly spawned child socket
|
||||
* @param String remoteIP The remote IP
|
||||
* @param any arg The argument set by SocketSetArg() for the listen-socket
|
||||
* @noreturn
|
||||
*/
|
||||
typeset SocketIncomingCB
|
||||
{
|
||||
function void (Handle socket, Handle newSocket, const char remoteIP[], int remotePort, any arg)
|
||||
};
|
||||
|
||||
/**
|
||||
* triggered if a socket receives data
|
||||
*
|
||||
* @note This is binary safe if you always use dataSize for operations on receiveData[]
|
||||
* @note packets may be split up into multiple chunks -> multiple calls to the receive callback
|
||||
* @note if not set otherwise by SocketSetOption(..., ConcatenateCallbacks, ...) receiveData will
|
||||
* never be longer than 4096 characters including \0 terminator
|
||||
*
|
||||
* @param Handle socket The socket handle pointing to the calling socket
|
||||
* @param String receiveData The data which arrived, 0-terminated at receiveData[dataSize]
|
||||
* @param cell_t dataSize The length of the arrived data excluding the 0-termination
|
||||
* @param any arg The argument set by SocketSetArg() for the socket
|
||||
* @noreturn
|
||||
*/
|
||||
typeset SocketReceiveCB
|
||||
{
|
||||
function void (Handle socket, const char receiveData[], const int dataSize, any arg)
|
||||
};
|
||||
|
||||
/**
|
||||
* called after a socket sent all items in its send queue successfully
|
||||
*
|
||||
* @param Handle socket The socket handle pointing to the calling socket
|
||||
* @param any arg The argument set by SocketSetArg() for the socket
|
||||
* @noreturn
|
||||
*/
|
||||
typeset SocketSendqueueEmptyCB
|
||||
{
|
||||
function void (Handle socket, any arg)
|
||||
};
|
||||
|
||||
/**
|
||||
* called if a socket has been properly disconnected by the remote side
|
||||
*
|
||||
* @note You should call CloseHandle(socket) or reuse the socket before this function ends
|
||||
*
|
||||
* @param Handle socket The socket handle pointing to the calling socket
|
||||
* @param any arg The argument set by SocketSetArg() for the socket
|
||||
* @noreturn
|
||||
*/
|
||||
typeset SocketDisconnectCB
|
||||
{
|
||||
function void (Handle socket, any arg)
|
||||
};
|
||||
|
||||
/**
|
||||
* called if an unrecoverable error occured, close the socket without an additional call to a disconnect callback
|
||||
*
|
||||
* @note You should call CloseHandle(socket) or reuse the socket before this function ends
|
||||
*
|
||||
* @param Handle socket The socket handle pointing to the calling socket
|
||||
* @param cell_t errorType The error type, see defines above
|
||||
* @param cell_t errorNum The errno, see errno.h for details
|
||||
* @param any arg The argument set by SocketSetArg() for the socket
|
||||
* @noreturn
|
||||
*/
|
||||
typeset SocketErrorCB
|
||||
{
|
||||
function void (Handle socket, const int errorType, const int errorNum, any arg)
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/******************************************** natives ********************************************/
|
||||
/*************************************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether a socket is connected or not.
|
||||
*
|
||||
* @param socket Socket handle to check
|
||||
* @return bool The connection status
|
||||
*/
|
||||
native bool:SocketIsConnected(Handle:socket);
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new socket.
|
||||
*
|
||||
* @note this function may be relatively expensive, reuse sockets if possible
|
||||
*
|
||||
* @param SocketType protocol The protocol to use, SOCKET_TCP is default
|
||||
* @param SocketErrorCB efunc The error callback
|
||||
* @return Handle The socket handle. Returns INVALID_HANDLE on failure
|
||||
*/
|
||||
native Handle:SocketCreate(SocketType:protocol=SOCKET_TCP, SocketErrorCB:efunc);
|
||||
|
||||
/**
|
||||
* Binds the socket to a local address
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param String hostname The hostname (or IP) to bind the socket to.
|
||||
* @param cell_t port The port to bind the socket to.
|
||||
* @return bool true on success
|
||||
*/
|
||||
native bool:SocketBind(Handle:socket, const String:hostname[], port);
|
||||
|
||||
/**
|
||||
* Connects a socket
|
||||
*
|
||||
* @note this native is threaded, it may be still running after it executed, use the connect callback
|
||||
* @note invokes the SocketError callback with errorType = CONNECT_ERROR or EMPTY_HOST if it fails
|
||||
* @note invokes the SocketConnect callback if it succeeds
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param SocketConnectCB cfunc The connect callback
|
||||
* @param SocketReceiveCB rfunc The receive callback
|
||||
* @param SocketDisconnectCB dfunc The disconnect callback
|
||||
* @param String hostname The hostname (or IP) to connect to.
|
||||
* @param cell_t port The port to connect to.
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketConnect(Handle:socket, SocketConnectCB:cfunc, SocketReceiveCB:rfunc, SocketDisconnectCB:dfunc, const String:hostname[], port);
|
||||
|
||||
/**
|
||||
* Disconnects a socket
|
||||
*
|
||||
* @note this will not close the handle, the socket will be reset to a state similar to after SocketCreate()
|
||||
* @note this won't trigger any disconnect/error callbacks
|
||||
*
|
||||
* @noreturn
|
||||
*/
|
||||
native bool:SocketDisconnect(Handle:socket);
|
||||
|
||||
/**
|
||||
* Makes a socket listen for incoming connections
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param SocketIncomingCB ifunc The callback for incoming connections
|
||||
* @return bool true on success
|
||||
*/
|
||||
native bool:SocketListen(Handle:socket, SocketIncomingCB:ifunc);
|
||||
|
||||
/**
|
||||
* Sends data through the socket.
|
||||
*
|
||||
* @note specify size for binary safe operation
|
||||
* @note if size is not specified the \0 terminator will not be included
|
||||
* @note This native is threaded, it may be still running after it executed (not atomic).
|
||||
* @note Use the SendqueueEmpty callback to determine when all data has been successfully sent.
|
||||
* @note The socket extension will ensure that the data will be send in the correct order and split
|
||||
* the data if required.
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param String data The data to send.
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSend(Handle:socket, const String:data[], size=-1);
|
||||
|
||||
/**
|
||||
* Sends UDP data through the socket to a specific destination.
|
||||
*
|
||||
* @note specify size for binary safe operation
|
||||
* @note if size is not specified the \0 terminator will not be included
|
||||
* @note This native is threaded, it may be still running after it executed (not atomic).
|
||||
* @note Use the SendqueueEmpty callback to determine when all data has been successfully sent.
|
||||
* @note The socket extension will ensure that the data will be send in the correct order and split
|
||||
* the data if required.
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param String data The data to send.
|
||||
* @param String hostname The hostname (or IP) to send to.
|
||||
* @param cell_t port The port to send to.
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSendTo(Handle:socket, const String:data[], size=-1, const String:hostname[], port);
|
||||
|
||||
/**
|
||||
* Set a socket option.
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used. May be INVALID_HANDLE if not essential.
|
||||
* @param SocketOption option The option to modify (see enum SocketOption for details).
|
||||
* @param cellt_ value The value to set the option to.
|
||||
* @return cell_t 1 on success.
|
||||
*/
|
||||
native SocketSetOption(Handle:socket, SocketOption:option, value);
|
||||
|
||||
|
||||
/**
|
||||
* Defines the callback function for when the socket receives data
|
||||
*
|
||||
* @note this is only useful and required for child-sockets spawned by listen-sockets
|
||||
* (otherwise you already set it in SocketConnect())
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param SocketReceiveCB rfunc The receive callback
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSetReceiveCallback(Handle:socket, SocketReceiveCB:rfunc);
|
||||
|
||||
/**
|
||||
* Defines the callback function for when the socket sent all items in its send queue
|
||||
*
|
||||
* @note this must be called AFTER sending (queueing) the data
|
||||
* @note if no send-data is queued this will fire the callback itself
|
||||
* @note the callback is guaranteed to fire
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param SocketDisconnectCB dfunc The disconnect callback
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSetSendqueueEmptyCallback(Handle:socket, SocketSendqueueEmptyCB:sfunc);
|
||||
|
||||
/**
|
||||
* Defines the callback function for when the socket was properly disconnected by the remote side
|
||||
*
|
||||
* @note this is only useful and required for child-sockets spawned by listen-sockets
|
||||
* (otherwise you already set it in SocketConnect())
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param SocketDisconnectCB dfunc The disconnect callback
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSetDisconnectCallback(Handle:socket, SocketDisconnectCB:dfunc);
|
||||
|
||||
/**
|
||||
* Defines the callback function for when the socket triggered an error
|
||||
*
|
||||
* @note this is only useful and required for child-sockets spawned by listen-sockets
|
||||
* (otherwise you already set it in SocketCreate())
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param SocketErrorCB efunc The error callback
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSetErrorCallback(Handle:socket, SocketErrorCB:efunc);
|
||||
|
||||
|
||||
/**
|
||||
* Sets the argument being passed to callbacks
|
||||
*
|
||||
* @param Handle socket The handle of the socket to be used.
|
||||
* @param any arg The argument to set
|
||||
* @noreturn
|
||||
*/
|
||||
native SocketSetArg(Handle:socket, any:arg);
|
||||
|
||||
/**
|
||||
* Retrieve the local system's hostname as the command "hostname" does.
|
||||
*
|
||||
* @param dest Destination string buffer to copy to.
|
||||
* @param destLen Destination buffer length (includes null terminator).
|
||||
*
|
||||
* @return 1 on success
|
||||
*/
|
||||
native SocketGetHostName(String:dest[], destLen);
|
||||
|
||||
/**
|
||||
* _________________Do not edit below this line!_______________________
|
||||
*/
|
||||
public Extension:__ext_smsock =
|
||||
{
|
||||
name = "Socket",
|
||||
file = "socket.ext",
|
||||
#if defined AUTOLOAD_EXTENSIONS
|
||||
autoload = 1,
|
||||
#else
|
||||
autoload = 0,
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
/* no idea, but removes warnings */
|
||||
#pragma tabsize 0
|
||||
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
#include <socket>
|
||||
|
||||
#define DEFAULT_LEN 64
|
||||
#define TAG_LEN 64
|
||||
#define INDEX_SEP ","
|
||||
#define ACTIVE_CLIENTS_LENGTH 8192
|
||||
|
||||
#define NO_PREFIX ""
|
||||
|
||||
/* ---------------------- Globals ---------------------- */
|
||||
static Handle:socket
|
||||
|
||||
/* ---------------------- Queries ---------------------- */
|
||||
public QueryTeam(Handle:socketl, char[] steamid, bool rebuild){
|
||||
/* steamid may be used to only query a single player's new team */
|
||||
char strActiveClients[ACTIVE_CLIENTS_LENGTH];
|
||||
char request[ACTIVE_CLIENTS_LENGTH + DEFAULT_LEN*2];
|
||||
|
||||
ActiveClients(NO_PREFIX, strActiveClients, ACTIVE_CLIENTS_LENGTH);
|
||||
if(rebuild){
|
||||
Format(request, sizeof(request), "rebuildteam,%s", strActiveClients);
|
||||
}else{
|
||||
Format(request, sizeof(request), "getteam,%s,%s", steamid, strActiveClients);
|
||||
}
|
||||
SocketSend(socket, request);
|
||||
}
|
||||
|
||||
public QueryRating(client){
|
||||
if(client>0 && IsClientConnected(client)){
|
||||
/* get steamid first */
|
||||
char pid[DEFAULT_LEN];
|
||||
GetClientAuthId(client, AuthId_SteamID64, pid, DEFAULT_LEN, true);
|
||||
/* build the request and send */
|
||||
char request[2*DEFAULT_LEN];
|
||||
Format(request, sizeof(request), "player,%s", pid);
|
||||
SocketSend(socket, request);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------- Receive ---------------------- */
|
||||
public CaseSwitchReceive(Handle:socketl, String:receiveData[], const dataSize, any:arg) {
|
||||
char tag[TAG_LEN];
|
||||
|
||||
//TODO why is receiveData and array of strings?!?!
|
||||
SplitString(receiveData, INDEX_SEP, tag, TAG_LEN);
|
||||
|
||||
if(StrEqual(tag, "BALANCE_SINGLE")){
|
||||
RebuildTeams(INDEX_SEP, "|",receiveData);
|
||||
}else if(StrEqual(tag, "BALANCE_TEAMS")){
|
||||
RebuildTeams(INDEX_SEP, "|",receiveData);
|
||||
}else if(StrEqual(tag, "RATING_SINGLE")){
|
||||
char ratingInfo[DEFAULT_LEN];
|
||||
SplitString(receiveData, INDEX_SEP, ratingInfo, DEFAULT_LEN);
|
||||
PrintToChatAll("%s", receiveData);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------- Socket ---------------------- */
|
||||
public CreateQuerySocket(int port){
|
||||
socket = SocketCreate(SOCKET_TCP, OnSocketError);
|
||||
SocketConnect(socket, OnSocketConnected, CaseSwitchReceive, OnSocketDisconnected, "127.0.0.1", port);
|
||||
}
|
||||
|
||||
public Action AttemptReconnectSocket(Handle:socketl){
|
||||
if(socket != INVALID_HANDLE){
|
||||
CloseHandle(socketl);
|
||||
}else{
|
||||
socket = SocketCreate(SOCKET_TCP,OnSocketError);
|
||||
if(socket != INVALID_HANDLE){
|
||||
SocketConnect(socket, OnSocketConnected, CaseSwitchReceive,
|
||||
OnSocketDisconnected, "127.0.0.1", 7040);
|
||||
}
|
||||
}
|
||||
/* check if socket is connected */
|
||||
if(SocketIsConnected(socketl)){
|
||||
return Plugin_Stop;
|
||||
}else{
|
||||
return Plugin_Continue;
|
||||
}
|
||||
}
|
||||
|
||||
public Handle GetSocket(){
|
||||
return socket;
|
||||
}
|
||||
|
||||
public OnSocketConnected(Handle:socketl,any:arg) {
|
||||
}
|
||||
|
||||
public OnSocketDisconnected(Handle:socketl,any:arg) {
|
||||
CreateTimer(10.0, AttemptReconnectSocket, socketl, TIMER_REPEAT);
|
||||
}
|
||||
|
||||
public OnSocketError(Handle:socketl, const errorType, const errorNum,any:arg) {
|
||||
LogError("socket error %d (errno %d)", errorType, errorNum);
|
||||
CloseHandle(socket);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#define DEFAULT_LEN 64
|
||||
#define MAX_PLAYERS 32
|
||||
#define MAX_LENGTH_ID_TEAM 128
|
||||
|
||||
public void EventWinnerTeam(Handle:event, char[] prefix, char[] buf, int buflen){
|
||||
new team_id = GetEventInt(event, "winner");
|
||||
Format(buf, buflen, "%s,%d", prefix, team_id);
|
||||
}
|
||||
|
||||
public void MapName(char[] prefix, char[] buf, int buflen){
|
||||
new String:map[DEFAULT_LEN];
|
||||
GetCurrentMap(map, sizeof(map));
|
||||
Format(buf, buflen, "%s%s", prefix, map);
|
||||
}
|
||||
|
||||
public void ServerName(char[] buf,int buflen){
|
||||
new String:sname[SNAME_LEN];
|
||||
ConVar servername = FindConVar("hostname");
|
||||
servername.GetString(sname,SNAME_LEN);
|
||||
ReplaceString(sname, SNAME_LEN," ","");
|
||||
ReplaceString(sname, SNAME_LEN,"[","");
|
||||
ReplaceString(sname, SNAME_LEN,"]","");
|
||||
ReplaceString(sname, SNAME_LEN,"(","");
|
||||
ReplaceString(sname, SNAME_LEN,")","");
|
||||
}
|
||||
|
||||
|
||||
public void RebuildTeams(char[] SEP, char[] SUBSEP, char[] input){
|
||||
|
||||
/* buffers for later */
|
||||
char buffers[MAX_PLAYERS][MAX_LENGTH_ID_TEAM];
|
||||
new parts = ExplodeString(input, SEP, buffers, MAX_PLAYERS, MAX_LENGTH_ID_TEAM);
|
||||
new StringMap:map;
|
||||
|
||||
/* i=1 to skip tag in something like TAG,player|team,player|team,... */
|
||||
for(new i = 1; i < parts; i++){
|
||||
char idAndTeam[2][MAX_LENGTH_ID_TEAM];
|
||||
ExplodeString(buffers[i], SUBSEP, idAndTeam, 2, MAX_LENGTH_ID_TEAM);
|
||||
map.SetString(idAndTeam[0], idAndTeam[1], true);
|
||||
}
|
||||
|
||||
/* hasmap indirection nessesary cause there doesnt exist a "ClientByAuthID-like function */
|
||||
for(new client=0; client <MaxClients; client++){
|
||||
/* get Steam-Id to match */
|
||||
char sid[DEFAULT_LEN];
|
||||
if(client>0 && IsClientConnected(client)){
|
||||
GetClientAuthId(client, AuthId_SteamID64, sid, DEFAULT_LEN, true);
|
||||
}
|
||||
/* get team for steamid and balance*/
|
||||
char team[MAX_LENGTH_ID_TEAM];
|
||||
if(map.GetString(sid, team, sizeof(team))){
|
||||
new tid = StringToInt(team);
|
||||
if(tid>0 && IsClientConnected(client)){
|
||||
ChangeClientTeam(client, tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user