mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 22:53:21 +00:00
More reconnect logic
This commit is contained in:
parent
863f5d1ce6
commit
41a3c4b06f
2 changed files with 112 additions and 44 deletions
Binary file not shown.
|
@ -36,8 +36,9 @@ ConVar cvar_debug;
|
||||||
ConVar cvar_gamemode; char gamemode[32];
|
ConVar cvar_gamemode; char gamemode[32];
|
||||||
ConVar cvar_difficulty; int gameDifficulty;
|
ConVar cvar_difficulty; int gameDifficulty;
|
||||||
ConVar cvar_maxplayers, cvar_visibleMaxPlayers;
|
ConVar cvar_maxplayers, cvar_visibleMaxPlayers;
|
||||||
ConVar cvar_address; char serverIp[16] = "127.0.0.1"; int serverPort = DEFAULT_SERVER_PORT;
|
ConVar cvar_address; char serverIp[32] = "127.0.0.1"; int serverPort = DEFAULT_SERVER_PORT;
|
||||||
ConVar cvar_authToken; char authToken[256];
|
ConVar cvar_authToken; char authToken[256];
|
||||||
|
ConVar cvar_hostPort;
|
||||||
|
|
||||||
char currentMap[64];
|
char currentMap[64];
|
||||||
int numberOfPlayers = 0;
|
int numberOfPlayers = 0;
|
||||||
|
@ -54,18 +55,25 @@ int playerJoinTime[MAXPLAYERS+1];
|
||||||
Handle updateHealthTimer[MAXPLAYERS+1];
|
Handle updateHealthTimer[MAXPLAYERS+1];
|
||||||
Handle updateItemTimer[MAXPLAYERS+1];
|
Handle updateItemTimer[MAXPLAYERS+1];
|
||||||
Handle receiveTimeoutTimer = null;
|
Handle receiveTimeoutTimer = null;
|
||||||
int pendingTries = 3;
|
int pendingAuthTries = 3;
|
||||||
|
|
||||||
bool lateLoaded;
|
|
||||||
|
|
||||||
Socket g_socket;
|
Socket g_socket;
|
||||||
int g_lastPayloadSent;
|
int g_lastPayloadSent;
|
||||||
enum AuthState {
|
enum AuthState {
|
||||||
Auth_Fail = -1,
|
Auth_Fail = -1,
|
||||||
|
Auth_Inactive,
|
||||||
Auth_Pending,
|
Auth_Pending,
|
||||||
|
Auth_PendingResponse,
|
||||||
Auth_Success,
|
Auth_Success,
|
||||||
}
|
}
|
||||||
AuthState authState;
|
char AUTH_STATE_LABEL[5][] = {
|
||||||
|
"failed",
|
||||||
|
"inactive",
|
||||||
|
"waiting connect",
|
||||||
|
"pending response",
|
||||||
|
"success"
|
||||||
|
};
|
||||||
|
AuthState authState = Auth_Inactive;
|
||||||
enum GameState {
|
enum GameState {
|
||||||
State_None,
|
State_None,
|
||||||
State_Transitioning = 1,
|
State_Transitioning = 1,
|
||||||
|
@ -76,13 +84,13 @@ enum PanelSettings {
|
||||||
Setting_None = 0,
|
Setting_None = 0,
|
||||||
Setting_DisableWithNoViewers = 1
|
Setting_DisableWithNoViewers = 1
|
||||||
}
|
}
|
||||||
GameState g_gameState;
|
GameState g_gameState = State_Hibernating;
|
||||||
#define BUFFER_SIZE 2048
|
#define BUFFER_SIZE 2048
|
||||||
Buffer sendBuffer;
|
Buffer sendBuffer;
|
||||||
Buffer receiveBuffer; // Unfortunately there's no easy way to have this not be the same as BUFFER_SIZE
|
Buffer receiveBuffer; // Unfortunately there's no easy way to have this not be the same as BUFFER_SIZE
|
||||||
|
|
||||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
||||||
lateLoaded = late;
|
// lateLoaded = late;
|
||||||
return APLRes_Success;
|
return APLRes_Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +120,8 @@ public void OnPluginStart() {
|
||||||
cvar_gamemode.AddChangeHook(OnCvarChanged);
|
cvar_gamemode.AddChangeHook(OnCvarChanged);
|
||||||
cvar_gamemode.GetString(gamemode, sizeof(gamemode));
|
cvar_gamemode.GetString(gamemode, sizeof(gamemode));
|
||||||
|
|
||||||
|
cvar_hostPort = FindConVar("hostport");
|
||||||
|
|
||||||
cvar_difficulty = FindConVar("z_difficulty");
|
cvar_difficulty = FindConVar("z_difficulty");
|
||||||
cvar_difficulty.AddChangeHook(OnCvarChanged);
|
cvar_difficulty.AddChangeHook(OnCvarChanged);
|
||||||
gameDifficulty = GetDifficultyInt();
|
gameDifficulty = GetDifficultyInt();
|
||||||
|
@ -151,9 +161,24 @@ public void OnPluginStart() {
|
||||||
CreateTimer(300.0, Timer_FullSync, _, TIMER_REPEAT);
|
CreateTimer(300.0, Timer_FullSync, _, TIMER_REPEAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stock void Debug(const char[] format, any ...) {
|
||||||
|
if(!cvar_debug.BoolValue) return;
|
||||||
|
char buffer[254];
|
||||||
|
VFormat(buffer, sizeof(buffer), format, 2);
|
||||||
|
PrintToServer("[AdminPanel] debug: %s", buffer);
|
||||||
|
}
|
||||||
|
stock void Log(const char[] format, any ...) {
|
||||||
|
char buffer[254];
|
||||||
|
VFormat(buffer, sizeof(buffer), format, 2);
|
||||||
|
PrintToServer("[AdminPanel] %s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
Action Timer_FullSync(Handle h) {
|
Action Timer_FullSync(Handle h) {
|
||||||
if(CanSendPayload(true)) {
|
if(CanSendPayload(true)) {
|
||||||
SendFullSync();
|
SendFullSync();
|
||||||
|
} else if(authState != Auth_Success && authState != Auth_Fail) {
|
||||||
|
// Try to reconnect if we aren't active
|
||||||
|
ConnectSocket();
|
||||||
}
|
}
|
||||||
return Plugin_Continue;
|
return Plugin_Continue;
|
||||||
}
|
}
|
||||||
|
@ -172,15 +197,19 @@ void TriggerItemUpdate(int client) {
|
||||||
updateItemTimer[client] = CreateTimer(1.0, Timer_UpdateItems, client);
|
updateItemTimer[client] = CreateTimer(1.0, Timer_UpdateItems, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSocketError(Socket socket, int errorType, int errorNumber, int any) {
|
void OnSocketError(Socket socket, int errorType, int errorNumber, int attempt) {
|
||||||
PrintToServer("[AdminPanel] Socket Error %d %d", errorType, errorNumber);
|
PrintToServer("[AdminPanel] Socket Error %d %d", errorType, errorNumber);
|
||||||
if(!socket.Connected) {
|
if(!socket.Connected) {
|
||||||
PrintToServer("[AdminPanel] Lost connection to socket, reconnecting", errorType, errorNumber);
|
PrintToServer("[AdminPanel] Lost connection to socket, reconnecting", errorType, errorNumber);
|
||||||
ConnectSocket();
|
float nextAttempt = Exponential(float(attempt) / 2.0) + 2.0;
|
||||||
|
if(nextAttempt > MAX_ATTEMPT_TIMEOUT) nextAttempt = MAX_ATTEMPT_TIMEOUT;
|
||||||
|
PrintToServer("[AdminPanel] Disconnected, retrying in %.0f seconds", nextAttempt);
|
||||||
|
g_socket.SetArg(attempt + 1);
|
||||||
|
CreateTimer(nextAttempt, Timer_Reconnect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendFullSync() {
|
bool SendFullSync() {
|
||||||
if(StartPayload(true)) {
|
if(StartPayload(true)) {
|
||||||
AddGameRecord();
|
AddGameRecord();
|
||||||
int stage = L4D2_GetCurrentFinaleStage();
|
int stage = L4D2_GetCurrentFinaleStage();
|
||||||
|
@ -190,21 +219,21 @@ void SendFullSync() {
|
||||||
|
|
||||||
// Resend all players
|
// Resend all players
|
||||||
SendPlayers();
|
SendPlayers();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int arg) {
|
void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int arg) {
|
||||||
receiveBuffer.FromArray(receiveData, dataSize);
|
receiveBuffer.FromArray(receiveData, dataSize);
|
||||||
LiveRecordResponse response = view_as<LiveRecordResponse>(receiveBuffer.ReadByte());
|
LiveRecordResponse response = view_as<LiveRecordResponse>(receiveBuffer.ReadByte());
|
||||||
if(cvar_debug.BoolValue) {
|
Debug("Received response=%d size=%d bytes", response, dataSize);
|
||||||
PrintToServer("[AdminPanel] Received response=%d size=%d bytes", response, dataSize);
|
if(authState == Auth_PendingResponse) {
|
||||||
}
|
|
||||||
if(authState == Auth_Pending) {
|
|
||||||
if(response == Live_OK) {
|
if(response == Live_OK) {
|
||||||
authState = Auth_Success;
|
authState = Auth_Success;
|
||||||
pendingTries = 0;
|
pendingAuthTries = 0;
|
||||||
PrintToServer("[AdminPanel] Authenticated with server successfully.");
|
PrintToServer("[AdminPanel] Authenticated with server successfully.");
|
||||||
SendFullSync();
|
CreateTimer(1.0, Timer_FullSync);
|
||||||
} else if(response == Live_Error) {
|
} else if(response == Live_Error) {
|
||||||
authState = Auth_Fail;
|
authState = Auth_Fail;
|
||||||
g_socket.Disconnect();
|
g_socket.Disconnect();
|
||||||
|
@ -430,19 +459,25 @@ int FindPlayer(const char[] arg) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSocketConnect(Socket socket, int any) {
|
void SendAuthPayload() {
|
||||||
if(cvar_debug.BoolValue)
|
// Already sending one, ignore.
|
||||||
PrintToServer("[AdminPanel] Connected to %s:%d", serverIp, serverPort);
|
if(authState == Auth_PendingResponse) return;
|
||||||
g_socket.SetArg(0);
|
g_socket.SetArg(0);
|
||||||
|
authState = Auth_PendingResponse;
|
||||||
PrintToServer("[AdminPanel] Authenticating with server");
|
|
||||||
StartPayloadEx();
|
StartPayloadEx();
|
||||||
AddAuthRecord();
|
AddAuthRecord();
|
||||||
SendPayload();
|
SendPayload();
|
||||||
|
PrintToServer("[AdminPanel] Authenticating with server");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This does not trigger if the server is hibernating.
|
||||||
|
void OnSocketConnect(Socket socket, int any) {
|
||||||
|
Debug("Connected to %s:%d. Authenticating...", serverIp, serverPort);
|
||||||
|
SendAuthPayload();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSocketDisconnect(Socket socket, int attempt) {
|
void OnSocketDisconnect(Socket socket, int attempt) {
|
||||||
authState = Auth_Pending;
|
authState = Auth_Inactive;
|
||||||
g_socket.SetArg(attempt + 1);
|
g_socket.SetArg(attempt + 1);
|
||||||
float nextAttempt = Exponential(float(attempt) / 2.0) + 2.0;
|
float nextAttempt = Exponential(float(attempt) / 2.0) + 2.0;
|
||||||
if(nextAttempt > MAX_ATTEMPT_TIMEOUT) nextAttempt = MAX_ATTEMPT_TIMEOUT;
|
if(nextAttempt > MAX_ATTEMPT_TIMEOUT) nextAttempt = MAX_ATTEMPT_TIMEOUT;
|
||||||
|
@ -471,6 +506,7 @@ public void L4D_OnServerHibernationUpdate(bool hibernating) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hibernating) {
|
if(hibernating) {
|
||||||
|
authState = Auth_Inactive;
|
||||||
g_socket.Disconnect();
|
g_socket.Disconnect();
|
||||||
} else {
|
} else {
|
||||||
ConnectSocket();
|
ConnectSocket();
|
||||||
|
@ -504,15 +540,32 @@ Action Timer_Reconnect(Handle h, int type) {
|
||||||
return Plugin_Continue;
|
return Plugin_Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectSocket() {
|
bool ConnectSocket(bool force = false, int authTry = 0) {
|
||||||
if(g_socket == null) LogError("Socket is invalid");
|
if(g_socket == null) LogError("Socket is invalid");
|
||||||
if(g_socket.Connected)
|
if(g_socket.Connected) {
|
||||||
|
Debug("Already connected, disconnecting...");
|
||||||
g_socket.Disconnect();
|
g_socket.Disconnect();
|
||||||
if(authToken[0] == '\0') return;
|
authState = Auth_Inactive;
|
||||||
|
}
|
||||||
|
if(authToken[0] == '\0') LogError("ConnectSocket() called with no auth token");
|
||||||
// Do not try to reconnect on auth failure, until token has changed
|
// Do not try to reconnect on auth failure, until token has changed
|
||||||
if(authState == Auth_Fail) return;
|
if(!force && authState == Auth_Fail) return false;
|
||||||
authState = Auth_Pending;
|
authState = Auth_Pending;
|
||||||
g_socket.Connect(OnSocketConnect, OnSocketReceive, OnSocketDisconnect, serverIp, serverPort);
|
g_socket.Connect(OnSocketConnect, OnSocketReceive, OnSocketDisconnect, serverIp, serverPort);
|
||||||
|
CreateTimer(10.0, Timer_ConnectTimeout, authTry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action Timer_ConnectTimeout(Handle h, int attempt) {
|
||||||
|
if(g_socket.Connected && authState == Auth_Pending) {
|
||||||
|
if(attempt == 3) {
|
||||||
|
Debug("Timed out");
|
||||||
|
g_socket.Disconnect();
|
||||||
|
}
|
||||||
|
Debug("timed out waiting for connection callback, trying again (try=%d)", attempt);
|
||||||
|
ConnectSocket(false, attempt + 1);
|
||||||
|
}
|
||||||
|
return Plugin_Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DATE_FORMAT "%F at %I:%M %p"
|
#define DATE_FORMAT "%F at %I:%M %p"
|
||||||
|
@ -520,13 +573,25 @@ Action Command_PanelDebug(int client, int args) {
|
||||||
char arg[32];
|
char arg[32];
|
||||||
GetCmdArg(1, arg, sizeof(arg));
|
GetCmdArg(1, arg, sizeof(arg));
|
||||||
if(StrEqual(arg, "connect")) {
|
if(StrEqual(arg, "connect")) {
|
||||||
if(authToken[0] == '\0')
|
if(authToken[0] == '\0') {
|
||||||
ReplyToCommand(client, "No auth token.");
|
ReplyToCommand(client, "No auth token.");
|
||||||
else
|
} else {
|
||||||
ConnectSocket();
|
if(ConnectSocket(true)) {
|
||||||
|
ReplyToCommand(client, "Connecting...");
|
||||||
|
} else {
|
||||||
|
ReplyToCommand(client, "Cannot connect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(StrEqual(arg, "auth")) {
|
||||||
|
if(!g_socket.Connected) {
|
||||||
|
ReplyToCommand(client, "Not connected.");
|
||||||
|
} else {
|
||||||
|
SendAuthPayload();
|
||||||
|
ReplyToCommand(client, "Sent auth payload");
|
||||||
|
}
|
||||||
} else if(StrEqual(arg, "info")) {
|
} else if(StrEqual(arg, "info")) {
|
||||||
ReplyToCommand(client, "Connected: %b\tAuthenticated: %d\tState: %d", g_socket.Connected, authState, g_gameState);
|
ReplyToCommand(client, "Connected: %b\tAuth State: %s\tGameState: %d", g_socket.Connected, AUTH_STATE_LABEL[view_as<int>(authState)+1], g_gameState);
|
||||||
int timeFromLastPayload = GetTime() - g_lastPayloadSent;
|
int timeFromLastPayload = g_lastPayloadSent > 0 ? GetTime() - g_lastPayloadSent : -1;
|
||||||
ReplyToCommand(client, "Last Payload: %ds", timeFromLastPayload);
|
ReplyToCommand(client, "Last Payload: %ds", timeFromLastPayload);
|
||||||
ReplyToCommand(client, "#Viewers: %d\t#Players: %d", numberOfViewers, numberOfPlayers);
|
ReplyToCommand(client, "#Viewers: %d\t#Players: %d", numberOfViewers, numberOfPlayers);
|
||||||
ReplyToCommand(client, "Target Host: %s:%d", serverIp, serverPort);
|
ReplyToCommand(client, "Target Host: %s:%d", serverIp, serverPort);
|
||||||
|
@ -625,7 +690,7 @@ void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) {
|
||||||
}
|
}
|
||||||
public void Event_PlayerToBot(Handle event, char[] name, bool dontBroadcast) {
|
public void Event_PlayerToBot(Handle event, char[] name, bool dontBroadcast) {
|
||||||
int player = GetClientOfUserId(GetEventInt(event, "player"));
|
int player = GetClientOfUserId(GetEventInt(event, "player"));
|
||||||
int bot = GetClientOfUserId(GetEventInt(event, "bot"));
|
// int bot = GetClientOfUserId(GetEventInt(event, "bot"));
|
||||||
if(player > 0 && !IsFakeClient(player) && StartPayload()) {
|
if(player > 0 && !IsFakeClient(player) && StartPayload()) {
|
||||||
AddSurvivorRecord(player);
|
AddSurvivorRecord(player);
|
||||||
SendPayload();
|
SendPayload();
|
||||||
|
@ -1024,14 +1089,15 @@ char pendingRecords[64];
|
||||||
|
|
||||||
bool CanSendPayload(bool ignorePause = false) {
|
bool CanSendPayload(bool ignorePause = false) {
|
||||||
if(!g_socket.Connected) return false;
|
if(!g_socket.Connected) return false;
|
||||||
if(authState != Auth_Success) {
|
// if(authState != Auth_Success) {
|
||||||
if(authState == Auth_Pending && pendingTries > 0) {
|
// if(authState == Auth_Pending && pendingTries > 0) {
|
||||||
pendingTries--;
|
// pendingTries--;
|
||||||
PrintToServer("[AdminPanel] Auth state is pending. Too early?");
|
// PrintToServer("[AdminPanel] Auth state is pending. Too early?");
|
||||||
ConnectSocket();
|
// ConnectSocket();
|
||||||
}
|
// }
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
if(authState != Auth_Success) return false;
|
||||||
if(cvar_flags.IntValue & view_as<int>(Setting_DisableWithNoViewers) && !ignorePause && (numberOfViewers == 0 || numberOfPlayers == 0)) return false;
|
if(cvar_flags.IntValue & view_as<int>(Setting_DisableWithNoViewers) && !ignorePause && (numberOfViewers == 0 || numberOfPlayers == 0)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1202,6 +1268,7 @@ void AddAuthRecord() {
|
||||||
StartRecord(Live_Auth);
|
StartRecord(Live_Auth);
|
||||||
sendBuffer.WriteByte(LIVESTATUS_VERSION);
|
sendBuffer.WriteByte(LIVESTATUS_VERSION);
|
||||||
sendBuffer.WriteString(authToken);
|
sendBuffer.WriteString(authToken);
|
||||||
|
sendBuffer.WriteInt(cvar_hostPort.IntValue);
|
||||||
EndRecord();
|
EndRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,11 +1279,12 @@ void AddMetaRecord(bool state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendPayload() {
|
void SendPayload() {
|
||||||
if(sendBuffer.offset == 0) return;
|
if(sendBuffer.offset == 0) {
|
||||||
int len = sendBuffer.Finish();
|
Debug("sendBuffer empty, ignoring SendPayload()");
|
||||||
if(cvar_debug.BoolValue) {
|
return;
|
||||||
PrintToServer("[AdminPanel] Sending %d bytes of data (records = %s)", len, pendingRecords);
|
|
||||||
}
|
}
|
||||||
|
int len = sendBuffer.Finish();
|
||||||
|
Debug("Sending %d bytes of data (records = %s)", len, pendingRecords);
|
||||||
g_lastPayloadSent = GetTime();
|
g_lastPayloadSent = GetTime();
|
||||||
g_socket.Send(sendBuffer.buffer, len);
|
g_socket.Send(sendBuffer.buffer, len);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue