• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Article] new era of save/load, deprecation of systems

Status
Not open for further replies.
Level 31
Joined
Jul 10, 2007
Messages
6,306
So, while I've been writing these masses of snippets for save/load, I came to understand something. By creating your own save/load system using a variety of snippets, you have greater control over the save/load process. Not only that, but with these snippets, that process becomes very simple. I created an entire save/load system in under 5 minutes.

By using snippets rather than a system, it allows for greater support. For example, a function for saving an inventory. It also allows for advanced features like those found in Encoder (saving item charges, hero specific gear, etc).


So do these snippets mark the deprecation of save/load systems?


The only system I can see still surviving is Encoder since it handles complex save/load more easily. However, you can still do anything Encoder can do using snippets.


Snippets allow you to easily save a single value or a set of small values or something really complex. Rather than being stuck with a huge system that is set up to do either the most complex things or the easiest things and is set up to do them a very specific way, you can decide how to do it yourself very easily with snippets.


Some such snippets that I've already written-
JASS:
function ColorCodeString takes string s, string numColor, string lowerColor, string upperColor, string specColor returns string
function AddRepeatedString takes string s, string str, integer spacing returns string
function RemoveString takes string s, string str, integer maxRemove returns string
struct NumberStack extends array
function SaveInventory takes NumberStack stack, unit whichUnit, integer itemCatalog returns nothing
function LoadInventory takes NumberStack stack, unit whichUnit, integer itemCatalog returns nothing
function SaveItemCharges takes NumberStack stack, unit whichUnit, Table itemMaxChargeTable, Table itemPerishableTable returns nothing
function LoadItemCharges takes NumberStack stack, unit whichUnit, Table itemMaxChargeTable, Table itemPerishableTable returns nothing
function EncryptNumber takes BigInt number, integer security, integer shuffles, integer forPlayerId, string playerSalt, real checksumVariance returns string
function DecryptNumber takes string c, Base base, integer security, integer shuffles, integer forPlayerId, string playerSalt, real checksumVariance returns BigInt
function SaveHeroXP takes NumberStack stack, unit whichUnit returns nothing
function LoadHeroXP takes NumberStack stack, unit whichUnit returns nothing


All of these snippets do Encoder quality save/load. The only thing that sometimes doesn't do as well as Encoder are the Encrypt/Decrypt functions. The biggest value should always be first in a save/load code. Encoder knows whether the biggest value will be the checksum or the first value in the save/load. EncryptNumber and DecryptNumber don't know that, so they will always end up placing the Checksum at the back of the number (multiply it in rather than add it in at the front). This means that in some cases, Encoder will produce smaller codes.


Encoder quality code means conditional save/load. SaveHeroXP for example will only save xp if the hero isn't at the max level. The SaveInventory function can save partial inventories (rather than saving 6 empty slots, it'll only save 1) just like Encoder.


What I've found is that using these snippets seems very easy. This is a demonstration of a load method-
JASS:
    private static method load takes nothing returns boolean
        local string s=GetEventPlayerChatString()       //retrieve string
        local NumberStack stack
        
        set s=RemoveString(s,GetEventPlayerChatStringMatched(),1)       //remove "-load "
        set s=RemoveString(s," ",0)                                     //remove " "
        set s=RemoveString(s,"-",0)                                     //remove "-"
        
        if (IsStringLengthInRange(s,5,9)) then
            //retrieve stack of numbers
            set stack = DecryptNumber(s,encryptionKey,1000000,3,GetPlayerId(GetTriggerPlayer()),"salt value",.85)
            
            //if stack isn't 0, code is valid
            if (0!=stack) then
                //display values stored in stack (reverse order)
                call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Loaded: "+I2S(stack.pop(31))+","+I2S(stack.pop(64)))
            else
                call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Invalid Code")
            endif
        else
            call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Invalid Code")
        endif
        return false
    endmethod

Creating the entire load portion of a save/load system took me only a minute using these snippets. The save portion is just as easy.

JASS:
    private static method save takes nothing returns boolean
        local NumberStack stack = NumberStack.create(encryptionKey)     //create a number stack
        local string encrypted
        
        //push numbers on to stack
        call stack.push(10,64)      //10
        call stack.push(20,31)      //20,10
        
        //display numbers in stack
        call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Saving: 10,20")
        
        //encrypt number as a string
        set encrypted=EncryptNumber(stack,1000000,3,GetPlayerId(GetTriggerPlayer()),"salt value",.85)
        
        //add dashes
        set encrypted=AddRepeatedString(encrypted,"-",4)
        
        //color
        set encrypted=ColorCodeString(encrypted, "40e0d0", "ff69b4", "00AA00", "ffff00")
        
        //display code
        call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,encrypted)
        
        //destroy stack
        call stack.destroy()
        
        return false
    endmethod


Are save/load systems now deprecated?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, as I look at this more, it seems like Encoder is deprecated by this new style as well (sad news for me since I spent so long on it).

I was recently trying to create a catalog that supported save/load of item charges and hero specific items (I was going to later expand it to specific inventory slots, like a slot that could just hold boots). What I discovered is that it couldn't be done in Encoder. Well, it would be done, it was just really ugly... and if I were to add the slots to it, it'd get even uglier.

Using the scripts, this is cake (super, super easy). Using Encoder, it's a nightmare. It's 100% impossible on any save/load system other than Encoder.


I think all save/load systems are now 100% deprecated.


The only issue left is a good way to rebuild save/load codes to move the checksum to the front if it's bigger than the biggest value = ).
 
Status
Not open for further replies.
Top