AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Scripting Help (https://forums.alliedmods.net/forumdisplay.php?f=11)
-   -   Redirect servers crash on mapchange (https://forums.alliedmods.net/showthread.php?t=154381)

darrin 04-07-2011 07:47

Redirect servers crash on mapchange
 
Code:

/*
    AMXX Plugin: DuplicateServerInfo
    Author: Sylwester
 
    Plugin information and updates available at:
    http://forums.alliedmods.net/showthread.php?p=1326999
*/
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <fun>
#include <sockets>
#define UPDATE_INTERVAL 1.0
#define TID_TIMER 3536
new pcvar_target
new pcvar_password
new pcvar_fake_map
new pcvar_query_interval
new g_socket = 0
new g_ch_ret[4] = {255,255,255,255}
new g_s_hostname[128]
new g_s_map[64]
new g_s_desc[64]
new g_s_max_players
new g_s_players
new g_s_bots
new g_s_player_name[33][33]
new g_s_player_kills[33]
new g_timer_entid
new Float:g_t_time
new g_max_players
new pcvar_hostname
new pcvar_visiblemaxplayers
new pcvar_dsi_hostname
new pcvar_dsi_gamedesc
new pcvar_dsi_map
new pcvar_dsi_players
new pcvar_dsi_nobots_str
new pcvar_dsi_bots_str
new pcvar_dsi_maxplayers
new g_map[64]
new g_base_hostname[128]
public plugin_init(){
    register_plugin("DuplicateServerInfo", "1.44", "Sylwester")
    g_max_players = get_maxplayers()
    pcvar_target = register_cvar("dsi_target", "0.0.0.0:0")
    pcvar_password = register_cvar("dsi_password", "")
    pcvar_fake_map = register_cvar("dsi_fake_map", "dummy.bsp")
    pcvar_query_interval = register_cvar("dsi_query_interval", "10")
    pcvar_visiblemaxplayers = get_cvar_pointer("sv_visiblemaxplayers")
    pcvar_dsi_hostname = register_cvar("dsi_hostname", "1")
    pcvar_dsi_gamedesc = register_cvar("dsi_gamedesc", "1")
    pcvar_dsi_map = register_cvar("dsi_map", "1")
    pcvar_dsi_players = register_cvar("dsi_players", "1")
    pcvar_dsi_maxplayers = register_cvar("dsi_maxplayers", "1")
 
    pcvar_dsi_nobots_str = register_cvar("dsi_nobots_str", "")
    pcvar_dsi_bots_str = register_cvar("dsi_bots_str", "")
    pcvar_hostname = get_cvar_pointer("hostname")
    get_mapname(g_map, 63)
    register_forward(FM_GetGameDescription, "GameDescryption")
    set_task(1.0, "start_dsi")
    //create (if necessary) and execute cfg file storing name of dummy map
    new cfgdir[256], fh
    get_configsdir(cfgdir, 255)
    format(cfgdir, 255, "%s%s", cfgdir, "/dsi_fake_map.cfg")
    if(!file_exists(cfgdir)){
        fh = fopen(cfgdir, "wt")
        if(!fh){
            log_amx("Error: Could not open file for writing (%s).", cfgdir)
            return
        }else{
            fputs(fh, "dsi_fake_map dummy.bsp^n")
            fclose(fh)
        }
    }
    server_cmd("exec %s", cfgdir)
}
 
public start_dsi(){
    set_cvar_num("sv_allowdownload", 0) //make sure it's impossible to download fake map
    get_pcvar_string(pcvar_hostname, g_base_hostname, 127)
    create_timer()
    if(connect_server())
        send_request()
}
 
public timer_cycle(){
    static cycles
    cycles++
    check_socket()
    if(cycles*UPDATE_INTERVAL >= get_pcvar_num(pcvar_query_interval)){
        cycles = 0
        disconnect_server()
        if(!connect_server())
            return
        send_request()
    }
}
 
public plugin_end(){
    if(g_socket > 0){
        socket_close(g_socket)
        g_socket = 0
    }
    set_pcvar_string(pcvar_hostname, g_base_hostname)
}
 
public update_hostname(){
    static hostname[128], tmp[32], dsi_hostname
    dsi_hostname = get_pcvar_num(pcvar_dsi_hostname)
    if(dsi_hostname==0){
        copy(hostname, 127, g_base_hostname)
    }else{
        copy(hostname, 127, g_s_hostname)
    }
    if(g_s_bots <= 0){
        get_pcvar_string(pcvar_dsi_nobots_str, tmp, 31)
    }else{
        get_pcvar_string(pcvar_dsi_bots_str, tmp, 31)
    }
    if(strlen(tmp)>1 && containi(hostname, tmp)==-1)
        add(hostname, 127, tmp)
    set_pcvar_string(pcvar_hostname, hostname)
}
 
public update_maxplayers(){
    if(get_pcvar_num(pcvar_dsi_maxplayers)==0)
        return
    if(g_s_max_players < 1)
        return
    set_pcvar_num(pcvar_visiblemaxplayers, g_s_max_players)
}
 
public update_players(){
    static players, p_list[32]
    static p_list2[32], p_list2_cnt, userid, tmp[128]
    static p_name[32], fc_id
    if(get_pcvar_num(pcvar_dsi_players)==0){
        g_s_players = 0
    }
    //now update fake clients - amount, names, frags
    if(g_s_players > g_max_players)
        g_s_players = g_max_players
    players = 0
    for(new i=1; i<=g_max_players; i++)
        if(is_user_connected(i))
            p_list[players++] = i
    p_list2_cnt = g_s_players
    for(new i=0; i<p_list2_cnt; i++)
        p_list2[i]=i
    //remove fakeclients with names that do not appear in info of target server
    //update score of fakeclients
    for(new i=0; i<players; i++){
        new j
        get_user_name(p_list[i], p_name, 31)
        while(j<p_list2_cnt && !equal(p_name, g_s_player_name[p_list2[j]]))
            j++
        if(j==p_list2_cnt){
            //name not found - kick
            userid = get_user_userid(p_list[i])
            server_cmd("kick #%d", userid)
        }else{
            //name found - update data
            set_user_frags(p_list[i], g_s_player_kills[p_list2[j]])
            p_list2[j] = p_list2[--p_list2_cnt]
        }
    }
    //create fakeclients for remaining names
    for(new i=0; i<p_list2_cnt; i++){
        fc_id = engfunc(EngFunc_CreateFakeClient, g_s_player_name[p_list2[i]])
        dllfunc(DLLFunc_ClientConnect, fc_id, g_s_player_name[p_list2[i]], "127.0.0.1", tmp)
        dllfunc(DLLFunc_ClientPutInServer, fc_id)
        if(0 < fc_id < 33)
            set_user_frags(fc_id, g_s_player_kills[p_list2[i]])
    }
}
 
public check_socket(){
    if(g_socket <= 0)
        return
    new cnt
    while(socket_change(g_socket, 1) && cnt < 10){
        cnt++
        receive_data()
    }
}
 
public connect_server(){
    static host[64], port, tmp[128], error, szError[64], tmp2[32]
    get_pcvar_string(pcvar_target, tmp, 127)
    strtok(tmp, host, 63, tmp2, 31, ':')
    port = str_to_num(tmp2)
    if(strlen(host) < 2 || port <=0 || port >= 65536){
        log_amx("incorrect data stored in dsi_target cvar ( ^"%s^" ), must be ^"ip:port^" or ^"dns:port^"", tmp)
        return false
    }
    if(g_socket > 0){
        socket_close(g_socket)
        g_socket = 0
    }
    g_socket = socket_open(host, port, SOCKET_UDP, error)
    if(g_socket <= 0 || error){
        switch(error){
            case 0: szError = "unknown"
            case 1: szError = "error while creating socket"
            case 2: szError = "could not resolve hostname"
            case 3: format(szError, 63, "could not connect to %s:%d", host, port)
        }
        log_amx("Socket error: %s", szError)
        return false
    }
    return true
}
 
public disconnect_server(){
    if(g_socket > 0){
        socket_close(g_socket)
        g_socket = 0
    }
}
 
public client_connect(id){
    static ip[32], pw[64]
    get_pcvar_string(pcvar_password, pw, 63)
    if(strlen(pw)>0){
        client_cmd(id, "password %s", pw)
    }
    get_pcvar_string(pcvar_target, ip, 31)
    client_cmd(id, "Connect %s", ip)
}
 
public create_timer(){
    g_timer_entid = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString,"info_target"))
    if(pev_valid(g_timer_entid)){
        set_pev(g_timer_entid, pev_classname, "dsi_timer")
        global_get(glb_time, g_t_time)
        set_pev(g_timer_entid, pev_nextthink, g_t_time + UPDATE_INTERVAL)
        register_forward(FM_Think,"fwd_Think")
    }else{
        log_amx("Warning: Failed to create timer entity, using task instead")
        set_task(UPDATE_INTERVAL, "timer_cycle", TID_TIMER, "", 0, "b")
    }
}
 
public GameDescryption(){
    if(get_pcvar_num(pcvar_dsi_gamedesc)==0)
        return FMRES_IGNORED
    if(strlen(g_s_desc)<=1)
        return FMRES_IGNORED
    forward_return(FMV_STRING, g_s_desc)
    return FMRES_SUPERCEDE
}
 
public update_map(){
    if(get_pcvar_num(pcvar_dsi_map)==0)
        return
    if(!equal(g_map, g_s_map)){
        if(!is_map_valid(g_s_map))
            create_fake_map()
        if(is_map_valid(g_s_map)){
            server_cmd("changelevel ^"%s^"", g_s_map)
        }else{
            log_amx("Warning: Unable to change map to %s (map file is not valid or does not exist)", g_s_map)
        }
    }
}
 
public ret_ac(num){ //change byte -128..127 into 0..255
    if(num<0)
        return 256+num
    return num
}
 
public send_request(){
    if(g_socket <= 0)
        return
    new cache[32]
    //http://developer.valvesoftware.com/wiki/Server_Queries#A2S_INFO
    format(cache, 25, "%c%c%c%c%c%s%c", 255, 255, 255, 255, 84, "Source Engine Query", 0 )
    socket_send2(g_socket, cache, 25)
    //http://developer.valvesoftware.com/wiki/Server_Queries#A2S_PLAYER
    format(cache, 25, "%c%c%c%c%c%c%c%c%c", 255, 255, 255, 255, 85, g_ch_ret[0], g_ch_ret[1], g_ch_ret[2], g_ch_ret[3])  //ping
    socket_send2(g_socket, cache, 9)
}
 
public create_fake_map(){
    new map[64], path_src[128], path_dest[128]
    get_pcvar_string(pcvar_fake_map, map, 63)
    format(path_src, 127, "maps/%s", map)
    format(path_dest, 127, "maps/%s.bsp", g_s_map)
    if(!file_exists(path_src)){
        log_amx("Warning: unable to create fake map file, source file %s does not exist", path_src)
        return
    }
    if(file_exists(path_dest)){
        log_amx("Warning: unable to create fake map file, map file already exists %s (but is not valid map)", path_dest)
        return
    }
    if(!rename_file(path_src, path_dest, 1)){
        log_amx("Warning: unable to create fake map file, renaming failed (%s -> %s)", path_src, path_dest)
        return
    }
    new cfgdir[256], cache[128], fh
    get_configsdir(cfgdir, 255)
    format(cfgdir, 255, "%s%s", cfgdir, "/dsi_fake_map.cfg")
    fh = fopen(cfgdir, "wt")
    if(!fh){
        log_amx("Error: Could not open file for writing (%s).", cfgdir)
        return
    }else{
        format(cache, 127, "dsi_fake_map %s.bsp^n", g_s_map)
        fputs(fh, cache)
        fclose(fh)
    }
}
 
public fwd_Think(Ent){
    if(Ent != g_timer_entid)
        return FMRES_IGNORED
    g_t_time += UPDATE_INTERVAL
    set_pev(Ent, pev_nextthink, g_t_time)
    timer_cycle()
    return FMRES_IGNORED
}
 
public receive_data(){
    static cache[2048], len
    len = socket_recv(g_socket, cache, 2047)
    if(len < 5)
        return
    if(!equal( cache, { -1, -1, -1, -1 }, 4 ) )
        return
    if(cache[4] == 'D'){ //(players details) A2S_PLAYER reply if sent challange number is correct
        g_s_players = cache[5]
        new j=6
        for(new i=0; i<g_s_players; i++){
            j++
            j += copyc(g_s_player_name[i], 31, cache[j], 0)+1
            g_s_player_kills[i] =  ret_ac(cache[j+3])<<24|ret_ac(cache[j+2])<<16|ret_ac(cache[j+1])<<8|ret_ac(cache[j])
            j+=8
        }
        update_players()
    }
    //(correct challange number) A2S_PLAYER reply if sent challange number is unknown/incorrect
    if(cache[4] == 'A'){
        g_ch_ret[0] = cache[5]
        g_ch_ret[1] = cache[6]
        g_ch_ret[2] = cache[7]
        g_ch_ret[3] = cache[8]
        new tmp[16]
        format(tmp, 15, "%c%c%c%c%c%c%c%c%c", 255, 255, 255, 255, 85, g_ch_ret[0], g_ch_ret[1], g_ch_ret[2], g_ch_ret[3])  //ping
        socket_send2(g_socket, cache, 9)
    }
    if(cache[4] == 'I'){ //(server details) A2S_INFO reply
        new i = 6, dir[64]
        i += copyc(g_s_hostname, 127, cache[i], 0)+1
        i += copyc(g_s_map, 63, cache[i], 0)+1
        i += copyc(dir, 63, cache[i], 0)+1
        i += copyc(g_s_desc, 63, cache[i], 0)+1
        g_s_max_players = cache[i+3]
        g_s_bots = cache[i+4]
        new pw[63]
        get_pcvar_string(pcvar_password, pw, 63)
        if(cache[i+7] && strlen(pw)<=0){
            log_amx("Warning: target server is password protected, set dsi_password to allow redirected players to join target server")
        }
        update_map()
        update_hostname()
        update_maxplayers()
    }
}

All my redirect servers crash if the game server changes map, which it does every 20 min. I think this is because I have set the redirect servers to immitate a fake dummy map from the real server, or something.
This plugin is from: http://forums.alliedmods.net/showthread.php?t=140821
I have commented out some of the code to make it work with this plugin:
http://forums.alliedmods.net/showthread.php?p=254620

Code:

//now update fake clients - amount, names, frags
    /*if(g_s_players > g_max_players)
        g_s_players = g_max_players
    players = 0
    for(new i=1; i<=g_max_players; i++)
        if(is_user_connected(i))
            p_list[players++] = i
    p_list2_cnt = g_s_players
    for(new i=0; i<p_list2_cnt; i++)
        p_list2[i]=i*/


However the redirect servers come back online after 1 minute from the mapchange


All times are GMT -4. The time now is 20:08.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.