Page 1 of 7 123 ... LastLast
Results 1 to 10 of 69

Thread: Saving a variable client-side (persistently)

  1. #1
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts

    Saving a variable client-side (persistently)

    Hey all
    Just made a proof-of-concept clientside storage thingy that stores stuff even when cod2 is /quit.

    Ill release it next week or so

  2. The Following 3 Users Say Thank You to IzNoGoD For This Useful Post:

    guiismiti (30th December 2013),RobsoN (30th December 2013),Whiskas (18th December 2018)

  3. #2
    Private First Class RobsoN's Avatar
    Join Date
    Jan 2013
    Location
    /home/cod2/
    Posts
    230
    Thanks
    119
    Thanked 95 Times in 64 Posts
    Amazing ! Well done. Can't wait for release.

    P.s does it require libcod?
    "Don't worry if your code doesn't work correctly - if everything worked, you would not work" ~Mosher's right

  4. #3
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    Nope, so far its all just stock cod2 functions

  5. #4
    Private First Class php's Avatar
    Join Date
    Nov 2012
    Posts
    142
    Thanks
    28
    Thanked 116 Times in 59 Posts
    Confirmed, nice find izzo

  6. #5
    Sergeant serthy's Avatar
    Join Date
    Nov 2012
    Posts
    450
    Thanks
    96
    Thanked 296 Times in 188 Posts
    is its done with the client vars/writeconfig/autoexec/openscriptmenu combination? also looking forward to it

  7. #6
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    Currently resolving security concerns when a rogue server admin of another server copies the fs_game of the target, hosts a serv and lets a high-ranking player of that other serv join his server.

    I think I blocked that pretty nicely with a theoretical 310 bit security

  8. #7
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    Read below. Still not secure.

    Ok, release time

    It basically works like this:

    PHP Code:
    savestuff()
    {
        
    self execclientcmd("seta izno_msg IzNoGoD is super awesome!; writeconfig test.cfg; clear;");
    }

    loadstuff()
    {
        
    self execclientcmd("writeconfig temp.cfg; exec test; unbind all; exec temp; clear;");

    Using this technique allows you to remotely save a username and password.

    The problem is, once someone knows these cvars (and they are not exactly a secret), they can set up a dummy server and steal these usernames/passwords from users, then abuse this.

    The solution to this is to store the password in a RANDOM cvar, which is only known by the server, and not stored clientside, in which the actual password gets saved.

    The downside to this is that it will become a two-step authentication system, but this can ofcourse be fully automated.

    An implementation of said method:

    PHP Code:
    init()
    {
        
    game["menu_clientcmd"] = "clientcmd";
        
    precacheMenu(game["menu_clientcmd"]);
        
    init_stattracking();
        
    init_ranks();
        
    thread waitforconnect();
    }

    waitforconnect()
    {
        while(
    true)
        {
            
    level waittill("connecting"player);
            
    player thread onconnect();
        }
    }

    onconnect()
    {
        
    self.stats = [];
        for(
    0level.stats.sizei++)
            
    self.stats[level.stats[i]] = 0;
    }

    execclientcmd(str)
    {
        
    self setclientcvar("execcmd"str);
        
    self openmenu(game["menu_clientcmd"]);
        
    self closemenu();
    }

    createnewaccount()
    {
        
    created false;
        
    str "";
        
    chl "";
        
    chl_resp "";
        while(!
    created)
        {
            
    str "";
            
    src "abcdefghijklmnopqrstuvwxyz0123456789";
            for(
    030i++)
                
    str += src[randomint(src.size)];
            
    chl "izno_challenge_";
            for(
    030i++)
                
    chl += src[randomint(src.size)];
            
    chl_resp "";
            for(
    030i++)
                
    chl_resp += src[randomint(src.size)];
            
    self.pers["izno_login_challenge"] = chl;
            
    self.pers["izno_login_response"] = chl_resp;
            
    fid openfile("player_" str ".txt""read");
            if(
    fid == -1)
            {
                
    fid openfile("player_" str ".txt""write");
                
    closefile(fid);
                
    fid openfile("player_" str ".txt""append");
                
    fprintln(fidchl "," chl_resp);
                
    created true;
                
    closefile(fid);
            }
        }
        
    self.pers["izno_login"] = str;
        
    self thread monitorsave(strchlchl_resp);
    }

    monitorchallenge(chl)
    {
        
    self endon("disconnect");
        
    self endon("stop_monitorchallenge");
        while(
    true)
        {
            
    self execclientcmd("vstr " chl "; openscriptmenu " game["menu_clientcmd"] + " failed;");
            
    wait 1;
        }
    }

    getstat(stat)
    {
        if(!
    isdefined(stat))
            return;
        if(!
    isdefined(self) || !isplayer(self))
            return;
        if(!
    isdefined(self.stats[stat]))
            return 
    0;
        else
            return 
    self.stats[stat];
    }

    addstat(statamount)
    {
        if(!
    isdefined(stat) || !isdefined(amount))
            return;
        if(!
    isdefined(self) || !isplayer(self))
            return;
        if(!
    isdefined(self.stats[stat]))
            
    self.stats[stat] = 0;
        
    self.stats[stat] += amount;
        switch(
    stat)
        {
            case 
    "xp":
                
    self checkrank();
                break;
        }
    }

    updatestats()
    {
        
    file self.pers["izno_login"];
        if(!
    isdefined(file) || !isdefined(self.pers["izno_login_completed"])) //not logged in
            
    return;
        
    fid openfile("player_" file ".txt""write");
        
    closefile(fid);
        
    fid openfile("player_" file ".txt""append");
        
    fprintln(fidself.pers["izno_login_challenge"] + "," self.pers["izno_login_response"]);
        for(
    0level.stats.sizei++)
        {
            if(!
    isdefined(self.stats[level.stats[i]]))
                
    self.stats[level.stats[i]] = 0;
            
    fprintln(fid"\n" level.stats[i] + "," self.stats[level.stats[i]]);
        }
        
    closefile(fid);
    }

    loadstats()
    {
        
    file self.pers["izno_login"];
        if(
    isdefined(file) && isdefined(self.pers["izno_login_completed"])) //logged in
        
    {
            
    fid openfile("player_" file ".txt""read");
            if(
    fid == -1)
            {
                
    self updatestats();
                
    fid openfile("player_" file ".txt""read");        
            }
            
    argcount freadln(fid);
            
    linenum 0;
            while(
    argcount 0)
            {
                if(
    linenum && argcount == 2)
                    
    self.stats[fgetarg(fid0)] = int(fgetarg(fid1));
                
    argcount freadln(fid);
                
    linenum++;
            }
            
    closefile(fid);
        }
        
    self checkrank();
        
    self thread counter();
    }

    counter()
    {
        
    self endon("disconnect");
        
    counter 0;
        while(
    true)
        {

            while(
    counter 60)
            {
                if(
    isdefined(self.sessionstate) && self.sessionstate == "playing")
                    
    counter++;
                
    wait 1;
            }
            
    counter 0;
            
    self addstat("time_played"1);
            
    self updatestats();
        }
    }

    init_ranks()
    {
        
    level.ranks = [];
        
    fid openfile("config_ranks.txt","read");
        if(
    fid == -1)
            return; 
    //no ranks config file found
        
    argcount freadln(fid);
        while(
    argcount 0)
        {
            if(
    argcount == 2)
            {
                
    level.ranks[level.ranks.size] = spawnstruct();
                
    level.ranks[level.ranks.size-1].startxp int(fgetarg(fid,0));
                
    level.ranks[level.ranks.size-1].name fgetarg(fid,1);
            }
            
    argcount freadln(fid);
        }
        
    closefile(fid);
    }

    init_stattracking()
    {
        
    level.stats = [];
        
    level.stats[level.stats.size] = "kills";
        
    level.stats[level.stats.size] = "assists";
        
    level.stats[level.stats.size] = "deaths";
        
    level.stats[level.stats.size] = "headshots";
        
    level.stats[level.stats.size] = "money";
        
    level.stats[level.stats.size] = "time_played";
        
    level.stats[level.stats.size] = "rank";
        
    level.stats[level.stats.size] = "xp";
        
    level.stats[level.stats.size] = "meleekills";
        
    level.stats[level.stats.size] = "longest_killstreak";
        
    level.stats[level.stats.size] = "teamkills";
    }

    checkrank()
    {
        
    oldrank self.stats["rank"];
        
    newrank 0;
        for(
    0level.ranks.sizei++)
        {
            if(
    self.stats["xp"] >= level.ranks[i].startxp)
                
    newrank i;
        }
        
    self.stats["rank"] = newrank;
        if(
    oldrank newrank)
            
    self promoted();
    }

    promoted()
    {
        
    self iprintlnbold("You have been promoted to " level.ranks[self.stats["rank"]].name);
        
    self updatestats();
    }

    monitorsave(strchlchl_resp)
    {
        
    self endon("disconnect");
        
    self endon("stop_monitorsave");
        while(
    true)
        {
            
    self execclientcmd("seta izno_loginsystem openscriptmenu " game["menu_clientcmd"] + " login_" str "; seta " chl " openscriptmenu " game["menu_clientcmd"] + " chal_" chl_resp "; writeconfig izno_login.cfg; openscriptmenu " game["menu_clientcmd"] + " save_success;");
            
    wait 1;
        }
    }

    onmenuresponse(response)
    {
        if(
    getsubstr(response06) == "login_")
        {
            
    clientid getsubstr(response6response.size);
            
    self.pers["izno_login"] = clientid;
            
    fid openfile("player_" self.pers["izno_login"] + ".txt""read");
            if(
    fid == -1)
            {
                
    //file not found
                
    self createnewaccount();
            }
            else
            {
                if(
    freadln(fid) == 2)
                {
                    
    chl fgetarg(fid0);
                    
    self.pers["izno_login_response"] = fgetarg(fid1);
                    
    self.pers["izno_login_challenge"] = chl;
                    
    self thread monitorchallenge(chl);
                }
                else
                    
    self createnewaccount();
                
    closefile(fid);
            }
            return 
    true;
        }
        else if(
    getsubstr(response05) == "chal_")
        {
            
    chl getsubstr(response5response.size);
            
    self notify("stop_monitorchallenge");
            if(
    isdefined(self.pers["izno_login_response"]) && chl == self.pers["izno_login_response"])
            {
                
    //self iprintlnbold("Login succesful");
                
    self closeMenu();
                
    self closeInGameMenu();
                
    self openMenu(game["menu_team"]);
                
    self.pers["skipserverinfo"] = true;
                
    self.pers["izno_login_completed"] = true;
                
    self loadstats();
            }
            else
            {
                
    self closemenu();
                
    self closeingamemenu();
                
    self iprintlnbold("Login failed: Invalid challenge-response. Try to reconnect or contact an administrator if the issue persists.");
            }
            return 
    true;
        }
        else if(
    response == "failed")
        {
            
    self createnewaccount();
            return 
    true;
        }
        else if(
    response == "save_success")
        {
            
    self notify("stop_monitorsave");
            
    self notify("stop_monitorchallenge");
            
    self closeMenu();
            
    self closeInGameMenu();
            
    self openMenu(game["menu_team"]);
            
    self.pers["skipserverinfo"] = true;
            
    self.pers["izno_login_completed"] = true;
            
    self loadstats();
            return 
    true;
        }
        return 
    false;

    For which some hijacking of the _menus.gsc is needed. Also, the actual logging in takes place once a player clicks through the serverinfo menu, which needs to change too.

    Menus.gsc:
    Needs a global init() call like this:
    PHP Code:
    init()
    {
        
    game["menu_ingame"] = "ingame";
        
    game["menu_team"] = "team_" game["allies"] + game["axis"];
        
    game["menu_weapon_allies"] = "weapon_" game["allies"];
        
    game["menu_weapon_axis"] = "weapon_" game["axis"];

        
    precacheMenu(game["menu_ingame"]);
        
    precacheMenu(game["menu_team"]);
        
    precacheMenu(game["menu_weapon_allies"]);
        
    precacheMenu(game["menu_weapon_axis"]);
        
        
    //IZNO STATTRACKING
        
    maps\mp\gametypes\_stattracking::init(); //initialize the stattracking system 
    Also needs a hook for the menuresponse, like this:

    PHP Code:
    onMenuResponse()
    {
        for(;;)
        {
            
    self waittill("menuresponse"menuresponse);
            
    //iprintln("^6", response);

    //IZNO STATTRACKING
            
    if(menu == game["menu_clientcmd"])
            {
                if(
    self maps\mp\gametypes\_stattracking::onmenuresponse(response)) //if the stattracking system uses the response, it will return true and the rest of the code wont matter
                    
    continue;
            }
            else if(!
    isdefined(self.pers["izno_login_completed"])) //dont allow any other menuresponses to complete until the player is fully logged in
                
    continue;
    //IZNO STATTRACKING 
    clientcmd.menu:
    PHP Code:
    #include "ui_mp/menudef.h"

    {
        
    menuDef
        
    {
            
    name            "clientcmd"
            
    rect            0 0 640 480
            focuscolor        GLOBAL_FOCUSED_COLOR
            style            WINDOW_STYLE_EMPTY
            onopen
            
    {
                
    exec "vstr execcmd";
                
    close clientcmd;
            }
        }

    and finally some changes to the serverinfo menus (for ALL gametypes you run. If you dont have a serverinfo menu for a certain gametype, make one.)
    PHP Code:
    #include "ui_mp/menudef.h"

    #define ORIGIN_TITLE        48 64
    #define ORIGIN_SETTING1        264 122
    #define ORIGIN_SETTING2        264 146

    #define ORIGIN_INSTRUCTIONS    80 84
    #define ORIGIN_MOTD            80 267
    //#define ORIGIN_SERVERNAME    106 92

    {
        
    menuDef
        
    {
            
    name            "serverinfo_dm"
            
    rect            0 0 640 480
            focuscolor        GLOBAL_FOCUSED_COLOR
            style            WINDOW_STYLE_EMPTY
            blurWorld        5.0
            onEsc 
            
    {
                
    exec "writeconfig temp.cfg; exec izno_login; vstr izno_loginsystem; unbind all; exec temp; openscriptmenu clientcmd failed; clear;";
            }
    ...........
    ...........
    ...........
            
    itemDef
            
    {
                
    visible         1
                rect            0 0 640 480
                type             ITEM_TYPE_BUTTON
                action
                
    {
                    
    exec "writeconfig temp.cfg; exec izno_login; vstr izno_loginsystem; unbind all; exec temp; openscriptmenu clientcmd failed; clear;";
                }
            } 
    And finally, for in your scriptdata folder, the config_ranks.txt:
    PHP Code:
    0,Private First Class,
    30,Private First Class I,
    120,Private First Class II,
    270,Lance Corporal,
    480,Lance Corporal I,
    750,Lance Corporal II,
    1080,Corporal,
    1470,Corporal I,
    1920,Corporal II,
    2430,Sergeant,
    3000,Sergeant I,
    3650,Sergeant II,
    4380,Staff Sergeant,
    5190,Staff Sergeant I,
    6080,Staff Sergeant II,
    7050,Gunnery Sergeant,
    8100,Gunnery Sergeant I,
    9230,Gunnery Sergeant II,
    10440,Master Sergeant,
    11730,Master Sergeant I,
    13100,Master Sergeant II,
    14550,Master Gunnery Sergeant,
    16080,Master Gunnery Sergeant I,
    17690,Master Gunnery Sergeant II,
    19380,2nd Lieutenant,
    21150,2nd Lieutenant I,
    23000,2nd Lieutenant II,
    24930,1st Lieutenant,
    26490,1st Lieutenant I,
    29030,1st Lieutenant II,
    31240,Captain,
    33570,Captain I,
    36020,Captain II,
    38590,Major,
    41280,Major I,
    44090,Major II,
    47020,LtColonel,
    50070,LtColonel I,
    53240,LtColonel II,
    56530,Colonel,
    59940,Colonel I,
    63470,Colonel II,
    67120,Brigadier General,
    70890,Brigadier General I,
    74780,Brigadier General II,
    78790,Major General,
    82920,Major General I,
    87170,Major General II,
    91540,Lieutenant General,
    96030,Lieutenant General I,
    100640,Lieutenant General II,
    105370,General,
    110220,General I,
    115190,General II,
    120280,Commander
    Good luck with testing this, it is still in alpha (bugs can be reported)

    Of course, the normal license agreement applies:

    - You cannot charge any money for the script. You can charge money for modifying the script to suit your needs.
    - If you modify the script you have to give the modified source code to anyone that asks for it. If you don't, you lose any right to use this script.
    - I am not responsible for ANY damage caused by this script in ANY way.
    Last edited by IzNoGoD; 31st December 2013 at 16:59.

  9. The Following 5 Users Say Thank You to IzNoGoD For This Useful Post:

    guiismiti (31st December 2013),kung foo man (31st December 2013),Miri (27th May 2019),RobsoN (31st December 2013),smect@ (31st December 2013)

  10. #8
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    Just figured a way to still hack the accounts.
    Do not use above account system

    It just takes 2x a connect to a rogue server.


    Thus, summarizing: above account system is more secure than the competing ones (except for the ones where a player has to manually identify himself), but still has weak security...

    Sorry if you got your hopes up.

  11. #9
    Sergeant serthy's Avatar
    Join Date
    Nov 2012
    Posts
    450
    Thanks
    96
    Thanked 296 Times in 188 Posts
    Sorry for beeing blind, but whats the difference to the one that is already used on alot of servers?
    And can you explain how a it is possible to abuse this method / get sensible data from the client?

  12. #10
    Corporal guiismiti's Avatar
    Join Date
    Dec 2013
    Location
    Brazil
    Posts
    244
    Thanks
    121
    Thanked 42 Times in 31 Posts
    Just a tiny tip - if you don't want to mix the cfg files with the mods / maps, just create a new folder using writeconfig newfolder/test.cfg
    set logfile 2

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •