Advertisement
Guest User

Untitled

a guest
May 10th, 2014
257
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.80 KB | None | 0 0
  1. /*
  2. **
  3. ** Server Hop (c) 2009, 2010 [GRAVE] rig0r
  4. **       www.gravedigger-company.nl
  5. **
  6. */
  7.  
  8. #include <sourcemod>
  9. #include <socket>
  10. #include <morecolors>
  11. #include <hexcode>
  12.  
  13. #define PLUGIN_VERSION "0.8.1"
  14. #define MAX_SERVERS 10
  15. #define REFRESH_TIME 60.0
  16. #define SERVER_TIMEOUT 10.0
  17. #define MAX_STR_LEN 160
  18. #define MAX_INFO_LEN 200
  19. //#define DEBUG
  20.  
  21. new serverCount = 0;
  22. new advertCount = 0;
  23. new advertInterval = 1;
  24. new String:serverName[MAX_SERVERS][MAX_STR_LEN];
  25. new String:serverAddress[MAX_SERVERS][MAX_STR_LEN];
  26. new serverPort[MAX_SERVERS];
  27. new String:serverInfo[MAX_SERVERS][MAX_INFO_LEN];
  28. new Handle:socket[MAX_SERVERS];
  29. new bool:socketError[MAX_SERVERS];
  30.  
  31. new Handle:cv_hoptrigger = INVALID_HANDLE
  32. new Handle:cv_serverformat = INVALID_HANDLE
  33. new Handle:cv_broadcasthops = INVALID_HANDLE
  34. new Handle:cv_advert = INVALID_HANDLE
  35. new Handle:cv_advert_interval = INVALID_HANDLE
  36.  
  37. public Plugin:myinfo =
  38. {
  39.   name = "Server Hop",
  40.   author = "[GRAVE] rig0r",
  41.   description = "Provides live server info with join option",
  42.   version = PLUGIN_VERSION,
  43.   url = "http://www.gravedigger-company.nl"
  44. };
  45.  
  46. public OnPluginStart()
  47. {
  48.   LoadTranslations( "serverhop.phrases" );
  49.  
  50.   // convar setup
  51.   RegConsoleCmd("sm_join", ServerMenu(client), "Join the server");
  52.   cv_hoptrigger = CreateConVar( "sm_hop_trigger",
  53.                                 "!servers",
  54.                                 "What players have to type in chat to activate the plugin (besides !hop)" );
  55.   cv_serverformat = CreateConVar( "sm_hop_serverformat",
  56.                                   "%map (%numplayers/%maxplayers) - %name",
  57.                                   "Defines how the server info should be presented" );
  58.   cv_broadcasthops = CreateConVar( "sm_hop_broadcasthops",
  59.                                    "1",
  60.                                    "Set to 1 if you want a broadcast message when a player hops to another server" );
  61.   cv_advert = CreateConVar( "sm_hop_advertise",
  62.                             "1",
  63.                             "Set to 1 to enable server advertisements" );
  64.   cv_advert_interval = CreateConVar( "sm_hop_advertisement_interval",
  65.                                      "1",
  66.                                      "Advertisement interval: advertise a server every x minute(s)" );
  67.  
  68.   AutoExecConfig( true, "plugin.serverhop" );
  69.  
  70.   new Handle:timer = CreateTimer( REFRESH_TIME, RefreshServerInfo, _, TIMER_REPEAT );
  71.  
  72.   RegConsoleCmd( "say", Command_Say );
  73.   RegConsoleCmd( "say_team", Command_Say );
  74.  
  75.   new String:path[MAX_STR_LEN];
  76.   new Handle:kv;
  77.  
  78.   BuildPath( Path_SM, path, sizeof( path ), "configs/serverhop.cfg" );
  79.   kv = CreateKeyValues( "Servers" );
  80.  
  81.   if ( !FileToKeyValues( kv, path ) )
  82.     LogToGame( "Error loading server list" );
  83.  
  84.   new i;
  85.   KvRewind( kv );
  86.   KvGotoFirstSubKey( kv );
  87.   do {
  88.     KvGetSectionName( kv, serverName[i], MAX_STR_LEN );
  89.     KvGetString( kv, "address", serverAddress[i], MAX_STR_LEN );
  90.     serverPort[i] = KvGetNum( kv, "port", 27015 );
  91.     i++;
  92.   } while ( KvGotoNextKey( kv ) );
  93.   serverCount = i;
  94.  
  95.   TriggerTimer( timer );
  96. }
  97.  
  98. public Action:Command_Say( client, args )
  99. {
  100.   new String:text[MAX_STR_LEN];
  101.   new startidx = 0;
  102.  
  103.   if ( !GetCmdArgString( text, sizeof( text ) ) ) {
  104.     return Plugin_Continue;
  105.   }
  106.  
  107.   if ( text[strlen( text) - 1] == '"' ) {
  108.     text[strlen( text )-1] = '\0';
  109.     startidx = 1;
  110.   }
  111.  
  112.   new String:trigger[MAX_STR_LEN];
  113.   GetConVarString( cv_hoptrigger, trigger, sizeof( trigger ) );
  114.  
  115.   if ( strcmp( text[startidx], trigger, false ) == 0 || strcmp( text[startidx], "!hop", false ) == 0 ) {
  116.     ServerMenu( client );
  117.   }
  118.  
  119.   return Plugin_Continue;
  120. }
  121.  
  122. public Action:ServerMenu( client )
  123. {
  124.   new Handle:menu = CreateMenu( MenuHandler );
  125.   new String:serverNumStr[MAX_STR_LEN];
  126.   new String:menuTitle[MAX_STR_LEN];
  127.   Format( menuTitle, sizeof( menuTitle ), "%T", "SelectServer", client );
  128.   SetMenuTitle( menu, menuTitle );
  129.  
  130.   for ( new i = 0; i < serverCount; i++ ) {
  131.     if ( strlen( serverInfo[i] ) > 0 ) {
  132.       #if defined DEBUG then
  133.         PrintToConsole( client, serverInfo[i] );
  134.       #endif
  135.       IntToString( i, serverNumStr, sizeof( serverNumStr ) );
  136.       AddMenuItem( menu, serverNumStr, serverInfo[i] );
  137.     }
  138.   }
  139.   DisplayMenu( menu, client, 20 );
  140. }
  141.  
  142. public MenuHandler( Handle:menu, MenuAction:action, param1, param2 )
  143. {
  144.   if ( action == MenuAction_Select ) {
  145.     new String:infobuf[MAX_STR_LEN];
  146.     new String:address[MAX_STR_LEN];
  147.  
  148.     GetMenuItem( menu, param2, infobuf, sizeof( infobuf ) );
  149.     new serverNum = StringToInt( infobuf );
  150.  
  151.     // header
  152.     new Handle:kvheader = CreateKeyValues( "header" );
  153.     new String:menuTitle[MAX_STR_LEN];
  154.     Format( menuTitle, sizeof( menuTitle ), "%T", "AboutToJoinServer", param1 );
  155.     KvSetString( kvheader, "title", menuTitle );
  156.     KvSetNum( kvheader, "level", 1 );
  157.     KvSetString( kvheader, "time", "10" );
  158.     CreateDialog( param1, kvheader, DialogType_Msg );
  159.     CloseHandle( kvheader );
  160.    
  161.     // join confirmation dialog
  162.     new Handle:kv = CreateKeyValues( "menu" );
  163.     KvSetString( kv, "time", "10" );
  164.     Format( address, MAX_STR_LEN, "%s:%i", serverAddress[serverNum], serverPort[serverNum] );
  165.     KvSetString( kv, "title", address );
  166.     CreateDialog( param1, kv, DialogType_AskConnect );
  167.     CloseHandle( kv );
  168.  
  169.     // broadcast to all
  170.     if ( GetConVarBool( cv_broadcasthops ) ) {
  171.       new String:clientName[MAX_NAME_LENGTH];
  172.       GetClientName( param1, clientName, sizeof( clientName ) );
  173.     //PrintColorText( "\x078A8A8A %t", "HopNotification", clientName, serverInfo[serverNum] );
  174.       CPrintToChatAll( "\x078A8A8A%t", "HopNotification", clientName, serverInfo[serverNum] );
  175.     }
  176.   }
  177. }
  178.  
  179. public Action:RefreshServerInfo( Handle:timer )
  180. {
  181.   for ( new i = 0; i < serverCount; i++ ) {
  182.     serverInfo[i] = "";
  183.     socketError[i] = false;
  184.     socket[i] = SocketCreate( SOCKET_UDP, OnSocketError );
  185.     SocketSetArg( socket[i], i );
  186.     SocketConnect( socket[i], OnSocketConnected, OnSocketReceive, OnSocketDisconnected, serverAddress[i], serverPort[i] );
  187.   }
  188.  
  189.   CreateTimer( SERVER_TIMEOUT, CleanUp );
  190. }
  191.  
  192. public Action:CleanUp( Handle:timer )
  193. {
  194.   for ( new i = 0; i < serverCount; i++ ) {
  195.     if ( strlen( serverInfo[i] ) == 0 && !socketError[i] ) {
  196.       LogError( "Server %s:%i is down: no timely reply received", serverAddress[i], serverPort[i] );
  197.       CloseHandle( socket[i] );
  198.     }
  199.   }
  200.  
  201.   // all server info is up to date: advertise
  202.   if ( GetConVarBool( cv_advert ) ) {
  203.     if ( advertInterval == GetConVarFloat( cv_advert_interval ) ) {
  204.       Advertise();
  205.     }
  206.     advertInterval++;
  207.     if ( advertInterval > GetConVarFloat( cv_advert_interval ) ) {
  208.       advertInterval = 1;
  209.     }
  210.   }
  211. }
  212.  
  213. public Action:Advertise()
  214. {
  215.   new String:trigger[MAX_STR_LEN];
  216.   GetConVarString( cv_hoptrigger, trigger, sizeof( trigger ) );
  217.  
  218.   // skip servers being marked as down
  219.   while ( strlen( serverInfo[advertCount] ) == 0 ) {
  220.     #if defined DEBUG then
  221.       LogError( "Not advertising down server %i", advertCount );
  222.     #endif
  223.     advertCount++;
  224.     if ( advertCount >= serverCount ) {
  225.       advertCount = 0;
  226.       break;
  227.     }
  228.   }
  229.  
  230.   if ( strlen( serverInfo[advertCount] ) > 0 ) {
  231.     CPrintToChatAll( "\x078A8A8A%t", "Advert", serverInfo[advertCount], trigger );
  232.     #if defined DEBUG then
  233.       LogError( "Advertising server %i (%s)", advertCount, serverInfo[advertCount] );
  234.     #endif
  235.  
  236.     advertCount++;
  237.     if ( advertCount >= serverCount ) {
  238.       advertCount = 0;
  239.     }
  240.   }
  241. }
  242.  
  243. public OnSocketConnected( Handle:sock, any:i )
  244. {
  245.   decl String:requestStr[ 25 ];
  246.   Format( requestStr, sizeof( requestStr ), "%s", "\xFF\xFF\xFF\xFF\x54Source Engine Query" );
  247.   SocketSend( sock, requestStr, 25 );
  248. }
  249.  
  250. GetByte( String:receiveData[], offset )
  251. {
  252.   return receiveData[offset];
  253. }
  254.  
  255. String:GetString( String:receiveData[], dataSize, offset )
  256. {
  257.   decl String:serverStr[MAX_STR_LEN] = "";
  258.   new j = 0;
  259.   for ( new i = offset; i < dataSize; i++ ) {
  260.     serverStr[j] = receiveData[i];
  261.     j++;
  262.     if ( receiveData[i] == '\x0' ) {
  263.       break;
  264.     }
  265.   }
  266.   return serverStr;
  267. }
  268.  
  269. public OnSocketReceive( Handle:sock, String:receiveData[], const dataSize, any:i )
  270. {
  271.   new String:srvName[MAX_STR_LEN];
  272.   new String:mapName[MAX_STR_LEN];
  273.   new String:gameDir[MAX_STR_LEN];
  274.   new String:gameDesc[MAX_STR_LEN];
  275.   new String:numPlayers[MAX_STR_LEN];
  276.   new String:maxPlayers[MAX_STR_LEN];
  277.  
  278.   // parse server info
  279.   new offset = 2;
  280.   srvName = GetString( receiveData, dataSize, offset );
  281.   offset += strlen( srvName ) + 1;
  282.   mapName = GetString( receiveData, dataSize, offset );
  283.   offset += strlen( mapName ) + 1;
  284.   gameDir = GetString( receiveData, dataSize, offset );
  285.   offset += strlen( gameDir ) + 1;
  286.   gameDesc = GetString( receiveData, dataSize, offset );
  287.   offset += strlen( gameDesc ) + 1;
  288.   offset += 2;
  289.   IntToString( GetByte( receiveData, offset ), numPlayers, sizeof( numPlayers ) );
  290.   offset++;
  291.   IntToString( GetByte( receiveData, offset ), maxPlayers, sizeof( maxPlayers ) );
  292.  
  293.   new String:format[MAX_STR_LEN];
  294.   GetConVarString( cv_serverformat, format, sizeof( format ) );
  295.   ReplaceString( format, strlen( format ), "%name", serverName[i], false );
  296.   ReplaceString( format, strlen( format ), "%map", mapName, false );
  297.   ReplaceString( format, strlen( format ), "%numplayers", numPlayers, false );
  298.   ReplaceString( format, strlen( format ), "%maxplayers", maxPlayers, false );
  299.  
  300.   serverInfo[i] = format;
  301.  
  302.   #if defined DEBUG then
  303.     LogError( serverInfo[i] );
  304.   #endif
  305.  
  306.   CloseHandle( sock );
  307. }
  308.  
  309. public OnSocketDisconnected( Handle:sock, any:i )
  310. {
  311.   CloseHandle( sock );
  312. }
  313.  
  314. public OnSocketError( Handle:sock, const errorType, const errorNum, any:i )
  315. {
  316.   LogError( "Server %s:%i is down: socket error %d (errno %d)", serverAddress[i], serverPort[i], errorType, errorNum );
  317.   socketError[i] = true;
  318.   CloseHandle( sock );
  319. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement