[Tutorial] Persistent Variables Using DVARs

Topic created ยท 7 Posts ยท 190 Views
  • Introduction

    I've seen some confusion regarding custom DVARs and using them for things like verification systems. This is less of a tutorial and more of an information resource to clear up how they work and how to use them. You'll also need some knowledge on GSC and programming in general.

    Benefits of using custom DVARs

    • Custom DVARs are persistent between rounds, matches, entire games, etc. To my knowledge, they are only cleared manually or when the server restarts.

    Disadvantages of using custom DVARs

    • getDvar() is finicky with the data type it returns. For example, if you set a DVAR as integer 1, getting the DVAR may return string "1". There are solutions to this such as using getDvarInt() or getDvarFloat(), but the best way is to check the data type manually when necessary.
    • To clear a custom DVAR at the end of a match, you must write your own end-game monitor or use one of the game's callback functions.

    Custom Player DVARs

    setPlayerDvar(player, dvar, value) {
    	dvar = player getXUID() + "_" + dvar;
    	setDvar(dvar, value);
    }
    
    getPlayerDvar(player, dvar) {
    	dvar = player getXUID() + "_" + dvar;
    	
    	return getDvar(dvar);
    }
    
    playerDvarUndefined(player, dvar) {
    	dvar = player getXUID() + "_" + dvar;
    	result = getDvar(dvar);
    
    	return result == undefined || result == "";
    }
    

    Here are some example function that set and get DVARs using a player's XUID. I've also included a function to determine if a player DVAR is undefined / unset. While it's possible to change your XUID, it's less likely to happen compared to changing your name.

    This is not the same as setClientDvar(). It is just setting DVARs that incorporate a player's XUID to make it easier to retrieve them later.

    Persistent verification example

    #include maps\mp\_utility;
    #include common_scripts\utility;
    #include maps\mp\gametypes\_hud_util;
    #include maps\mp\gametypes\_hud_message;
    
    init() {
    	/*
    		You can add as many verification status' and XUIDs as you want. 
    		The only status that's hardcoded into this script is "unverified" in verifyOnConnect().
    	*/
    	addVerifiedXUID("verified", "1fa7d46291e005");
    	addVerifiedXUID("verified", "9fa7d45291ff05");
    	addVerifiedXUID("vip", "10101010101010");
    	addVerifiedXUID("mod", "99999999999999");
    	addVerifiedXUID("mod", "22222222222222");
    	addVerifiedXUID("admin", "12345678901234");
    	addVerifiedXUID("admin", "1947583bhf73nf8");
    	addVerifiedXUID("admin", "9f73bc8fh3jf");
    	addVerifiedXUID("admin", "jfhshdjvneucgw");
    	addVerifiedXUID("edgelord", "9hd2nfw8fhj3f");
    
    	level thread onPlayerConnect();
    }
    
    onPlayerConnect() {
    	for(;;) {
    		level waittill("connected", player);
    
    		player verifyOnConnect();
    
    		player thread onPlayerSpawned();
    	}
    }
    
    onPlayerSpawned() {
    	self endon("disconnect");
    	self endon("game_ended");
    	
    	for(;;) {
    		self waittill("spawned_player");
    		self iprintln("Verification: ^1" + self getVerificationDvar());
    	}
    }
    
    verifyOnConnect() {
        xuid_arr_entry = level.verified_xuids[self getXUID()]; // Get player's verif from their xuid in the array 
        if(xuid_arr_entry != undefined) { // Player XUID is in array 
            self setVerificationDvar(xuid_arr_entry); // Set the player's verification based on their xuid array entry 
        }
        else { // Player XUID is not in verification array 
            if(self verificationDvarUndefined()) { // Player's verification dvar has never been set before 
                self setVerificationDvar("unverified");
            }
        }
    }
    
    addVerifiedXUID(verif_level, xuid) {
    	if(!isDefined(level.verified_xuids)) {
    		level.verified_xuids = [];
    	}
    	level.verified_xuids[xuid] = verif_level;
    }
    
    setVerificationDvar(verif_level) {
        dvar = self getXUID() + "_verification";
        setDvar(dvar, verif_level);
    }
    
    getVerificationDvar() {
        dvar = self getXUID() + "_verification";
        return getDvar(dvar);
    }
    
    verificationDvarUndefined() {
        dvar = self getXUID() + "_verification";
        result = getDvar(dvar);
    
        return result == undefined || result == "";
    }
    

    Here's an example project that lets you add verified XUIDs using addVerifiedXUID() and then prints the player's verification status when they spawn. It also takes into account whether you give a player verification mid-match so you can add their XUID to the script later.

    How it works

    • Checks if the player's XUID is in the verified XUIDs array.
      • If it is, give the player the verification defined in the verified XUIDs array.
      • If it isn't, check if the player's verification DVAR has been set previously.
        • If it has, don't do anything and let the player keep their verification.
        • If it hasn't, set the player's verification to "unverified".
    • Print the player's verification to the player when they spawn.

    This is just an example to show how you can set persistent variables using DVARs. If you want to incorporate an actual DVAR-based verification system into your project, you'll have to write your own around your code.

    Additional info.

    • I recommend looking in the GSC dumps for functions like getDvarInt(), getDvarFloat(), setDvarBool(), setDvarInt(), etc. There's several of them that will make working with DVARs much easier.
    • Credits to Im_YouViolateMe for making me aware of this method years ago.
    • Let me know if there's anything I should add or explain better.
  • Great thread OP, very useful ๐Ÿ™‚

  • Good post!

  • @TheHiddenHour Very cool! Thank you! I was actually made aware of a verification system back in the COD 4 days when you could use setstat & getstat to verify/un-verify players in game. I was wondering if you think it may be possible. ๐Ÿ˜‰

    Kinda weird since nobody has tried it yet to my knowledge. Something like this can be done, and if it is and it never changes, that means it's more permanent than dvars. Verification for life pretty much. Unless you change the stat value somehow.

    diamond_club = self getdstat( "playerstatslist", "diamond_club", "StatValue" );
    if(diamond_club == "1337") self verifythisnigga(ver_level);
    

    I'm not very familiar with how stats are handled in this game, but the very barebones limits (again, thank you CraigChrist) point toward a lot of stats being limited in-game or else they're thrown out when the game ends. So i'm unsure with what stat(s) can be edited. In COD4, there were blank player stats that were left in the game that were never used. So it was simple to edit those and check for values. Because those stats were never used in the game, they never changed.

    [Stat Functions]

    player getdstat();
    player setdstat();
    player adddstat();
    player addweaponstat();
    player addbonuscardstat();
    player addplayerstat();
    player addplayerstatwithgametype();
    player addgametypestat();
    

    [Stat Limits]

    /*plevel and stats_version can't change
    rankxp can't increase by 65536 or decrease by 1
    kills and assists can't increase by 300 or decrease by 1
    deaths can't decrease
    headshots can't increase by 200 or decrease
    revives can't increase by 100 or decrease
    time played, wins and ties can't decrease
    hits and misses can't increase by 2500 or decrease
    total shots can't increase by 5000 or decrease
    rank can't increase by 20*/
    

    Functions like this could be used to set and grab values as well..

    set_d_stat(stat_name, value)
    {
    	self setdstat( "PlayerStatsList", stat_name, "StatValue", value );
    }
    get_d_stat( stat_name )
    {
    	return self getdstat( "PlayerStatsList", stat_name, "StatValue" );
    }
    

    I'm assuming it may be possible, given the globalstats table released by CraigChrist way back in 2014.

    There are some (i believe) stats that don't really change all that much, and that can be edited to a certain value for verification purposes or just for checks in general on spawn. But yeah. Would love your opinion ๐Ÿ˜‰

  • @Deicide I've hardly worked with the stat functions before but I don't see why one couldn't experiment with them in such a way. Perhaps you could write a thread similar to this one in which you discuss them more, I'd love to give it a read ๐Ÿ‘ . Also, thanks for the pastebin link. I haven't seen that one yet ๐Ÿ‘€ .

  • @TheHiddenHour said in [Tutorial] Persistent Variables Using DVARs:

    @Deicide I've hardly worked with the stat functions before but I don't see why one couldn't experiment with them in such a way. Perhaps you could write a thread similar to this one in which you discuss them more, I'd love to give it a read ๐Ÿ‘ . Also, thanks for the pastebin link. I haven't seen that one yet ๐Ÿ‘€ .

    I probably won't make any thread on this as much as i would like too. I just don't have the resources to test all of it. Hard to test things like assigning profile stats with bots. Lmao. But, i guess apparently a lot of people don't know about some of these structure's and stuff that have been released. So i guess ill put them in their own separate thread.

  • This post is deleted!
Log in to reply