Правила форума Гаранты форума
Размещение рекламы AMX-X компилятор

Здравствуйте, гость Вход | Регистрация

Наши новости:

14-дек
24-апр
10-апр
11-апр

> Правила форума

Этот раздел, как вы могли заметить по названию, предназначен для решения вопросов по поводу уже существующих модов и плагинов.
Пожалуйста, если у вас проблема с написанием плагина, не путайте этот раздел с разделом по скриптингу.
Для поиска плагинов и модов существует соответствующий раздел.

Название темы должно соответствовать содержанию. Темы с названием типа "Помогите", "Вопрос", "парни подскажите..." - будут удалены.
Все темы, не относящиеся к "Вопросам по модам и плагинам", будут удалены или перемещены в соответствующий раздел.

Правила оформления темы:
1. Помимо заголовка не забудьте верно сформулировать свой вопрос.
2. Выложите исходник (в тег кода + ) или ссылку на плагин который вызывает у вас вопросы.
3. Выложите лог с ошибками (если имеется) под спойлер

Вопрос по плагину xredirect

, Плагин xredirect Возможность перехода между серверами.
urpok
сообщение 12.4.2013, 9:38
Сообщение #1
Стаж: 13 лет

Сообщений: 890
Благодарностей: 164
Полезность: 0

Всем доброго дня.
Ребята, помогите разобраться с данным плагином.
Установил, всё как надо, он работает, это однозначно. Так как проверил.
Сам прикол заключаеться в том, что работает как-то странно, вот на данный момент, я на работе, на ноуте стоит клиент КС, захожу на сервер, пишу в чат /server, появляется меню со списком серверов, нажимаю чтобы перейти на другой, в чат пишет сообщение, о том, что я вроде как перекинут на другой сервер, но остаюсь на этом же.
Прикол номер два. Я дома, захожу в КС, захожу на сервер, прописываю тоже, нажимаю перейти, и меня сразу перекидывает на тот сервер, на который я нажал.
Удивился, попросил знакомого проверить, он пишет в чат, появляется менюшка, он нажимает, и его тоже перекидывает.
Прикол номер три. Прошу тоже самое проделать с другим знакомым. Он всё это делает, но он остаёться на сервере и его ни куда не перекидывает. Но опять же в чат пишет, с понтом дела вы перекинуты на тот или иной сервер.

Все варианты были опробованны на NonSteam клиентах.

В общем, получается так, что одни могут переходить, а другие нет. Терзают мутные сомнения, и я так предпологаю, что всё это может зависить от самого клиента или может я ошибаюсь ? Но тогда в чём может быть причина ?

Все сервера, на личном хостинге. Перенаправлений на левые, нету. В настройках всё вроде как нормально.

Вот могу показать впринципе serverlist.ini

serverlist.ini
[Название сервера_1]
address=ip - сервера
port=27015
cmdbackup=5
noauto=1
nomanual=0
nodisplay=0

[Название сервера_2]
address=ip - сервера
port=27016
cmdbackup=5
noauto=1
nomanual=0
nodisplay=0

[Название сервера_3]
address=ip - сервера
port=27017
cmdbackup=5
noauto=1
nomanual=0
nodisplay=0

[Название сервера_4]
address=ip - сервера
port=27018
cmdbackup=5
noauto=1
nomanual=0
nodisplay=0

[Название сервера_5]
address=ip - сервера
port=27019
cmdbackup=5
noauto=1
nomanual=0
nodisplay=0


Точно такой же serverlist.ini стоит на всех серверах. Всё работает, об этом написано выше.

Что это может быть за проблема, что у одних получается перейти, а у других нет !?

Помогите разобраться пожалуйста... Зарание спасибо.

Отредактировал: HeBuguMka, - 12.4.2013, 9:50
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   Цитировать сообщение
Статус пользователя Legenda
сообщение 12.4.2013, 11:00
Сообщение #2


Иконка группы

Стаж: 16 лет

Сообщений: 3619
Благодарностей: 1706
Полезность: 1010

Меценат Меценат

1. и какую же команду посылает сервер? для коннекте на другой сервер
2. конечно зависит от клиента, некоторые клиента вполне могут блокировать команда connect

лично не проверял, но думаю например на cracked steam это вполне может быть
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
urpok
сообщение 12.4.2013, 11:07
Сообщение #3
Стаж: 13 лет

Сообщений: 890
Благодарностей: 164
Полезность: 0

Цитата
1. и какую же команду посылает сервер? для коннекте на другой сервер
2. конечно зависит от клиента, некоторые клиента вполне могут блокировать команда connect

лично не проверял, но думаю например на cracked steam это вполне может быть


Всмысле какую команду. Плагин же редиректит на сервер, который ты указал в serverlist.ini
Команда же не connect, а в чат пишешь /server, тебе открывается список серверов, с названием карты и кол-во игроков, нажимаешь по нумерации и тебя перекидывает на этот сервер.

Отредактировал: HeBuguMka, - 12.4.2013, 11:08
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
Статус пользователя Legenda
сообщение 12.4.2013, 11:13
Сообщение #4


Иконка группы

Стаж: 16 лет

Сообщений: 3619
Благодарностей: 1706
Полезность: 1010

Меценат Меценат

Цитата(HeBuguMka @ 12.4.2013, 13:07) *
Всмысле какую команду. Плагин же редиректит на сервер, который ты указал в serverlist.ini
Команда же не connect, а в чат пишешь /server, тебе открывается список серверов, с названием карты и кол-во игроков, нажимаешь по нумерации и тебя перекидывает на этот сервер.

вообщем забудь о команде для редиректа
объяснять долго и бессмыслено раз уж с первого поста не понял

вопрос был о том, какая команда выполняет на клиенте для редиректа игрока
способов много есть, некоторые блокируются, а некоторые нет
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
urpok
сообщение 12.4.2013, 11:21
Сообщение #5
Стаж: 13 лет

Сообщений: 890
Благодарностей: 164
Полезность: 0

Вот .sma

xredirect
/ serverlist.ini \
[my example server]
address=example.n-ice.org
localaddress=192.168.0.3
port=27015
cmdbackup=5
noauto=1
nomanual=0
nodisplay=0

[my 2nd example server]
address=example2.n-ice.org
port=27015
\______________________________________/


// includes
#include <amxmodx>
#include <amxmisc>
#include <sockets>

// plugin defines
#define PLUGIN_NAME "xREDIRECT"
#define PLUGIN_VERSION "1.0"
#define PLUGIN_AUTHOR "x0R"
#define PLUGIN_TAG "[xREDIRECT]"

// maximum values - don't change this if you don't know what you are doing!
#define MAX_FILE_LEN 256 // maximum length of file names
#define MAX_SERVERLINE_LEN 256 // maximum length of a line read from SERVERFILE
#define MAX_SERVERNAME_LEN 50 // maximum length of a server name read from SERVERFILE
#define MAX_SERVERADDRESS_LEN 100 // maximum length of a server address read from SERVERFILE
#define MAX_NAME_LEN 33 // maximum length of a player name
#define MAX_MENUBODY_LEN 512 // maximum length of a menu body
#define MAX_WELCOME_LEN 1024 // maximum length of the welcome message
#define MAX_INFO_LEN 1500 // maximum length of info reply - when longer than that the packet is fragmented (due to MTU setting)
#define MAX_MAP_LEN 30 // maximum length of map names
#define MAX_IP_LEN 16 // maximum length of IP addresses
#define MAX_PORT_LEN 6 // maximum length of port numbers (as strings of course)
#define MAX_KEY_LEN 20 // maximum length of a key name in SERVERFILE
#define MAX_PASSWORD_LEN 15 // maximum length of a password in SERVERFILE
#define MAX_VALUE_LEN 255 // maximum length of a value in SERVERFILE
#define MAX_PLAYERS 32 // maximum number of players on the server

// unique task ID's - currently not needed but who knows when they will be
#define TASKID_QUERY 21934807
#define TASKID_QUERY_RECEIVE 21934808
#define TASKID_ANNOUNCE 21934809

// options - these can be changed by the user, rememeber that you need to recompile for any changes here to take effect
#define SERVERFILE "serverlist.ini" // name of file in /configs containing the server forwards - you can also prepend a subdirectory
#define QUERY_INTERVAL 20.0 // interval of server querying (in seconds)
#define QUERY_TIMEOUT 1.0 // the maximum time to wait for a server answer (in seconds) before it is considered being down
#define MAX_SERVERFORWARDS 6 // maximum number of server forwards in forwards file
#define MAX_MENUPAGES 10 // maximum number of pages the server selection menu can have
#define DEFAULT_CMDBACKUP 2 // how often to resend the UDP request to servers by default
#define MENU_FORCENOCOLOR false // false = display colored menues if the mod supports it; true = never display colored menues
#define MIN_ADMIN_LEVEL ADMIN_RESERVATION // the minimum level a player must have to be treated as admin (= won't be automatically redirected, can use reserved slots, can join passworded servers with publicpassword=0...)
// can be one of these listed here: http://www.amxmodx.org/funcwiki.php?go=mod...d=1#const_admin

// positions to get information from in server answer
#define STRUCT_INFO_POS_MAP 2
#define STRUCT_INFO_POS_ACTIVEPLAYERS 5
#define STRUCT_INFO_POS_MAXPLAYERS 6
#define STRUCT_INFO_POS_PROTOCOL 7
#define STRUCT_INFO_POS_MODVERSION 8
#define STRUCT_INFO_POS_ISMOD 9

// flags
#define SERVERFLAG_NOAUTO 0
#define SERVERFLAG_NOMANUAL 1
#define SERVERFLAG_NODISPLAY 2

// --------------------------------------- end of defines ---------------------------------------


// -=[ global variables - remember to add an initialization in srvcmd_reload() for all variables you add here! ]=-
/// <summary>Defines whether the plugin was completely initialized.</summary>
new g_bInitialized = false // no srvcmd_reload() initialization needed for this one, as it's not directly related to the server list
/// <summary>Server name.</summary>
new g_saServerNames[MAX_SERVERFORWARDS][MAX_SERVERNAME_LEN]
/// <summary>Server address.</summary>
new g_saServerAddresses[MAX_SERVERFORWARDS][MAX_SERVERADDRESS_LEN]
/// <summary>Server port.</summary>
new g_naServerPorts[MAX_SERVERFORWARDS] = {27015, ...}
/// <summary>Server password.</summary>
new g_saServerPasswords[MAX_SERVERFORWARDS][MAX_PASSWORD_LEN]
/// <summary>Is the server password public?</summary>
new g_naServerPublicPassword[MAX_SERVERFORWARDS] = {0, ...}
/// <summary>Currently active player count.</summary>
new g_naServerActivePlayers[MAX_SERVERFORWARDS] = {-1, ...}
/// <summary>Maximum number of players the server accepts. Does not take reserved slots into account.</summary>
new g_naServerMaxPlayers[MAX_SERVERFORWARDS] = {-1, ...}
/// <summary>Currently running map on server.</summary>
new g_saServerMap[MAX_SERVERFORWARDS][MAX_MAP_LEN]
/// <summary>The socket for the server to handle requests.</summary>
new g_naServerSockets[MAX_SERVERFORWARDS] = {0, ...}
/// <summary>The number how often server queries should be resent to that server.</summary>
new g_naServerCmdBackup[MAX_SERVERFORWARDS] = {DEFAULT_CMDBACKUP, ...}
/// <summary>Flags with several server options. Use the constant defines starting with SERVERFLAG_ to access these.</summary>
new g_naServerFlags[MAX_SERVERFORWARDS] = {0, ...}
/// <summary>Are admin slots reserved on this server?</summary>
new g_naServerReserveSlots[MAX_SERVERFORWARDS] = {0, ...}
/// <summary>Local server address.</summary>
new g_saServerLocalAddresses[MAX_SERVERFORWARDS][MAX_SERVERADDRESS_LEN]
/// <summary>At which real index does the menu page start? It is shifted because of hidden servers.</summary>
new g_naMenuPageStart[MAX_PLAYERS][MAX_MENUPAGES]
/// <summary>Is the server responding?</summary>
new bool:g_baServerResponding[MAX_SERVERFORWARDS] = {false, ...}
/// <summary>Number of servers found in server list file.</summary>
new g_nServerCount = 0
/// <summary>The last server someone has been redirected to. Needed for <seealso name="cmd_follow_player"/>.</summary>
new g_nLastRedirectServer = -1
/// <summary>The nick of the person who has been redirected at last. Needed for <seealso name="cmd_follow_player"/>.</summary>
new g_sLastRedirectName[MAX_NAME_LEN] = ""
/// <summary>The index of the current server. This is neccessary for the server to check its own data.</summary>
new g_nOwnServer = -1
/// <summary>The page number for each user which he had open last time, needed for switching back from sub menu to server menu.</summary>
new g_naLastMenuPages[MAX_PLAYERS] = {1, ...}
/// <summary>Hidden servers cause a difference between shown and real server numbers - this array associates the real server index with a given key - different for each user as some users can see servers that others don't.</summary>
new g_naServerSelections[MAX_PLAYERS][8]
/// <summary>This is the cycle variable that holds which server to begin from in <seealso name="announce_servers"/>.</summary>
new g_nNextAnnounceServer = 0
/// <summary>The last server the player came from through redirection. Needed in case he wants to send himself back with /retry.</summary>
new g_nLastServer[MAX_PLAYERS] = {-1, ...}
/// <summary>The last server the player has accessed the sub menu of. Needed when the player refreshes the sub menu.</summary>
new g_nLastSelected[MAX_PLAYERS] = {-1, ...}
/// <summary>This array contains the retry queue consisting of a player ID and a serve number for each record.</summary>
new g_nRetryQueue[MAX_PLAYERS*MAX_SERVERFORWARDS][2]
/// <summary>Counter for global number of queue entries.</summary>
new g_nRetryCount = 0
/// <summary>Controls whether certain debug messages are shown. It is autoamtically set to true when the plugin has debug mode set in plugins.ini.</summary>
new bool:g_bDebug = false


// -=[ global CVAR's ]=-
new cvar_active
new cvar_auto
new cvar_manual
new cvar_follow
new cvar_external_address
new cvar_check_method
new cvar_announce
new cvar_announce_mode
new cvar_announce_alivepos_x
new cvar_announce_alivepos_y
new cvar_announce_deadpos_x
new cvar_announce_deadpos_y
new cvar_show
new cvar_adminslots
new cvar_maxadmins
new cvar_retry
new cvar_hidedown
new cvar_localslots

// --------------------------------------- end of global vars ---------------------------------------

/*
public plugin_precache()
{
}
*/

#if AMXX_VERSION_NUM >= 170

/// <summary>Initialize CVARs, load servers, register commands, register menues, register dictionaries, start tasks...</summary>
public plugin_init() {
register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR)

register_cvar("redirect_version", PLUGIN_VERSION, FCVAR_SERVER|FCVAR_SPONLY)
set_cvar_string("redirect_version", PLUGIN_VERSION)

// please see the description at top if you want to know what these CVAR's do
cvar_active = register_cvar("redirect_active", "1")
cvar_auto = register_cvar("redirect_auto", "0")
cvar_manual = register_cvar("redirect_manual", "0")
cvar_follow = register_cvar("redirect_follow", "0")
cvar_external_address = register_cvar("redirect_external_address", "")
cvar_check_method = register_cvar("redirect_check_method", "0")
cvar_announce = register_cvar("redirect_announce", "120")
cvar_announce_mode = register_cvar("redirect_announce_mode", "3")
cvar_announce_alivepos_x = register_cvar("redirect_announce_alivepos_x", "-1.0")
cvar_announce_alivepos_y = register_cvar("redirect_announce_alivepos_y", "0.01")
cvar_announce_deadpos_x = register_cvar("redirect_announce_deadpos_x", "-1.0")
cvar_announce_deadpos_y = register_cvar("redirect_announce_deadpos_y", "0.35")
cvar_show = register_cvar("redirect_show", "1")
cvar_adminslots = register_cvar("redirect_adminslots", "0")
cvar_maxadmins = register_cvar("redirect_maxadmins", "0")
cvar_retry = register_cvar("redirect_retry", "0")
cvar_hidedown = register_cvar("redirect_hidedown", "0")
cvar_localslots = register_cvar("redirect_localslots", "0")

register_dictionary("xredirect.txt")
register_dictionary("common.txt")

load_servers()

register_menu("Redirect Menu", 1023, "server_menu_select")
register_menu("Detail Menu", 1023, "sub_menu_select")

register_srvcmd("redirect_reload", "srvcmd_reload", -1, "- reload redirect servers")

register_clcmd("say /server", "cmd_show_server_menu", 0, "- show server redirection menu")
register_clcmd("say_team /server", "cmd_show_server_menu", 0, "- show server redirection menu")
register_clcmd("pickserver", "cmd_pickserver", 0, "show server redirection menu")
register_clcmd("say /follow", "cmd_follow_player", 0, "- follow the last redirected player to his server")
register_clcmd("say_team /follow", "cmd_follow_player", 0, "- follow the last redirected player to his server")
register_clcmd("say /retry", "cmd_retry", 0, "- redirect back as soon as the foregoing server has a free slot")
register_clcmd("say_team /retry", "cmd_retry", 0, "- redirect back as soon as the foregoing server has a free slot")
register_clcmd("say /stopretry", "cmd_stopretry", 0, "- stop retrying the foregoing server")
register_clcmd("say_team /stopretry", "cmd_stopretry", 0, "- stop retrying the foregoing server")
register_clcmd("redirect_announce_now", "announce_servers", ADMIN_KICK , "- announce server list immediately")
register_clcmd("redirect_user", "cmd_redirect_user", ADMIN_KICK , "<playername|playerid> [servernum] - redirect a player [to a given server]")

set_task(QUERY_INTERVAL, "query_servers", TASKID_QUERY, "", 0, "b")

// check whether we are in debug mode or not
new saDummy[2]
new saStatus[6]
get_plugin(-1, saDummy, 0, saDummy, 0, saDummy, 0, saDummy, 0, saStatus, 5)
g_bDebug = bool:equal(saStatus, "debug")
}

/// <summary>More initializations that have to be done here, because when <seealso name="plugin_init"/> is called CVARs are not yet set. They are in plugin_cfg(), but not for the first start of the game server with ./hlds_run so we use this extra function called once when the first player connects.</summary>
public plugin_postinit()
{
g_bInitialized = true
new sFullAddress[MAX_SERVERADDRESS_LEN]
new sTmpServerIP[MAX_IP_LEN + MAX_PORT_LEN]
get_cvar_string("net_address", sTmpServerIP, MAX_IP_LEN + MAX_PORT_LEN - 1)
new sTmpOwnAddress[MAX_SERVERADDRESS_LEN]
get_pcvar_string(cvar_external_address, sTmpOwnAddress, MAX_SERVERADDRESS_LEN - 1)

new nServerCount = 0
while (nServerCount < g_nServerCount)
{
format(sFullAddress, MAX_SERVERADDRESS_LEN - 1, "%s:%d", g_saServerAddresses[nServerCount], g_naServerPorts[nServerCount])
if (equal(sFullAddress, sTmpOwnAddress) || equal(sFullAddress, sTmpServerIP))
{
g_nOwnServer = nServerCount
break
}
nServerCount++
}

if (g_nOwnServer == -1) // we have not been able to detect the own server - inform the user about this
{
log_amx("%L", LANG_SERVER, "MSG_OWN_DETECTION_ERROR")
return PLUGIN_CONTINUE
}

if (get_pcvar_float(cvar_announce) > 0.0)
if (!task_exists(TASKID_ANNOUNCE))
set_task(get_pcvar_float(cvar_announce), "announce_servers", TASKID_ANNOUNCE, "", 0, "b")

return PLUGIN_CONTINUE
}


/// <summary>This is used to register the native redirect() function.</summary>
public plugin_natives()
{
register_native("redirect", "native_redirect", 1)
}

/// <summary>This is used to tell AMXX that the sockets module is required.</summary>
/// <remarks>Can be safely removed from the code when only redirect_check_method 0 will be used.</remarks>
public plugin_modules()
{
require_module("sockets")
}

/// <summary>Load servers from server list file.</summary>
/// <returns>true when servers have been successfully loaded, false if there were errors.</returns>
public bool:load_servers()
{
new sConfigDir[MAX_FILE_LEN], sServerFile[MAX_FILE_LEN]

get_configsdir(sConfigDir, MAX_FILE_LEN-1)
format(sServerFile, MAX_FILE_LEN-1, "%s/%s", sConfigDir, SERVERFILE)

if (!file_exists(sServerFile))
{
log_amx("%L", LANG_SERVER, "MSG_ERROR_NO_FILE", sServerFile)
return false
}

new nFilePos = 0
new sFileLine[MAX_SERVERLINE_LEN]
new nReadLen
new sPort[MAX_PORT_LEN]

new sKey[MAX_KEY_LEN]
new sValue[MAX_VALUE_LEN]

new nCurrentServer = -1

while (read_file(sServerFile, nFilePos++, sFileLine, MAX_SERVERLINE_LEN-1, nReadLen))
{
if ((sFileLine[0] == ';') && (strcmp(sFileLine, "") == 0)) continue // skip comment and empty lines

if ((sFileLine[0] == '[') && (sFileLine[strlen(sFileLine) - 1] == ']')) // a section starts
{
nCurrentServer++
if (nCurrentServer > 0)
{
// check whether the previous server was valid
if ((g_naServerPorts[nCurrentServer - 1] != 0) && (strcmp(g_saServerAddresses[nCurrentServer - 1], "") != 0))
{
g_nServerCount++
num_to_str(g_naServerPorts[nCurrentServer - 1], sPort, MAX_PORT_LEN - 1)
log_amx("%L", LANG_SERVER, "MSG_LOADED_SERVER", g_saServerNames[nCurrentServer - 1], g_saServerAddresses[nCurrentServer - 1], sPort)

}
else
nCurrentServer--
}

if (nCurrentServer >= MAX_SERVERFORWARDS)
break;

copy(g_saServerNames[nCurrentServer], strlen(sFileLine) - 2, sFileLine[1])

continue
}

if (nCurrentServer >= 0) // do we already have found a section?
{
strtok(sFileLine, sKey, MAX_KEY_LEN - 1, sValue, MAX_VALUE_LEN - 1, '=', 1)
strtoupper(sKey)
if (strcmp(sKey, "ADDRESS") == 0)
copy(g_saServerAddresses[nCurrentServer], MAX_SERVERADDRESS_LEN - 1, sValue)
else
if (strcmp(sKey, "LOCALADDRESS") == 0)
copy(g_saServerLocalAddresses[nCurrentServer], MAX_SERVERADDRESS_LEN - 1, sValue)
else
if (strcmp(sKey, "PASSWORD") == 0)
copy(g_saServerPasswords[nCurrentServer], MAX_PASSWORD_LEN - 1, sValue)
else
if (strcmp(sKey, "PUBLICPASSWORD") == 0)
{
if (is_str_num(sValue))
if (str_to_num(sValue) == 1)
g_naServerPublicPassword[nCurrentServer] = 1
}
else
if (strcmp(sKey, "PORT") == 0)
{
if (is_str_num(sValue))
g_naServerPorts[nCurrentServer] = str_to_num(sValue)
else
g_naServerPorts[nCurrentServer] = 27015
if ((g_naServerPorts[nCurrentServer] > 65536) || (g_naServerPorts[nCurrentServer] < 1024))
g_naServerPorts[nCurrentServer] = 27015
}
else
if (strcmp(sKey, "CMDBACKUP") == 0)
{
if (is_str_num(sValue))
g_naServerCmdBackup[nCurrentServer] = str_to_num(sValue)
else
g_naServerCmdBackup[nCurrentServer] = DEFAULT_CMDBACKUP
// protect from insane values
if ((g_naServerCmdBackup[nCurrentServer] > 100) || (g_naServerCmdBackup[nCurrentServer] < 0))
g_naServerCmdBackup[nCurrentServer] = DEFAULT_CMDBACKUP
}
else
if (strcmp(sKey, "NOAUTO") == 0)
{
if (is_str_num(sValue))
if (str_to_num(sValue) == 1)
g_naServerFlags[nCurrentServer] = g_naServerFlags[nCurrentServer] | (1<<SERVERFLAG_NOAUTO)
}
else
if (strcmp(sKey, "NOMANUAL") == 0)
{
if (is_str_num(sValue))
if (str_to_num(sValue) == 1)
g_naServerFlags[nCurrentServer] = g_naServerFlags[nCurrentServer] | (1<<SERVERFLAG_NOMANUAL)

}
else
if (strcmp(sKey, "NODISPLAY") == 0)
{
if (is_str_num(sValue))
if (str_to_num(sValue) == 1)
g_naServerFlags[nCurrentServer] = g_naServerFlags[nCurrentServer] | (1<<SERVERFLAG_NODISPLAY)
}
else
if (strcmp(sKey, "ADMINSLOTS") == 0)
{
if (is_str_num(sValue))
g_naServerReserveSlots[nCurrentServer] = str_to_num(sValue)
else
g_naServerReserveSlots[nCurrentServer] = 0
if ((g_naServerReserveSlots[nCurrentServer] > MAX_PLAYERS) || (g_naServerReserveSlots[nCurrentServer] < 0))
g_naServerReserveSlots[nCurrentServer] = 0
}
}
}

if ((nCurrentServer >= MAX_SERVERFORWARDS) || (nCurrentServer == -1))
return true;

// check whether the previous server was valid
if ((g_naServerPorts[nCurrentServer] != 0) && (strcmp(g_saServerAddresses[nCurrentServer], "") != 0))
{
g_nServerCount++
num_to_str(g_naServerPorts[nCurrentServer], sPort, MAX_PORT_LEN - 1)
log_amx("%L", LANG_SERVER, "MSG_LOADED_SERVER", g_saServerNames[nCurrentServer], g_saServerAddresses[nCurrentServer], sPort)
}

if (g_nServerCount < 2)
{
log_amx("%L", LANG_SERVER, "MSG_ERROR_NOT_ENOUGH_SERVERS")
return false
}

return true
}

/// <summary>Checks whether the IP in <paramref name="sCheckAddress"/> is a local address arcording to RFC 1918.</summary>
/// <summary>10.0.0.0 - 10.255.255.255 - single class A</summary>
/// <summary>172.16.0.0 - 172.31.255.255 - 16 contiguous class Bs</summary>
/// <summary>192.168.0.0 - 192.168.255.255 - 256 contiguous class Cs</summary>
/// <summary>169.254.0.0 - 169.254.255.255 - zeroconf</summary>
/// <param name="sCheckAddress">The IP address to check passed as a string.</param>
/// <returns>true if <paramref name="sCheckAddress"/> is a local IP address, false if not.</returns>
public bool:is_local_address(sCheckAddress[MAX_IP_LEN])
{
new sIPPart1[4]
new sIPPart2[4]
new nIPPart[4]
new sCompareIP[MAX_IP_LEN]
sCompareIP = sCheckAddress
strtok(sCheckAddress, sIPPart1, 3, sCheckAddress, MAX_IP_LEN - 1, '.')
nIPPart[0] = str_to_num(sIPPart1);
strtok(sCheckAddress, sIPPart1, 3, sCheckAddress, MAX_IP_LEN - 1, '.')
nIPPart[1] = str_to_num(sIPPart1);
strtok(sCheckAddress, sIPPart1, 3, sIPPart2, 3, '.')
nIPPart[2] = str_to_num(sIPPart1);
nIPPart[3] = str_to_num(sIPPart2);
return ((nIPPart[0] == 10) || ((nIPPart[0] == 192) && (nIPPart[1] == 168)) || ((nIPPart[0] == 172) && (nIPPart[1] > 15) && (nIPPart[1] < 32)) || ((nIPPart[0] == 169) && (nIPPart[1] == 254)))
}


/// <summary>Checks whether the player with ID <paramref name="nPlayerID"/> can be redirected to the server with server number <paramref name="nServerNum"/>.</summary>
/// <param name="nServer">The server number which shall be checked whether it is currently a valid redirection target.</param>
/// <param name="nPlayerID">The internal player ID which shall be checked for access to <paramref name="nServerNum"/>.</param>
/// <param name="nMode">Defines the redirection mode - 1 = automatic, 2 = manual.</param>
/// <param name="bIgnoreAdmin">Set to true, when the plugin should not tread admins as special, otherwise false.</param>
/// <returns>0 if redirection is possible, otherwise an error code: 1 = current server, 2 = no permission(passworded), 3 = manual redirection disabled. 4 = server full, 5 = server down, 6 = automatic redirection disabled.</returns>
public can_redirect_player(nServer, nPlayerID, nMode, bIgnoreAdmin)
{
if (nServer == -1)
return 0
new nCheckMethod = get_pcvar_num(cvar_check_method)

new bool:bCanRedirectByPassword = !(!equal(g_saServerPasswords[nServer], "") && (g_naServerPublicPassword[nServer] == 0))

if (nServer == g_nOwnServer)
return 1
else if (access(nPlayerID, MIN_ADMIN_LEVEL) && (!bIgnoreAdmin)) // even for admins it doesn't make sense to redirect to the current server so check admin rights from here
return 0
else if ((nCheckMethod > 0) && (!g_baServerResponding[nServer]))
return 5
else if (!bCanRedirectByPassword)
return 2
else if ((g_naServerFlags[nServer] & (1<<SERVERFLAG_NOMANUAL)) && (nMode == 2))
return 3
else if ((g_naServerFlags[nServer] & (1<<SERVERFLAG_NOAUTO)) && (nMode == 1))
return 6
else if ((nCheckMethod == 2) && (((g_naServerActivePlayers[nServer] == (g_naServerMaxPlayers[nServer] - 1)) && (g_naServerReserveSlots[nServer] > 0)) || (g_naServerActivePlayers[nServer] >= g_naServerMaxPlayers[nServer])))
return 4

return 0
}


/// <summary>Checks whether the player with ID <paramref name="nPlayerID"/> can be queued to redirect to the server with server number <paramref name="nServerNum"/>.</summary>
/// <param name="nServer">The server number which shall be checked whether it is currently a valid redirection queue target.</param>
/// <param name="nPlayerID">The internal player ID which shall be checked for access to <paramref name="nServerNum"/>.</param>
/// <returns>true if queueing is possible, otherwise false</returns>
public bool:can_queue_player(nServer, nPlayerID)
{
if (nServer == -1)
return false

new bIsAdmin = access(nPlayerID, MIN_ADMIN_LEVEL)

if ((get_pcvar_num(cvar_retry) == 0) && (!bIsAdmin)) // admin always can enqueue themselves, even when this feature is disabled
return false

new bool:bCanRedirectByPassword = !(!equal(g_saServerPasswords[nServer], "") && (g_naServerPublicPassword[nServer] == 0))
if (nServer == g_nOwnServer)
return false
if (bIsAdmin)
return true
if (((!bCanRedirectByPassword) || (g_naServerFlags[nServer] & (1<<SERVERFLAG_NOMANUAL))))
return false

return true
}

/// <summary>Checks whether the player with ID <paramref name="id"/> is already in redirection queue for server with number <paramref name="nServer"/>.</summary>
/// <param name="nServer">The server number which shall be checked whether player with <paramref name="id"/> is in its queue.</param>
/// <param name="id">The internal player ID which shall be checked whether it is queued for server <paramref name="nServer"/>.</param>
/// <remarks>A player can be in more than one queue but not twice in the queue for one server, <seealso name="queue_add"/> prevents double adding.</remarks>
/// <returns>true if player is in queue, false if not.</returns>
/// <seealso name="queue_add"/>
/// <seealso name="queue_remove"/>
public bool:is_queued(id, nServer)
{
new nCount = 0
while (nCount < g_nRetryCount)
{
if ((g_nRetryQueue[nCount][0] == id) && (g_nRetryQueue[nCount][1] == nServer))
return true
nCount++
}
return false
}

/// <summary>Adds the player with ID <paramref name="id"/> to the redirection queue for server with number <paramref name="nServer"/>.</summary>
/// <param name="nServer">The server number to add the player with <paramref name="id"/> to its queue.</param>
/// <param name="id">The internal player ID which shall be added to the queue for server <paramref name="nServer"/>.</param>
/// <remarks>A player can be in more than one queue but not twice in the queue for one server, <seealso name="queue_add"/> prevents double adding.</remarks>
/// <seealso name="is_queued"/>
/// <seealso name="queue_remove"/>
public queue_add(id, nServer)
{
if (get_pcvar_num(cvar_retry) > 0)
{
//first check whether the server-player-combination is not already in queue
new nCount = 0
new nServerQueue = 0
while (nCount < g_nRetryCount)
{
// count how many people are in the queue for the target server
if (g_nRetryQueue[nCount][1] == nServer)
{
nServerQueue++
// no need to continue when he already is in the queue
if (g_nRetryQueue[nCount][0] == id)
return
}
nCount++
}

new sUserNick[MAX_NAME_LEN]
get_user_name(id, sUserNick, MAX_NAME_LEN - 1)

if (get_pcvar_num(cvar_show) == 1)
{
new naPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount, nCurrentPlayer
get_players(naPlayers, nPlayerNum, "c")
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
nCurrentPlayer = naPlayers[nPlayerCount]
if (nCurrentPlayer != id) // he has his own message
client_print(nCurrentPlayer, print_chat, "%s: %L", PLUGIN_TAG, nCurrentPlayer, "MSG_QUEUE_ANNOUNCE", sUserNick, g_saServerNames[nServer])
}
}


if (g_bDebug)
log_amx("added player %i to queue for server %i in slot %i", id, nServer, g_nRetryCount)


g_nRetryQueue[g_nRetryCount][0] = id
g_nRetryQueue[g_nRetryCount][1] = nServer
g_nRetryCount++

client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_QUEUE_ADD", ++nServerQueue, g_saServerNames[nServer])
}
else
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_QUEUE_DEACTIVATED")
}

/// <summary>Removes the player with ID <paramref name="id"/> from the redirection queue for server with number <paramref name="nServer"/>.</summary>
/// <param name="nServer">The server number to remove the player with <paramref name="id"/> from its queue.</param>
/// <param name="id">The internal player ID which shall be removed from the queue for server <paramref name="nServer"/>.</param>
/// <remarks>A player can be in more than one queue but not twice in the queue for one server, <seealso name="queue_add"/> prevents double adding.</remarks>
/// <seealso name="is_queued"/>
/// <seealso name="add_remove"/>
public queue_remove(id, nServer)
{
new nCount = 0
while (nCount < g_nRetryCount)
{
if ((g_nRetryQueue[nCount][0] == id) && ((nServer == -1) || (g_nRetryQueue[nCount][1] == nServer)))
{ // ok, remove from queue and let all others go one place up

// in case it's the last entry in queue where the following loop would never be executed:
g_nRetryQueue[nCount][0] = -1
g_nRetryQueue[nCount][1] = -1

// move other entries up
while ((nCount + 1) < g_nRetryCount)
{
g_nRetryQueue[nCount][0] = g_nRetryQueue[nCount + 1][0]
g_nRetryQueue[nCount][1] = g_nRetryQueue[nCount + 1][1]
nCount++
}
g_nRetryCount--
break
}
nCount++
}
}

/// <summary>Resets the setinfo string of the player with <paramref name="id"/> by removing tags that xREDIRECT used.</summary>
/// <param name="id">The internal player ID of the player that shall have the setinfo data resetted. It is passed as an array so that this function can easily be called from <seealso name="set_task"/>.</param>
public reset_info(id[])
{
client_cmd(id[0], "setinfo ^"xredir^" ^"^"")
client_cmd(id[0], "setinfo ^"password^" ^"^"")

}

/// <summary>Announce the servers on top of the screen. The position and interval for announcements can be set by CVARs.</summary>
public announce_servers()
{
if (get_pcvar_num(cvar_active) == 1)
{
if (g_nServerCount > 0)
{
new nCheckMethod = get_pcvar_num(cvar_check_method)
new sAnnounceBody[MAX_MENUBODY_LEN] = ""
new nDisplayCount = 0
new nServerCount = g_nNextAnnounceServer
if (nServerCount >= g_nServerCount)
nServerCount = 0

while ((nServerCount < g_nServerCount) && (nDisplayCount < 8))
{

if (!((g_naServerFlags[nServerCount] & (1<<SERVERFLAG_NODISPLAY)) || ((get_pcvar_num(cvar_hidedown) > 1) && (!g_baServerResponding[nServerCount]) && (nServerCount != g_nOwnServer))))
{
if (nServerCount == g_nOwnServer)
{
new sMap[MAX_MAP_LEN]
get_mapname(sMap, MAX_MAP_LEN - 1)
format(sAnnounceBody, MAX_MENUBODY_LEN - 1, "%s^n%s [%s] (%d/%d)", sAnnounceBody, g_saServerNames[nServerCount], sMap, get_playersnum(1), get_maxplayers())
}
else
{
if (nCheckMethod == 0)
format(sAnnounceBody, MAX_MENUBODY_LEN - 1, "%s^n%s", sAnnounceBody, g_saServerNames[nServerCount])
else
if (g_baServerResponding[nServerCount])
{
if (nCheckMethod == 1)
format(sAnnounceBody, MAX_MENUBODY_LEN - 1, "%s^n%s", sAnnounceBody, g_saServerNames[nServerCount])
else if (nCheckMethod == 2)
format(sAnnounceBody, MAX_MENUBODY_LEN - 1, "%s^n%s [%s] (%d/%d)", sAnnounceBody, g_saServerNames[nServerCount], g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
}
else
format(sAnnounceBody, MAX_MENUBODY_LEN - 1, "%s^n%s (down)", sAnnounceBody, g_saServerNames[nServerCount])
}
}
nServerCount++
nDisplayCount++
}
g_nNextAnnounceServer = nServerCount
set_hudmessage(000, 100, 255, -1.0, 0.01, 0, 0.0, 10.0, 0.5, 0.10, 1)
//show_hudmessage(0, sAnnounceBody)

if (get_pcvar_float(cvar_announce) > 0.0)
{
new nAnnounceMode = get_pcvar_num(cvar_announce_mode)
if (nAnnounceMode > 0)
{
new naPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount
new sAnnounceText[MAX_MENUBODY_LEN]
if ((nAnnounceMode == 1) || (nAnnounceMode == 3))
{
get_players(naPlayers, nPlayerNum, "ac") // alive players
set_hudmessage(000, 100, 255, get_pcvar_float(cvar_announce_alivepos_x), get_pcvar_float(cvar_announce_alivepos_y), 0, 0.0, 10.0, 0.5, 0.10, 1)
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
if (get_pcvar_num(cvar_manual) >= 1)
format(sAnnounceText, MAX_MENUBODY_LEN - 1, "%L^n%s", naPlayers[nPlayerCount], "MSG_SAY_SERVER", sAnnounceBody)
else
sAnnounceText = sAnnounceBody
show_hudmessage(naPlayers[nPlayerCount], sAnnounceText)
}
}
if ((nAnnounceMode == 2) || (nAnnounceMode == 3))
{
get_players(naPlayers, nPlayerNum, "bc") // dead players
set_hudmessage(000, 100, 255, get_pcvar_float(cvar_announce_deadpos_x), get_pcvar_float(cvar_announce_deadpos_y), 0, 0.0, 10.0, 0.5, 0.10, 1) // show list at lower position for them so it is not covered by the "spectator bars"
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
if (get_pcvar_num(cvar_manual) >= 1)
format(sAnnounceText, MAX_MENUBODY_LEN - 1, "%L^n%s", naPlayers[nPlayerCount], "MSG_SAY_SERVER", sAnnounceBody)
else
sAnnounceText = sAnnounceBody
show_hudmessage(naPlayers[nPlayerCount], sAnnounceText)
}
}
}
}
}
}
return PLUGIN_HANDLED
}


/// <summary>Shows the sub menu for server with number <paramref name="nServer"/> to the the player with ID <paramref name="id"/>.</summary>
/// <param name="nServer">The server to show the sub menu for.</param>
/// <param name="id">The ID of the player to show the sub menu.</param>
/// <seealso name="server_menu_select"/>
/// <seealso name="sub_menu_select"/>
/// <seealso name="show_server_menu"/>
public show_sub_menu(id, nServer)
{
new nCanRedirect = can_redirect_player(nServer, id, 2, false)
new nCanRedirectIgnoreAdmin = can_redirect_player(nServer, id, 2, true);
new bool:bCanQueue = can_queue_player(nServer, id)
new bColorMenu = (colored_menus() && !MENU_FORCENOCOLOR)
new nCheckMethod = get_pcvar_num(cvar_check_method)
new sMenuBody[MAX_MENUBODY_LEN]

// can we display colors?
if (bColorMenu)
{
format(sMenuBody, MAX_MENUBODY_LEN - 1, "\y%L^n", id, "MSG_SRVINFO_CAPTION")
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y%L \w%s", sMenuBody, id, "MSG_SRVINFO_NAME", g_saServerNames[nServer])
}
else
{
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%L^n", id, "MSG_SRVINFO_CAPTION")
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n%L %s", sMenuBody, id, "MSG_SRVINFO_NAME", g_saServerNames[nServer])
}

// can we display map and player information?
if (((nCheckMethod == 2) && ((g_baServerResponding[nServer])) || (nServer == g_nOwnServer)))
{
if (bColorMenu)
{
if (nServer == g_nOwnServer)
{
new sMap[MAX_MAP_LEN]
get_mapname(sMap, MAX_MAP_LEN - 1)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y%L \w%s", sMenuBody, id, "MSG_SRVINFO_MAP", sMap)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y%L \w%d/%d", sMenuBody, id, "MSG_SRVINFO_PLAYERS", get_playersnum(1), get_maxplayers())
}
else
{
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y%L \w%s", sMenuBody, id, "MSG_SRVINFO_MAP", g_saServerMap[nServer])
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y%L \w%d/%d", sMenuBody, id, "MSG_SRVINFO_PLAYERS", g_naServerActivePlayers[nServer], g_naServerMaxPlayers[nServer])
}
}
else
{
if (nServer == g_nOwnServer)
{
new sMap[MAX_MAP_LEN]
get_mapname(sMap, MAX_MAP_LEN - 1)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n%L %s", sMenuBody, id, "MSG_SRVINFO_MAP", sMap)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n%L %d/%d", sMenuBody, id, "MSG_SRVINFO_PLAYERS", get_playersnum(1), get_maxplayers())
}
else
{
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n%L %s", sMenuBody, id, "MSG_SRVINFO_MAP", g_saServerMap[nServer])
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n%L %d/%d", sMenuBody, id, "MSG_SRVINFO_PLAYERS", g_naServerActivePlayers[nServer], g_naServerMaxPlayers[nServer])
}
}
}

// make the next line red if colors are supported and (the user is no admin or it's the current server)
if ((bColorMenu) && ((!access(id, MIN_ADMIN_LEVEL)) || (nCanRedirect == 1)))
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\r", sMenuBody)
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n", sMenuBody)

// now display reason why we can't redirect there
switch (nCanRedirectIgnoreAdmin)
{
case 1:
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s%L", sMenuBody, id, "MSG_SRVINFO_ERR_CURRENT")
case 2:
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s%L", sMenuBody, id, "MSG_SRVINFO_ERR_PERMISSION")
case 3:
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s%L", sMenuBody, id, "MSG_SRVINFO_ERR_NOMANUAL")
case 4:
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s%L", sMenuBody, id, "MSG_SRVINFO_ERR_FULL")
case 5:
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s%L", sMenuBody, id, "MSG_SRVINFO_ERR_DOWN")
}

// enable/disable key for redirection/queue functionality
new key = (1<<9) // cancel
key = key | (1<<8) // back
if ((nCheckMethod > 0) || (nServer == g_nOwnServer))
key = key | (1<<2) // refresh
if (nCanRedirect == 0)
key = key | (1<<0) // redirect
if (bCanQueue && (nCheckMethod > 1))
key = key | (1<<1) // enqueue

new sQueueMsg[30]
if (is_queued(id, nServer))
sQueueMsg = "MSG_LEAVEQUEUE"
else
sQueueMsg = "MSG_QUEUE"

// display the last menu items according to availability
if (bColorMenu)
{
if (nCanRedirect == 0)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n\y1. \w %L", sMenuBody, id, "MSG_REDIRECT")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n\y1. \d %L", sMenuBody, id, "MSG_REDIRECT")
if (bCanQueue && (nCheckMethod > 1))
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y2. \w %L", sMenuBody, id, sQueueMsg)
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y2. \d %L", sMenuBody, id, sQueueMsg)
if ((nCheckMethod > 0) || (nServer == g_nOwnServer))
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y3. \w %L", sMenuBody, id, "MSG_REFRESH")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y3. \d %L", sMenuBody, id, "MSG_REFRESH")
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n\y9. \w %L", sMenuBody, id, "MSG_BACK")
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y0. \w %L", sMenuBody, id, "MSG_CANCEL")
}
else
{
if (nCanRedirect == 0)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n1. %L", sMenuBody, id, "MSG_REDIRECT")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n_. %L", sMenuBody, id, "MSG_REDIRECT")
if (bCanQueue && (nCheckMethod > 1))
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n2. %L", sMenuBody, id, sQueueMsg)
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n_. %L", sMenuBody, id, sQueueMsg)
if ((nCheckMethod > 0) || (nServer == g_nOwnServer))
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n3. %L", sMenuBody, id, "MSG_REFRESH")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n_. %L", sMenuBody, id, "MSG_REFRESH")
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n9. %L", sMenuBody, id, "MSG_BACK")
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n0. %L", sMenuBody, id, "MSG_CANCEL")
}
g_nLastSelected[id - 1] = nServer
show_menu(id, key, sMenuBody, -1, "Detail Menu")
}

/// <summary>Shows the server menu page <paramref name="menupage"/> to the the player with ID <paramref name="id"/>.</summary>
/// <param name="id">The ID of the player to show the server menu to.</param>
/// <param name="menupage">The menu page number to show to the player. Offset is 0.</param>
/// <seealso name="server_menu_select"/>
/// <seealso name="sub_menu_select"/>
/// <seealso name="show_sub_menu"/>
public show_server_menu(id, menupage)
{
new nServerCount
if (get_pcvar_num(cvar_active) == 1)
{
if (g_nServerCount > 0)
{
new bool:bSubMenu = (get_pcvar_num(cvar_manual) >= 2)
new bool:bColorMenu = (colored_menus() && !MENU_FORCENOCOLOR)
new nCheckMethod = get_pcvar_num(cvar_check_method)
new sMenuBody[MAX_MENUBODY_LEN]
if (bColorMenu)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "\y%L^n", id, "MSG_SELECT_SERVER")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%L^n", id, "MSG_SELECT_SERVER")


if (menupage <= 1)
nServerCount = 0
else
nServerCount = g_naMenuPageStart[id - 1][menupage - 2]

new nDisplayNumber = 1

new key = (1<<9) // cancel key is always enabled

new nHideDown = get_pcvar_num(cvar_hidedown)
if (nHideDown == 1)
nHideDown = 3

// the 3 parts of a menu item, third part only displayed with redirect_check_method >= 2
new sMenuNumber[10]
new sMenuSrvName[50]
new sMenuInfo[50]
if (nCheckMethod < 2)
sMenuInfo = ""

while ((nDisplayNumber < 9) && (nServerCount < g_nServerCount))
{
if (!((g_naServerFlags[nServerCount] & (1<<SERVERFLAG_NODISPLAY)) || ((nHideDown > 2) && (!g_baServerResponding[nServerCount]) && (nServerCount != g_nOwnServer))))
{
new bool:bCanRedirectByPassword = !(!equal(g_saServerPasswords[nServerCount], "") && (g_naServerPublicPassword[nServerCount] == 0) && (!access(id, MIN_ADMIN_LEVEL)))

if (bColorMenu)
{
format(sMenuNumber, 9, "\y%d. ", nDisplayNumber)
if (bSubMenu)
format(sMenuSrvName, 49, "\w %s", g_saServerNames[nServerCount])
else
format(sMenuSrvName, 49, "\d %s", g_saServerNames[nServerCount])
}
else
{
format(sMenuNumber, 9, "%d. ", nDisplayNumber)
format(sMenuSrvName, 49, " %s", g_saServerNames[nServerCount])
}

new bool:bCanRedirect = true
sMenuInfo = ""

// manual redirection to that server is disabled or server is passworded but password is not public and user has insufficent admin rights
if ((nCheckMethod == 2) && (((g_naServerFlags[nServerCount] & (1<<SERVERFLAG_NOMANUAL)) || !bCanRedirectByPassword)))
{
bCanRedirect = false
if ((!bColorMenu) && (!bSubMenu))
sMenuNumber = "_. "
if (nCheckMethod == 2)
format(sMenuInfo, 49, " [%s] (%d/%d)", g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
}
// server is full (and player has insufficient rights to join on an admin slot)
if ((nCheckMethod == 2) && (((g_naServerActivePlayers[nServerCount] == (g_naServerMaxPlayers[nServerCount] - 1)) && (g_naServerReserveSlots[nServerCount] > 0) && (!access(id, MIN_ADMIN_LEVEL))) || (g_naServerActivePlayers[nServerCount] >= g_naServerMaxPlayers[nServerCount])))
{
bCanRedirect = false
if ((!bColorMenu) && (!bSubMenu))
sMenuNumber = "_. "
if (bColorMenu)
format(sMenuInfo, 49, " [%s] \r(\w%d/%d\r)", g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
else
format(sMenuInfo, 49, " [%s] (%d/%d)", g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
}
// server is down
if ((nCheckMethod > 0) && (!g_baServerResponding[nServerCount]))
{
if ((!bColorMenu) && (!bSubMenu))
sMenuNumber = "_. "
bCanRedirect = false
if (bColorMenu)
sMenuInfo = " \r(\wdown\r)"
else
sMenuInfo = " (down)"
}
// server is current server
if (nServerCount == g_nOwnServer)
{
if ((!bColorMenu) && (!bSubMenu))
sMenuNumber = "_. "
bCanRedirect = false
new sMap[MAX_MAP_LEN]
get_mapname(sMap, MAX_MAP_LEN - 1)
if (bSubMenu && bColorMenu)
format(sMenuInfo, 49, " \y[\w%s\y] \y(\w%d/%d\y)", sMap, get_playersnum(1), get_maxplayers())
else
format(sMenuInfo, 49, " [%s] (%d/%d)", sMap, get_playersnum(1), get_maxplayers())
}

// everything's fine, we can redirect here
if (bCanRedirect)
{
if (bColorMenu)
{
format(sMenuSrvName, 49, "\w %s", g_saServerNames[nServerCount])
if (nCheckMethod > 1)
format(sMenuInfo, 49, " \y[\w%s\y] \y(\w%d/%d\y)", g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
}
else
{
if (nCheckMethod > 1)
format(sMenuInfo, 49, " [%s] (%d/%d)", g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
}

key = key | (1<<(nDisplayNumber - 1))
g_naServerSelections[id - 1][nDisplayNumber - 1] = nServerCount
}
else if ((bSubMenu) && (nServerCount != g_nOwnServer)) // display server like it was enabled when submenues are enabled
if (bColorMenu)
{
format(sMenuSrvName, 49, "\w %s", g_saServerNames[nServerCount])
if ((nCheckMethod == 0) && (g_baServerResponding[nServerCount]))
format(sMenuInfo, 49, " \y[\w%s\y] \y(\w%d/%d\y)", g_saServerMap[nServerCount], g_naServerActivePlayers[nServerCount], g_naServerMaxPlayers[nServerCount])
}

// assemble the menu item and append it to menu body
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n%s%s%s", sMenuBody, sMenuNumber, sMenuSrvName, sMenuInfo)

// if enabled a submenu is always possible to be displayed, regardless of the server's redirection status
if (bSubMenu)
{
key = key | (1<<(nDisplayNumber - 1))
g_naServerSelections[id - 1][nDisplayNumber - 1] = nServerCount
}

nDisplayNumber++
}
nServerCount++
}

if (nServerCount < g_nServerCount)
{
if (bColorMenu)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n\y9.\w %L", sMenuBody, id, "MSG_MORE")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n9. %L", sMenuBody, id, "MSG_MORE")
key = key | (1<<8)
}
else
{
if (bColorMenu)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n\y9.\d %L", sMenuBody, id, "MSG_MORE")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n^n_. %L", sMenuBody, id, "MSG_MORE")
}

if (bColorMenu)
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n\y0.\w %L", sMenuBody, id, "MSG_CANCEL")
else
format(sMenuBody, MAX_MENUBODY_LEN - 1, "%s^n0. %L", sMenuBody, id, "MSG_CANCEL")

show_menu(id, key, sMenuBody, -1, "Redirect Menu")
}
}
g_naMenuPageStart[id - 1][menupage - 1] = nServerCount

g_naLastMenuPages[id - 1] = menupage
}


/// <summary>Reloads the servers from server list. Takes care of variable and array reinitialization.</summary>
/// <remarks>To be able to rely on this in the future make sure to add an initialization here for all variables you add!</remarks>
public srvcmd_reload()
{
new nCounter

// clear all global arrays and variables before reloading
for (nCounter = 0; nCounter < MAX_SERVERFORWARDS; nCounter++)
{
if (g_naServerSockets[nCounter] > 0)
{
socket_close(g_naServerSockets[nCounter])
g_naServerSockets[nCounter] = 0
}
g_naServerPorts[nCounter] = 27015
g_naServerActivePlayers[nCounter] = -1
g_naServerMaxPlayers[nCounter] = -1
g_naServerCmdBackup[nCounter] = DEFAULT_CMDBACKUP
g_naServerFlags[nCounter] = 0
g_naServerReserveSlots[nCounter] = 0
g_baServerResponding[nCounter] = false
g_saServerMap[nCounter] = ""
g_saServerNames[nCounter] = ""
g_saServerAddresses[nCounter] = ""
g_saServerPasswords[nCounter] = ""
g_naServerPublicPassword[nCounter] = 0
}

// reset global variables
g_nNextAnnounceServer = 0
g_nServerCount = 0
g_nLastRedirectServer = -1
g_sLastRedirectName = ""
g_nOwnServer = -1
g_nRetryCount = 0

for (new nPlrCnt = 0; nPlrCnt < MAX_PLAYERS; nPlrCnt++)
{
// server IDs might change and thus render all currently saved server IDs invalid, so remove them, to be sure
g_nRetryQueue[nPlrCnt][0] = -1
g_nRetryQueue[nPlrCnt][1] = -1
g_nLastServer[nPlrCnt] = -1
g_nLastSelected[nPlrCnt] = -1
}

load_servers()

new sFullAddress[MAX_SERVERADDRESS_LEN]
new sTmpServerIP[MAX_IP_LEN + MAX_PORT_LEN]
get_cvar_string("net_address", sTmpServerIP, MAX_IP_LEN + MAX_PORT_LEN - 1)
new sTmpOwnAddress[MAX_SERVERADDRESS_LEN]
get_pcvar_string(cvar_external_address, sTmpOwnAddress, MAX_SERVERADDRESS_LEN - 1)

// define the own server again
new nServerCount = 0
while (nServerCount < g_nServerCount)
{
format(sFullAddress, MAX_SERVERADDRESS_LEN - 1, "%s:%d", g_saServerAddresses[nServerCount], g_naServerPorts[nServerCount])
if (equal(sFullAddress, sTmpOwnAddress) || equal(sFullAddress, sTmpServerIP))
{
g_nOwnServer = nServerCount
break
}
nServerCount++
}
if (g_nOwnServer == -1)
log_amx("%L", LANG_SERVER, "MSG_OWN_DETECTION_ERROR")
}


/// <summary>This is needed so server doesn't display "unknown command: pickserver". Returning PLUGIN_HANDLED directly in cmd_show_server_menu would supress the chat message so we use this workaround.</summary>
public cmd_pickserver(id, level, cid)
{
cmd_show_server_menu(id, level, cid)
return PLUGIN_HANDLED
}

/// <summary>This function does the actual redirection. It is also what <seealso name="native_redirect"/> is a wrapper for with <paramref name="nServer"/> preset to -1 (the external plugin does not know about our server list and numbers anyway) and <paramref name="bIgnoreSource"/> preset to true (an external plugin does not care whether this would mean redirecting the player back to where he came from).</summary>
/// <summary>It is aware of user permissions and has several options which are set via parameters.</summary>
/// <param name="id">ID of player to redirect.</param>
/// <param name="nServer">Target server, -1 for automatic choosing according to redirect_auto.</param>
/// <param name="bCanOther">If nServer is no valid redirect target can we use another server instead?</param>
/// <param name="bCanDrop">Drop user if no server was found?</param>
/// <param name="bIgnoreSource>"Redirect regardless of redirecting would be back to source server.</param>
/// <seealso name="native_redirect"/>
/// <seealso name="cmd_redirect_user"/>
public redirect(id, nServer, bCanOther, bCanDrop, bIgnoreSource)
{

new nForwardServer = -1
new bool:bFoundServer = false
new nRedirType
if (nServer == -1)
nRedirType = 1
else
nRedirType = 2

new nSourceServer

if (bIgnoreSource)
{
nSourceServer = -1
}
else
{
new sSourceServer[3]
get_user_info(id, "xredir", sSourceServer, 2)
if (!is_str_num(sSourceServer))
nSourceServer = -1
else
nSourceServer = str_to_num(sSourceServer)
if ((nSourceServer < 0) || (nSourceServer >= g_nServerCount))
nSourceServer = -1
}

if ((can_redirect_player(nServer, id, nRedirType, false) > 0) || (nServer == -1))
{
if (!bCanOther)
{
if (bCanDrop)
{
client_cmd(id, "echo %s: %L", PLUGIN_TAG, id, "MSG_NO_REDIRECT_SERVER")
client_cmd(id, "disconnect")
}
return false
}

nForwardServer = 0

// make sure at least one valid server exists or the second loop could be endless
while (nForwardServer < g_nServerCount)
{
if ((can_redirect_player(nForwardServer, id, nRedirType, false) == 0) && (nForwardServer != nSourceServer))
{
bFoundServer = true
break
}
nForwardServer++
}
new nAutoMode = get_pcvar_num(cvar_auto)
if ((nAutoMode == 1) || (nAutoMode == 3) || (nAutoMode == 5)) // redirect to random server
nForwardServer = -1
}
else
{
nForwardServer = nServer
bFoundServer = true
}

if (bFoundServer)
{
while (nForwardServer == -1)
{
nForwardServer = random_num(0, g_nServerCount - 1)
if ((can_redirect_player(nForwardServer, id, nRedirType, false) > 0) || ((nForwardServer == nSourceServer)))
nForwardServer = -1
}

new sUserNick[MAX_NAME_LEN]
get_user_name(id, sUserNick, MAX_NAME_LEN - 1)
if (!equal(g_saServerPasswords[nForwardServer], "")) // set the user's server connect password if needed
client_cmd(id, "setinfo ^"password^" ^"%s^"", g_saServerPasswords[nForwardServer])
client_cmd(id, "setinfo ^"xredir^" ^"%d^"", g_nOwnServer)

new sCheckAddress[MAX_IP_LEN]
get_user_ip(id, sCheckAddress, MAX_IP_LEN - 1, 1)
new sFullAddress[MAX_SERVERADDRESS_LEN]
if (is_local_address(sCheckAddress))
format(sFullAddress, MAX_SERVERADDRESS_LEN - 1, "%s:%d", g_saServerLocalAddresses[nForwardServer], g_naServerPorts[nForwardServer])
else
format(sFullAddress, MAX_SERVERADDRESS_LEN - 1, "%s:%d", g_saServerAddresses[nForwardServer], g_naServerPorts[nForwardServer])
if (nRedirType == 1)
client_cmd(id, "echo %s: %L", PLUGIN_TAG, id, "MSG_SERVER_FULL_REDIRECTING", g_saServerNames[nForwardServer])
client_cmd(id, "connect %s", sFullAddress)


if (get_pcvar_num(cvar_show) == 1)
{
new nPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount, nCurrentPlayer
get_players(nPlayers, nPlayerNum, "c")
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
nCurrentPlayer = nPlayers[nPlayerCount]
if (get_pcvar_num(cvar_follow) == 1)
client_print(nCurrentPlayer, print_chat, "%s: %L - %L", PLUGIN_TAG, nCurrentPlayer, "MSG_REDIRECTED", sUserNick, g_saServerNames[nForwardServer], nCurrentPlayer, "MSG_FOLLOW")
else
client_print(nCurrentPlayer, print_chat, "%s: %L", PLUGIN_TAG, nCurrentPlayer, "MSG_REDIRECTED", sUserNick, g_saServerNames[nForwardServer])
}
}

g_nLastRedirectServer = nForwardServer
g_sLastRedirectName = sUserNick
}
else if (bCanDrop)
{
client_cmd(id, "echo %s: %L", PLUGIN_TAG, id, "MSG_NO_REDIRECT_SERVER")
client_cmd(id, "disconnect")
}
return true
}

/// <summary>Basically a wrapper for <seealso name="redirect"/> to make it available to other pugins as native.</summary>
/// <seealso name="redirect"/>
/// <seealso name="cmd_redirect_user"/>
public native_redirect(id, nServer, bCanDrop)
{
redirect(id, nServer, (nServer == -1), bCanDrop, true)
return PLUGIN_HANDLED
}

/// <summary>Handler for in-game command <paramref name="redirect_user"/>, checks user permissions for this command and uses <seealso name="redirect"/> to do the redirection.</summary>
/// <seealso name="redirect"/>
/// <seealso name="native_redirect"/>
public cmd_redirect_user(id, level, cid)
{
if (!cmd_access(id, level, cid, 2))
return PLUGIN_HANDLED

new nForwardServer = -1
new sName[32]
read_argv(1, sName, 31)
new nCmdID = cmd_target(id, sName, 8)

if (!nCmdID)
return PLUGIN_HANDLED

// contains destination server number?
if (read_argc() > 2)
{
new argtmp[3]
read_argv(2, argtmp, 2)
if (is_str_num(argtmp))
nForwardServer = (str_to_num(argtmp) - 1)
}

redirect(nCmdID, nForwardServer, (nForwardServer == -1), true, true)

return PLUGIN_HANDLED
}

/// <summary>Handler for in-game command <paramref name="pickserver"/> or chat command <paramref name="/server"/>. Shows the server menu to the player using <seealso name="show_server_menu"/>.</summary>
/// <seealso name="show_server_menu"/>
public cmd_show_server_menu(id, level, cid)
{
if (get_pcvar_num(cvar_manual) >= 1)
show_server_menu(id, 1)
else
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_MANUAL_DISABLED")
return PLUGIN_CONTINUE
}

/// <summary>Handler for chat command <paramref name="/retry"/>. Adds the user to the retry queue using <seealso name="queue_add"/>.</summary>
/// <seealso name="queue_add"/>
public cmd_retry(id, level, cid)
{
if (g_nLastServer[id - 1] > -1)
queue_add(id, g_nLastServer[id - 1])
else
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_QUEUE_NO_LAST")
return PLUGIN_CONTINUE
}

/// <summary>Handler for chat command <paramref name="/stopretry"/>. Removes the user from the retry queue using <seealso name="queue_remove"/>.</summary>
/// <seealso name="queue_remove"/>
public cmd_stopretry(id, level, cid)
{
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_QUEUE_REMOVE_ALL", g_saServerNames[g_nLastServer[id - 1]])
queue_remove(id, -1)
return PLUGIN_CONTINUE
}

/// <summary>Handler for chat command <paramref name="/follow"/>. Sends a player after the last player that was redirected using <seealso name="redirect"/>.</summary>
/// <seealso name="redirect"/>
public cmd_follow_player(id, level, cid)
{
if (get_pcvar_num(cvar_active) == 1)
{
if (get_pcvar_num(cvar_follow) == 1)
{
if (g_nLastRedirectServer >= 0)
{
console_print(id, "%s: %L", PLUGIN_TAG, id, "MSG_REDIRECTING", g_saServerNames[g_nLastRedirectServer])
new sFullAddress[MAX_SERVERADDRESS_LEN]
new sCheckAddress[MAX_IP_LEN]
get_user_ip(id, sCheckAddress, MAX_IP_LEN - 1, 1)
if (is_local_address(sCheckAddress))
format(sFullAddress, MAX_SERVERADDRESS_LEN - 1, "%s:%d", g_saServerLocalAddresses[g_nLastRedirectServer], g_naServerPorts[g_nLastRedirectServer])
else
format(sFullAddress, MAX_SERVERADDRESS_LEN - 1, "%s:%d", g_saServerAddresses[g_nLastRedirectServer], g_naServerPorts[g_nLastRedirectServer])
client_cmd(id, "connect %s", sFullAddress)
new sUserNick[MAX_NAME_LEN]
get_user_name(id, sUserNick, MAX_NAME_LEN - 1)
if (get_pcvar_num(cvar_show) == 1)
client_print(0, print_chat, "%s: %L - %L", PLUGIN_TAG, id, "MSG_FOLLOWED", sUserNick, g_sLastRedirectName, g_saServerNames[g_nLastRedirectServer], id, "MSG_FOLLOW")
g_sLastRedirectName = sUserNick
}
else
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_CANT_FOLLOW")

}
else
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_FOLLOW_DISABLED")
}
return PLUGIN_CONTINUE
}

/// <summary>Event handler for sub menu selection.</summary>
/// <summary>When the user presses a number key in the sub menu this handler is called.</summary>
/// <param name="id">Slot ID of player that selected a menu item.</param>
/// <param name="key">Key that was pressed, number between 0 and 9.</param>
/// <seealso name="server_menu_select"/>
/// <seealso name="show_server_menu"/>
/// <seealso name="show_sub_menu"/>
public sub_menu_select(id, key)
{
new nServer = g_nLastSelected[id - 1]
if (key == 0) // redirect
{
// check if meanwhile the redirection is not possible anymore - if so, refresh the detail menu
if (can_redirect_player(nServer, id, 2, false) > 0)
show_sub_menu(id, nServer)
else
redirect(id, nServer, false, false, true)
}
else if (key == 1) // queue
{
if (is_queued(id, nServer))
{
queue_remove(id, nServer)
client_print(id, print_chat, "%s: %L", PLUGIN_TAG, id, "MSG_QUEUE_REMOVE", g_saServerNames[nServer])
}
else
{
queue_add(id, nServer)
}
}
else if (key == 2) // refresh
{
show_sub_menu(id, nServer)
}
else if (key == 8) // go back to where the user was before in main menu
show_server_menu(id, g_naLastMenuPages[id - 1])
}

/// <summary>Event handler for server menu selection.</summary>
/// <summary>When the user presses a number key in the server menu this handler is called.</summary>
/// <summary>Depending on settings it will display a sub menu or redirect the user.</summary>
/// <param name="id">Slot ID of player that selected a menu item.</param>
/// <param name="key">Key that was pressed, number between 0 and 9.</param>
/// <seealso name="sub_menu_select"/>
/// <seealso name="show_server_menu"/>
/// <seealso name="show_sub_menu"/>
public server_menu_select(id, key)
{
if (key < 8)
{
new nServerIdx = g_naServerSelections[id - 1][key]

new nManualMode = get_pcvar_num(cvar_manual)
// show the detail menu?
if (((nManualMode == 2) && (can_redirect_player(nServerIdx, id, 2, false) > 0)) || (nManualMode == 3))
show_sub_menu(id, nServerIdx)
else
redirect(id, nServerIdx, false, false, true)
}
else
{
if (key == 8)
show_server_menu(id, g_naLastMenuPages[id - 1] + 1)
}
}


/// <summary>Sends the information query packets to all other servers.</summary>
/// <summary>This sends the UDP server information query packets in old and new style HL format to all servers in the list.</summary>
/// <summary>Receiving of server data is handled by <seealso name="receive_serverquery_answers"/>.</summary>
/// <seealso name="receive_serverquery_answers"/>
public query_servers()
{
new nCheckMethod = get_pcvar_num(cvar_check_method)
if (nCheckMethod == 0)
return PLUGIN_HANDLED
new socket_error
new sOldRequest[9]
new sNewRequest[25]

if (nCheckMethod == 1)
// ping format didn't change for new style queries so one query for all servers
format(sOldRequest, 9, "%c%c%c%c%s", 255, 255, 255, 255, "ping")
else if (nCheckMethod == 2)
{
// we don't know what server it is so send both old and new style query
format(sOldRequest, 9, "%c%c%c%c%s", 255, 255, 255, 255, "info")
format(sNewRequest, 24, "%c%c%c%c%s", 255, 255, 255, 255, "TSource Engine Query")
}

new nServerCount = 0
new nQuerySocket
new nCmdBackup
new nSendCount
while (nServerCount < g_nServerCount)
{
if (nServerCount != g_nOwnServer)
{
nQuerySocket = g_naServerSockets[nServerCount]
if (nQuerySocket > 0)
{
new sEmptyBufferDummy[512]
new nEndlessProtection = 0
while ((socket_change(nQuerySocket, 1)) && (nEndlessProtection < 500))
{
//log_amx("emptying socket %i (%s)", nQuerySocket, g_saServerNames[nServerCount])
socket_recv(nQuerySocket, sEmptyBufferDummy, 512)
nEndlessProtection++
}
if (nEndlessProtection >= 500)
{
socket_close(nQuerySocket)
log_amx("WARNING: endless protection triggered for socket %i (%s)", nQuerySocket, g_saServerNames[nServerCount])
}

}
else
{
//log_amx("opening socket for server %i (%s)", nServerCount, g_saServerNames[nServerCount])
if (!equal(g_saServerLocalAddresses[nServerCount], ""))
nQuerySocket = socket_open(g_saServerLocalAddresses[nServerCount], g_naServerPorts[nServerCount], SOCKET_UDP, socket_error)
else
nQuerySocket = socket_open(g_saServerAddresses[nServerCount], g_naServerPorts[nServerCount], SOCKET_UDP, socket_error)
//log_amx("opened socket %i for server %i (%s)", nQuerySocket, nServerCount, g_saServerNames[nServerCount])
}

if ((nQuerySocket > 0) && (socket_error == 0))
{
g_naServerSockets[nServerCount] = nQuerySocket
nCmdBackup = g_naServerCmdBackup[nServerCount]

for (nSendCount = -1; nSendCount < nCmdBackup; nSendCount++)
socket_send(nQuerySocket, sOldRequest, 9)

if (nCheckMethod == 2)
for (nSendCount = -1; nSendCount < nCmdBackup; nSendCount++)
socket_send(nQuerySocket, sNewRequest, 24)
}
else
{
g_naServerSockets[nServerCount] = 0
log_amx("%L", LANG_SERVER, "MSG_SOCKET_ERROR", socket_error, nServerCount)
}
}
nServerCount++
}
set_task(QUERY_TIMEOUT, "receive_serverquery_answers", TASKID_QUERY_RECEIVE)

return PLUGIN_HANDLED
}

/// <summary>Handler for parsing the answers to server query packet.</summary>
/// <summary>This handler parses the UDP information answer packets from the servers that have been queried with <seealso name="query_servers"/>.</summary>
/// <seealso name="query_servers"/>
public receive_serverquery_answers()
{
new nCheckMethod = get_pcvar_num(cvar_check_method)

new sRcvBuf[MAX_INFO_LEN]
new nCharCnt
new nStructPos
new nActivePlayers
new nMaxPlayers
new nRcvLen
new nRecvCount
new sMap[MAX_MAP_LEN]
new nServerCount = 0
while (nServerCount < g_nServerCount)
{
if (!g_naServerSockets[nServerCount])
{
g_baServerResponding[nServerCount] = false
/*
should only happen for the g_nOwnServer

client_print(0, print_chat, "%s no socket", g_saServerNames[nServerCount])
*/
}
else
{
nRecvCount = 0
new nCmdBackup = g_naServerCmdBackup[nServerCount]
g_baServerResponding[nServerCount] = false
new nSocket = g_naServerSockets[nServerCount]
while (socket_change(nSocket, 1) && (nRecvCount <= nCmdBackup))
{
//log_amx("socket changed: %i (%s)", nSocket, g_saServerNames[nServerCount])
nRecvCount++

// initialize our receive buffer
setc(sRcvBuf, MAX_INFO_LEN, 0);
//for (nClearCounter = 0; nClearCounter < MAX_INFO_LEN; nClearCounter++)
//sRcvBuf[nClearCounter] = 0
//log_amx("receiving from socket: %i (%s)", nSocket, g_saServerNames[nServerCount])
nRcvLen = socket_recv(nSocket, sRcvBuf, MAX_INFO_LEN)
//log_amx("finished receiving from socket %i (%s), received %i bytes", nSocket, g_saServerNames[nServerCount], nRcvLen)


if (nRcvLen > 5) // shortest reply is a ping response with length of 6
{
if (nCheckMethod == 1)
{
// ping response
if (equal(sRcvBuf, {-1,-1,-1,-1,'j'}, 5))
{
g_baServerResponding[nServerCount] = true
break
}
}
else if (nCheckMethod == 2)
{
if (equal(sRcvBuf, {-1,-1,-1,-1}, 4))
{
// old style HL1 response || new style HL1 response
if ((sRcvBuf[4] == 'C') || (sRcvBuf[4] == 'm'))
{
g_baServerResponding[nServerCount] = true
nStructPos = 0
nCharCnt = 0
while ((nStructPos <= STRUCT_INFO_POS_MODVERSION) && (nCharCnt < MAX_INFO_LEN))
{
if (equal(sRcvBuf[nCharCnt], {0}))
{
nStructPos++
switch (nStructPos)
{
case STRUCT_INFO_POS_MAP:
{
copyc(sMap, MAX_MAP_LEN - 1, sRcvBuf[nCharCnt + 1], 0)
g_saServerMap[nServerCount] = sMap
}
case STRUCT_INFO_POS_ACTIVEPLAYERS:
{
nActivePlayers = int:sRcvBuf[nCharCnt + 1]
g_naServerActivePlayers[nServerCount] = nActivePlayers
nMaxPlayers = int:sRcvBuf[nCharCnt + 2]
g_naServerMaxPlayers[nServerCount] = nMaxPlayers
break
}
/* deactivated for 1.0 release, this code does not work
case STRUCT_INFO_POS_MODVERSION:
{
if (!get_pcvar_num(cvar_countbots))
{
new nBotCount = int:sRcvBuf[nCharCnt+13]
if (nBotCount)
g_naServerActivePlayers[nServerCount] -= nBotCount
}
break
}
*/
}
}
nCharCnt++
}
break
}
}
}
}
}
/*
if (nRecvCount == 0)
log_amx("no change on socket %i (%s)", g_naServerSockets[nServerCount], g_saServerNames[nServerCount])
*/
//socket_close(nSocket)
//g_naServerSockets[nServerCount] = 0
}
nServerCount++
}

if (get_pcvar_num(cvar_retry) > 0)
{
// now search for players who queued themselves to be redirected
new nServer
new nPlrCnt = 0

while (nPlrCnt < g_nRetryCount)
{
nServer = g_nRetryQueue[nPlrCnt][1]
if (nServer > -1) // just to be sure
{
new nPlr = g_nRetryQueue[nPlrCnt][0]
if (can_redirect_player(nServer, nPlr, 2, false) == 0)
{
console_print(nPlr, "%s: %L", PLUGIN_TAG, nPlr, "MSG_RETRY_SUCCESS")
redirect(nPlr, nServer, false, false, true)
g_naServerActivePlayers[nServer]++
}
}
nPlrCnt++
}
}

return PLUGIN_HANDLED
}

/// <summary>Retrieves number of admins currently on the server.</summary>
/// <returns>Number of admins currently on server.</returns>
public get_admin_count()
{
new nPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount
get_players(nPlayers, nPlayerNum, "ch")
new nAdmins = 0
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
if (access(nPlayers[nPlayerCount], MIN_ADMIN_LEVEL))
nAdmins++
}
return nAdmins
}

/// <summary>Event handler for client disconnect event.</summary>
/// <summary>This handler makes sure people that have been in queue while disconnecting are removed from it.</summary>
/// <summary>Furthermore it resets the "last server" information for this now empty player slot.</summary>
/// <param name="id">Slot ID of player that was disconnected.</param>
public client_disconnect(id)
{
queue_remove(id, -1)
g_nLastServer[id - 1] = -1
}

/// <summary>Event handler for client authorized event.</summary>
/// <summary>This handler is called as soon as a connecting client was authenticated with WON/Steam system and received a WON/Steam ID.</summary>
/// <summary>It is used in favor of client_connected(), because here the client already logged in to AMXX user system and it can be determined whether the user is an admin, which is not the case for client_connected() event.</summary>
/// <param name="id">Slot ID of player that was authorized.</param>
public client_authorized(id)
{
if (is_user_bot(id) || is_user_hltv(id))
return PLUGIN_CONTINUE

if ((g_nOwnServer == -1) && (!g_bInitialized))
{
plugin_postinit()
}

g_naLastMenuPages[id - 1] = 1

new nAutoMode = get_pcvar_num(cvar_auto)
if (get_pcvar_num(cvar_active) == 1)
{
if (nAutoMode > 0)
{
if (((get_maxplayers() - get_playersnum(1)) == 0) || (nAutoMode > 2))
{
if (g_nServerCount > 0)
{
new bool:bLocalPriority = false
// if local slot reservation is enabled we need to check whether this is a local player
if (get_pcvar_num(cvar_localslots) == 1)
{
new sCheckAddress[MAX_IP_LEN]
get_user_ip(id, sCheckAddress, MAX_IP_LEN - 1, 1)
if (is_local_address(sCheckAddress))
bLocalPriority = true
}
new nMaxAdmins = get_pcvar_num(cvar_maxadmins)
if (nMaxAdmins == 0)
nMaxAdmins = MAX_PLAYERS
new bool:bRedirect = false // to keep some better overview assemble the if-comparison part by part in bRedirect
// redirect if automode is 1 or 2, user is no admin or is admin but there are no admin slots (disabled or max admin slots in use already)
bRedirect = bRedirect | (((nAutoMode == 1) || (nAutoMode == 2)) && ((!access(id, MIN_ADMIN_LEVEL)) || (get_pcvar_num(cvar_adminslots) == 0) || (get_admin_count() > nMaxAdmins)))
// redirect if automode is 3 or 4 and user is no admin
bRedirect = bRedirect | (((nAutoMode == 3) || (nAutoMode == 4)) && (!access(id, MIN_ADMIN_LEVEL)))
// redirect if automode is 5 or 6
bRedirect = bRedirect | ((nAutoMode == 5) || (nAutoMode == 6))
if (bRedirect)
{
if (bLocalPriority)
{
// find the remote user that is connected for the shortest time and redirect him

new nPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount
new nMinConnectedTime = 0x7FFFFFFF
new nMinTimePlayer = -1
new nUserTime
get_players(nPlayers, nPlayerNum, "ch")
new nCurID
new sCheckAddress[MAX_IP_LEN]
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
nCurID = nPlayers[nPlayerCount]
get_user_ip(nCurID, sCheckAddress, MAX_IP_LEN - 1, 1)

nUserTime = get_user_time(nCurID)
if ((nUserTime < nMinConnectedTime) && (!access(nCurID, MIN_ADMIN_LEVEL)) && (!is_local_address(sCheckAddress)))
{
nMinTimePlayer = nCurID
nMinConnectedTime = nUserTime
}
}
if (nMinTimePlayer >= 0)
{
client_cmd(nMinTimePlayer, "echo %s: %L", PLUGIN_TAG, nMinTimePlayer, "MSG_REDIRFORLOCAL")
redirect(nMinTimePlayer, -1, true, true, true)
return PLUGIN_CONTINUE
}
else
if (g_bDebug)
log_amx("no valid redirect target to free up slot for local player %i", id)

}
else
{
redirect(id, -1, true, (nAutoMode < 3), false)
return PLUGIN_CONTINUE
}
}
else
{
// find the user that is connected for the shortest time and redirect him away

new nPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount
new nMinConnectedTime = 0x7FFFFFFF
new nMinTimePlayer = -1
new nUserTime
get_players(nPlayers, nPlayerNum, "ch")
new nCurID
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
nCurID = nPlayers[nPlayerCount]

nUserTime = get_user_time(nCurID)
if ((nUserTime < nMinConnectedTime) && (!access(nCurID, MIN_ADMIN_LEVEL)))
{
nMinTimePlayer = nCurID
nMinConnectedTime = nUserTime
}
}
if (nMinTimePlayer >= 0)
{
client_cmd(nMinTimePlayer, "echo %s: %L", PLUGIN_TAG, nMinTimePlayer, "MSG_REDIRFORADMIN")
redirect(nMinTimePlayer, -1, true, true, true)
return PLUGIN_CONTINUE
}
else
if (g_bDebug)
log_amx("no valid redirect target to free up slot for admin %i", id)
}

}
}
}
}

// show the welcome message delayed to that player
new sID[1]
sID[0] = id
set_task(20.0, "welcome_message", 0, sID, 1)

new sSourceServer[4] // maximum is 999 servers, so we have a maximum of 3 digits
get_user_info(id, "xredir", sSourceServer, 3)
if (strcmp(sSourceServer, "") != 0)
{
new nSourceServer = str_to_num(sSourceServer)
g_nLastServer[id - 1] = nSourceServer
if (g_bDebug)
log_amx("saved last server for player %i as server %i", id, g_nLastServer[id - 1])

if ((nSourceServer >= 0) && (nSourceServer < g_nServerCount))
{
if (get_pcvar_num(cvar_show) == 1)
{
new nPlayers[MAX_PLAYERS]
new nPlayerNum, nPlayerCount, nCurrentPlayer
new sConnectNick[MAX_NAME_LEN]
get_user_name(id, sConnectNick, MAX_NAME_LEN - 1)
get_players(nPlayers, nPlayerNum, "c")
set_hudmessage(000, 100, 255, get_pcvar_float(cvar_announce_alivepos_x), get_pcvar_float(cvar_announce_alivepos_y), 0, 0.0, 10.0, 0.5, 0.10, 1)
for (nPlayerCount = 0; nPlayerCount < nPlayerNum; nPlayerCount++)
{
nCurrentPlayer = nPlayers[nPlayerCount]
client_print(nCurrentPlayer, print_chat, "%s: %L", PLUGIN_TAG, nCurrentPlayer, "MSG_REDIRECT_RECEIVE", sConnectNick, g_saServerNames[nSourceServer])
}
}
}
new sID[1]
sID[0] = id

client_cmd(id, "setinfo ^"xredir^" ^"^"")
client_cmd(id, "setinfo ^"password^" ^"^"")

set_task(10.0, "reset_info", 0, sID, 1)
}
return PLUGIN_CONTINUE
}


/// <summary>This function shows a message to the player that has connected, to tell him that he was redirected and how he can use /retry to get back (if so).</summary>
/// <summary>welcome_message is called with a set_task to show the welcome message delayed, so that the player has usually already chosen a team and his screen is clear to read it.</summary>
/// <summary>This message is only displayed to players that have been redirected from another server in the chain. If redirect_retry is enabled, it also tells the player</summary>
/// <summary>that he can use /retry command to have himself queued to redirect back to the source server.</summary>
/// <param name="id">The slot ID of the player that should have the welcome message displayed. It is passed as array, because it is called with set_task.</param>
public welcome_message(id[])
{
new nID = id[0]
if (is_user_connected(nID)) // make sure the player didn't already disconnect within the set_task delay
{
new nLastServer = g_nLastServer[nID - 1]
if ((nLastServer >= 0) && (nLastServer != g_nOwnServer) && (nLastServer < MAX_SERVERFORWARDS))
{
new sAnnounceText[MAX_WELCOME_LEN]
format(sAnnounceText, MAX_WELCOME_LEN - 1, "%L", nID, "MSG_REDIRFROM", g_saServerNames[g_nOwnServer], g_saServerNames[nLastServer])
if ((get_pcvar_num(cvar_retry) == 1) && (get_pcvar_num(cvar_show) == 1))
format(sAnnounceText, MAX_WELCOME_LEN - 1, "%s^n%L", sAnnounceText, nID, "MSG_RETRY_BACK_ANNOUNCE")

set_hudmessage(000, 100, 255, -1.0, -1.0, 0, 0.0, 10.0, 0.5, 2.0, 1)
show_hudmessage(nID, sAnnounceText)
}
}
}


#else

/// <summary>Dummy handler to catch the case where a user tried to compile the plugin with a too old compiler.</summary>
public plugin_init()
{
log_amx("ERROR: Your AMXX version is too old for this plugin.")
}
#endif


Если поможет.

Отредактировал: HeBuguMka, - 12.4.2013, 12:27
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
Статус пользователя kasIIep
сообщение 12.4.2013, 11:22
Сообщение #6
Стаж: 17 лет

Сообщений: 333
Благодарностей: 88
Полезность: 209

Меценат Меценат

Попробуй такой xredirect_steam
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
Статус пользователя kasIIep
сообщение 12.4.2013, 11:55
Сообщение #7
Стаж: 17 лет

Сообщений: 333
Благодарностей: 88
Полезность: 209

Меценат Меценат

Код под спойлер спрячь, в том который я кинул редирект поправлен и работал.
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
urpok
сообщение 12.4.2013, 12:32
Сообщение #8
Стаж: 13 лет

Сообщений: 890
Благодарностей: 164
Полезность: 0

Цитата
Код под спойлер спрячь, в том который я кинул редирект поправлен и работал.


Не мог спрятать, зависло... Хз что это было. Три раза редактил.

А то что он стимовский, у меня просто большая часть НОН играет, они смогут переходить ?
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
Статус пользователя kasIIep
сообщение 12.4.2013, 12:38
Сообщение #9
Стаж: 17 лет

Сообщений: 333
Благодарностей: 88
Полезность: 209

Меценат Меценат

HeBuguMka,
Да все будет работать, проверяй выключить ни когда не поздно.
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
urpok
сообщение 12.4.2013, 12:47
Сообщение #10
Стаж: 13 лет

Сообщений: 890
Благодарностей: 164
Полезность: 0

Цитата
Да все будет работать, проверяй выключить ни когда не поздно.


Окей. попробую... Если что, с меня спасибка.
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
urpok
сообщение 21.4.2013, 17:34
Сообщение #11
Стаж: 13 лет

Сообщений: 890
Благодарностей: 164
Полезность: 0

Цитата
Попробуй такой xredirect_steam


Нет, не помогло. Теперь я и даже на домашнем компе не могу перейти... Что за делема, в чём может быть проблема ? Народ, у кого стоит такой плагин, поделитесь исходником, или советом, и вообще, может ли быть, проблема в самом клиенте, но, тогда как так получается, что раньше мог зайти, если дома был, а сейчас не могу.
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
Статус пользователя xoomie
сообщение 21.4.2013, 18:23
Сообщение #12


Стаж: 13 лет

Сообщений: 136
Благодарностей: 15
Полезность: 93

Народ. Сколько можно Постов создавать по данному плагину поисковиком не про бывали искать на форуме?
Перейти в начало страницы         Просмотр профиля    Отправить личное сообщение
   + Цитировать сообщение
  Тема закрытаНачать новую тему
 
0 пользователей и 1 гостей читают эту тему: