I am having some issues with server.js stops working/hung up after a while. No errors.
It warns about MaxListenersExceededWarning after a few messages from the game servers. It is still working though for a while. Until it eventually just stops. The script/process is still alive, but no in-game messages are being sent to Discord, or sent from Discord to the game servers.
Code:
/*
AMXX CGC (Cross-Game Chatter)
More info at: <a href="https://forums.alliedmods.net/showthread.php?t=319940" target="_blank" rel="noopener">https://forums.alliedmods.net/showthread.php?t=319940</a>
Source available at: <a href="https://github.com/x-Eagle-x/Cross-Game-Chatter" target="_blank" rel="nofollow noopener">https://github.com/x-Eagle-x/Cross-Game-Chatter</a>
<a href="https://www.youtube.com/watch?v=7AqTB30d-Mc" target="_blank" rel="nofollow noopener">https://www.youtube.com/watch?v=7AqTB30d-Mc</a>
*/
#define DEBUG
#if defined DEBUG
#pragma unused g_iTimeout
#endif
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <sockets>
#include <discord>
//#include <steamid>
#include <geoip>
#pragma semicolon 1
#define VERSION "1.61"
#define PORT 1337
#define TSK_CHAT_INDEX 3210
#define TSK_CHECK_SOCKET_SERVER 4200
#define MAX_MSG_LENGTH 128
#define MAX_DMSG_LENGTH 256
new Float:RETRY_DELAY = 5.0;
new g_cvarServerTag;
new g_szServerTag[MAX_NAME_LENGTH];
new g_szSystemName[MAX_NAME_LENGTH];
new g_cvarOutgoing_SystemUsername;
new g_iSocket, bool:g_bRunning, g_iTimeout;
new bool:bIsConnected[MAX_PLAYERS + 1], g_szCountry[MAX_PLAYERS + 1][10];
public plugin_init()
{
register_plugin( "Cross-Game Chatter", VERSION, "thEsp, Rirre" );
register_clcmd( "say", "cmd_Chat" );
register_clcmd( "say_team", "cmd_Chat" );
register_forward( FM_ClientUserInfoChanged, "ClientInfoChanged" );
}
public plugin_cfg()
{
new szServerName[MAX_NAME_LENGTH];
get_cvar_string( "hostname", szServerName, charsmax(szServerName) );
g_cvarServerTag = register_cvar( "discord_servertag", "!s1", FCVAR_PROTECTED );
g_cvarOutgoing_SystemUsername = register_cvar( "discord_servername", szServerName, FCVAR_PROTECTED );
DiscordRelay();
}
public DiscordRelay()
{
if( !Initialize() )
{
if( task_exists(TSK_CHECK_SOCKET_SERVER) )
remove_task(TSK_CHECK_SOCKET_SERVER);
#if !defined DEBUG
if( g_iTimeout >= 10 )
{
//set_fail_state("[ERROR] crossgamechatter: Max attempts (%i) to initialize the socket server reached", g_iTimeout);
server_print("[ERROR] crossgamechatter: Max attempts (%i) to initialize the socket server reached", g_iTimeout);
return false;
}
g_iTimeout++;
#endif
server_print("[ERROR] crossgamechatter: Failed to initialize the socket server. Retrying...");
return set_task( RETRY_DELAY, "DiscordRelay", TSK_CHECK_SOCKET_SERVER );
}
new szMsg[MAX_DMSG_LENGTH], szMapName[MAX_NAME_LENGTH];
get_pcvar_string(g_cvarOutgoing_SystemUsername, g_szSystemName, charsmax(g_szSystemName));
get_mapname(szMapName, charsmax(szMapName));
formatex(szMsg, charsmax(szMsg), "```%s --- Map: %s started```", g_szSystemName, szMapName );
trim(szMsg);
socket_send( g_iSocket, szMsg, charsmax(szMsg) );
arrayset( szMsg, 0, sizeof(szMsg) );
g_iTimeout = 0;
return (g_bRunning = true);
}
bool:Initialize()
{
new Error;
g_iSocket = socket_open("127.0.0.1", PORT, _, Error);
if( Error == SOCK_ERROR_OK )
{
tsk_Chat();
#if defined DEBUG
server_print("[INFO] Cross-Game Chatter: Successful connection to relay server!");
#endif
}
//server_print("-- DEBUG: Error code: %i", Error);
return !bool:Error;
}
public client_putinserver(id)
{
bIsConnected[id] = true;
if( !g_bRunning )
return;
if( !Running() )
{
Close();
Retry();
return;
}
new szMessage[MAX_DMSG_LENGTH], szName[MAX_NAME_LENGTH], szAuthID[MAX_AUTHID_LENGTH], szIP[20];
get_user_name(id, szName, charsmax(szName));
replace_all(szName, charsmax(szName), "`", ""); // avoid escaping ``
get_user_ip(id, szIP, charsmax(szIP), 1);
get_user_authid(id, szAuthID, charsmax(szAuthID));
new szCountry[3];
if( geoip_code2_ex( szIP, szCountry ) )
{
strtolower( szCountry );
format( g_szCountry[id], charsmax( g_szCountry[] ), ":flag_%s:", szCountry );
}
else
{
g_szCountry[id] = "";
}
get_pcvar_string(g_cvarOutgoing_SystemUsername, g_szSystemName, charsmax(g_szSystemName));
formatex( szMessage, charsmax(szMessage), "`%s: %s <%s><%s> has joined the game`", g_szSystemName, szName, szAuthID, szIP );
trim(szMessage);
socket_send( g_iSocket, szMessage, charsmax(szMessage) );
arrayset( szMessage, 0, sizeof(szMessage) );
}
public client_disconnect(id)
{
if( !g_bRunning || !bIsConnected[id] )
return;
if( !Running() )
{
Close();
Retry();
return;
}
new szMessage[MAX_DMSG_LENGTH], szName[MAX_NAME_LENGTH], szAuthID[MAX_AUTHID_LENGTH], szIP[20];
get_user_name(id, szName, charsmax(szName));
replace_all(szName, charsmax(szName), "`", ""); // avoid escaping ``
get_user_ip(id, szIP, charsmax(szIP), 1);
get_user_authid(id, szAuthID, charsmax(szAuthID));
get_pcvar_string(g_cvarOutgoing_SystemUsername, g_szSystemName, charsmax(g_szSystemName));
formatex( szMessage, charsmax(szMessage), "`%s: %s <%s><%s> has left the game`", g_szSystemName, szName, szAuthID, szIP );
trim(szMessage);
socket_send( g_iSocket, szMessage, charsmax(szMessage) );
arrayset( szMessage, 0, sizeof(szMessage) );
bIsConnected[id] = false;
}
public ClientInfoChanged( id )
{
if( !g_bRunning )
return FMRES_IGNORED;
if( !Running() )
{
Close();
Retry();
return FMRES_IGNORED;
}
static const name[] = "name";
static szOldName[MAX_NAME_LENGTH], szNewName[MAX_NAME_LENGTH];
pev(id, pev_netname, szOldName, charsmax(szOldName));
if( szOldName[0] )
{
get_user_info(id, name, szNewName, charsmax(szNewName));
if( !equal(szOldName, szNewName) )
{
replace_all(szOldName, charsmax(szOldName), "`", ""); // avoid escaping ``
replace_all(szNewName, charsmax(szNewName), "`", ""); // avoid escaping ``
new szAuthID[MAX_AUTHID_LENGTH], szMessage[MAX_DMSG_LENGTH];
get_user_authid(id, szAuthID, charsmax(szAuthID));
get_pcvar_string(g_cvarOutgoing_SystemUsername, g_szSystemName, charsmax(g_szSystemName));
formatex( szMessage, charsmax(szMessage), "`%s: %s <%s> changed name to %s`", g_szSystemName, szOldName, szAuthID, szNewName );
trim(szMessage);
socket_send( g_iSocket, szMessage, charsmax(szMessage) );
arrayset( szMessage, 0, sizeof(szMessage) );
}
}
return FMRES_IGNORED;
}
// Server to Discord
public cmd_Chat(id)
{
if( !bIsConnected[id] )
return PLUGIN_HANDLED;
if( !g_bRunning )
return PLUGIN_CONTINUE;
if( !Running() )
{
Close();
Retry();
return PLUGIN_CONTINUE;
}
new szMessage[MAX_DMSG_LENGTH], szMessageSafe[MAX_DMSG_LENGTH], szName[MAX_NAME_LENGTH], szAuthID[MAX_AUTHID_LENGTH];//, szCommunityID[MAX_AUTHID_LENGTH];
read_args(szMessage, charsmax(szMessage));
get_user_name(id, szName, charsmax(szName));
get_user_authid(id, szAuthID, charsmax(szAuthID));
remove_quotes(szMessage);
trim(szMessage);
if( !szMessage[0] )
return PLUGIN_CONTINUE;
if( containi(szMessage, "@everyone") != -1 )
return PLUGIN_HANDLED;
if( containi(szMessage, "discord.gg/") != -1 ) // Prevent invites
return PLUGIN_CONTINUE;
// (!) Hyper/inline links aren't working in Discord :(
//if( GetFriendID( szAuthID, szCommunityID, charsmax(szCommunityID)) )
{
get_pcvar_string(g_cvarOutgoing_SystemUsername, g_szSystemName, charsmax(g_szSystemName));
//formatex( szMessage, charsmax(szMessage), "%s: %s <[%s](http://steamcommunity.com/profiles/%s)>: %s", g_szSystemName, szName, szAuthID, szCommunityID, szMessage );
/*format( szMessage, charsmax(szMessage), "%s: %s `%s` <%s>: %s", g_szSystemName, g_szCountry[id], szName, szAuthID, szMessage );
discord_escape_string( szMessage, szMessageSafe, charsmax(szMessageSafe) );
socket_send( g_iSocket, szMessageSafe, charsmax(szMessageSafe) );*/
//discord_get_user_name( id, szName, charsmax(szName) ); // not needed when szName is embedded within ``
//replace_all( szAuthID, charsmax(szAuthID), "_", "\_" ); // not needed when szAuthID is embedded within ``
format( szMessageSafe, charsmax(szMessageSafe), "%s", szMessage );
discord_escape_string( szMessageSafe, szMessage, charsmax(szMessage) );
format( szMessage, charsmax(szMessage), "%s: %s `%s <%s>` %s", g_szSystemName, g_szCountry[id], szName, szAuthID, szMessageSafe );
trim(szMessage);
socket_send( g_iSocket, szMessage, charsmax(szMessage) );
//server_print( "%s", szMessage );
arrayset( szMessage, 0, sizeof(szMessage) );
}
/* else
server_print( "[WARNING] Cross-Game Chatter: <%s> is a invalid Steam ID! Cannot convert it to Steam Community ID.", szAuthID );*/
return PLUGIN_CONTINUE;
}
// Discord to servers
public tsk_Chat()
{
if( !Running() )
{
Close();
return;
}
// Note: this is executed ONCE during compilation. A macro function isn't really needed.
#if AMXX_VERSION_NUM < 190
if( socket_change(g_iSocket, 0) )
#else
if( socket_is_readable(g_iSocket, 0) )
#endif
{
static szData[MAX_DMSG_LENGTH];
socket_recv( g_iSocket, szData, charsmax(szData) );
if( !szData[0] || szData[0] == '^n' && !szData[1] )
return;
get_pcvar_string( g_cvarServerTag, g_szServerTag, charsmax(g_szServerTag) );
static iChars; iChars = strlen(g_szServerTag);
if( strlen(g_szServerTag) == 4 || strlen(g_szServerTag) >= iChars )
{
static szMsg[2][MAX_DMSG_LENGTH];
explode_string( szData, "): ", szMsg, sizeof(szMsg), sizeof(szMsg[]) );
if( equali(szMsg[1], "!all", 4) || equali(szMsg[1], g_szServerTag, iChars) )
{ // this server got mentioned, allow message to pass
static len; len = 0;
len = formatex( szMsg[0], charsmax(szMsg[]), szMsg[0] );
len += formatex( szMsg[0][len], charsmax(szMsg[]) - len, "): " ); // add delimiter back to the string
formatex( szMsg[1], charsmax(szMsg[]), szMsg[1][iChars + 1] ); // strip server tag
trim(szMsg[1]);
static szResponse[MAX_DMSG_LENGTH], iMsgLen;
if( equal(szMsg[1][0], "@", 1) )
{
formatex( szMsg[1], charsmax(szMsg[]), szMsg[1][1] ); // strip '@'
iMsgLen = strlen(szMsg[0]) + strlen(szMsg[1]);
if( !CheckMsgLength(iMsgLen) )
{
formatex(szResponse, charsmax(szResponse), "%sYour message is too long (%i characters)! (maximum 128 characters)", szMsg[0], iMsgLen );
socket_send( g_iSocket, szResponse, charsmax(szResponse) );
set_task( 0.1, "tsk_Chat", TSK_CHAT_INDEX );
return;
}
new players[MAX_PLAYERS], iNum, player;
get_players(players, iNum, "ch");
for( new i = 0; i < iNum; ++i )
{
player = players[i];
if( get_user_flags(player) & ADMIN_CHAT )
client_print( player, print_chat, "(ADMINS) %s%s", szMsg[0], szMsg[1] );
}
server_print( "(ADMINS) %s%s", szMsg[0], szMsg[1] );
}
else
{
iMsgLen = strlen(szMsg[0]) + strlen(szMsg[1]);
if( !CheckMsgLength(iMsgLen) )
{
formatex(szResponse, charsmax(szResponse), "%sYour message is too long (%i characters)! (maximum 128 characters)", szMsg[0], iMsgLen );
socket_send( g_iSocket, szResponse, charsmax(szResponse) );
set_task( 0.1, "tsk_Chat", TSK_CHAT_INDEX );
return;
}
client_print( 0, print_chat, "%s%s", szMsg[0], szMsg[1] );
server_print( "%s%s", szMsg[0], szMsg[1] );
}
//server_print( "-- DEBUG: %s%s", szMsg[0], szMsg[1] );
formatex(szResponse, charsmax(szResponse), "%sMessage sent successfully!", szMsg[0] );
socket_send( g_iSocket, szResponse, charsmax(szResponse) );
}
}
}
set_task( 0.1, "tsk_Chat", TSK_CHAT_INDEX );
}
bool:CheckMsgLength( iMsgLen )
{
return (iMsgLen > 128) ? false : true;
}
bool:Running()
{
return (socket_send(g_iSocket, {0}, 1) != -1);
}
Close()
{
socket_close(g_iSocket);
g_bRunning = false;
if( task_exists(TSK_CHAT_INDEX) )
remove_task(TSK_CHAT_INDEX);
if( task_exists(TSK_CHECK_SOCKET_SERVER) )
remove_task(TSK_CHECK_SOCKET_SERVER);
#if defined DEBUG
server_print("[INFO] Cross-Game Chatter: Relay server has shut down");
#endif
}
Retry()
{
if( !task_exists(TSK_CHECK_SOCKET_SERVER) )
set_task( RETRY_DELAY, "DiscordRelay", TSK_CHECK_SOCKET_SERVER, _, _, "b");
}
public plugin_end()
{
if( !g_bRunning )
Close();
// If the relay (server) is closed, this socket will also be closed.
// This way you don't have to start the nodejs script each time server restarts (or map changes).
}