System The Shortest Save/Load Code Ever

Korolen

New User (Why do I keep getting those red bars?)
Kode:
the shortest save/load code ever
Version 0.2.1

1: Introduction
1.1: What is Kode
Kode is a system that allows you to store information - such as a hero, or a players wins and losses - in a code. The player writes down his code, and the next time he plays your map, he can type in that code and restore that information. In an RPG, this can create for a persistent universe. In a mini-game, it can store a players record, and in a TD, it can unlock special towers.

Passing information between games can greatly enhance your map. This is why many save/load systems have been created. However, many of these code systems produce very long codes that the player has to write down. Also, codes can often be changed or loaded by someone besides their proper owner. I designed Kode with both code length and security in mind, among other things, and I hope you will find it useful.​

2: Installation
2.1: Download
If you do not know JASS, download the GUI-Friendly version. If you would like to use JASS, then download the normal version. Both can be found attached at the bottom of this post.

Now, open the map file in the World Editor, along with your map.​

2.2: Transfer
In the Trigger Editor, click on the very top item on the left (it will be called "Kode.w3x" or "Kode (GUI-Friendly).w3x"). Select everything on the right, and copy it to the clipboard (Ctrl+C). Select the group named "Kode" on the left, and copy that to the clipboard, also (Another Ctrl+C). Then, switch to your map.

Make sure the option "automatically create variables when pasting triggers" is checked in your preferences, and paste the group into your map (Ctrl+V). Then delete the trigger called "GUI Variables". Select the map header, which is in the same spot as the "Kode.w3x" was when you were copying all the text. On the right, scroll to the very bottom and click on the last line, and then paste in the text you copied (Ctrl+V).

That's it, you should be able to save your map. However, there is one last thing you need to do to, and that is configuring Kode.​

2.3: Configure
Go to the Variable Editor, and select "Kode_Charmap". Change the string of letters there to your desired character map (The characters that are in the charmap will appear in the code that Kode generates. For example, if your charmap is "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", then all the letters and numbers will make up your code. Case-insensitivity is somewhat kludged in by simply having a hard-coded map it checks against as it goes along this configured one. All you need to know is if you want your code to be case-insensitive, make sure it starts with the uppercase alphabet, and if you want it to be case-sensitive, make it start with the lowercase alphabet followed by the upper-case one (in that order).)

Change "Kode_Encryption_Strength" to 5. The higher this number is, the longer it will take to generate a code, but the more secure the code will be. Generally, setting it to more then 5 is unnecessary, unless your code can be very long.​

3: Usage
3.1: Saving data
Prepare the workspace by "call clear_bits()", or by executing the "Kode Prepare Workspace" trigger if your using GUI. Then, for each number you want to save, call "push_int(<the number>, <maximum the number can be>)". If your using GUI, set the variable "Kode_Integer" to your number, and "Kode_Integer_Max" to the maximum the number will ever be, and then execute "Kode Add Integer to Code". Note that the number ranges from zero to one less then the maximum. If the number is equal to or greater the maximum, your code won't work when loading.

Another option for saving large numbers is the "push_progressive_int" function ("Kode Add Progressive Integer to Code" in GUI). For example, say you're saving the players gold. Most of the time, they will have spent most of it and it will be only a few hundred. Sometimes, however, they will be saving up for something, and it will be near a million. Normally, you would save their money with a maximum of a million. This would waste a lot of space in the code, though, if their gold were only 10. Thus, the "progressive int" allows the number to be very big, but saves space if it is small.

When saving a progressive integer, you must specify the number to be saved, the base, and the maximum exponent. For the gold example, you might specify 10 as the base, and 6 as the maximum exponent. Thus, the maximum value that it could be would be 10^6, or 1,000,000. In GUI, Kode_PInteger_Base and Kode_PInteger_Max_Exponent are the base and maximum exponent, respectively.

Also, if there is no maximum value that the number can be, you can use an "expanding int", which is a "null-terminated int". These are actually bigger in size then I had expected, so it is recommended that you just use a large progressive int.

Finally, if you have multiple things all of the same range, where their order doesn't matter - a hero's inventory, for example (although I'm to lazy to upgrade the hero example to work with this, especially because it would require some modification of Kode to get the charges working) - then you can use a unordered list. This works exactly like adding the numbers to the code one by one, except for two things. First, the numbers will probably be loaded in a different order. Second, the code will be shorter. I'd say that's a pretty small price to pay.

To use an unordered list, set the values of the global variable Kode_UL, starting with 0. For example, if you were saving 82, 16, and 64, set Kode_UL[0] = 82, Kode_UL[1] = 16, and Kode_UL[2] = 64. Once Kode_UL has been filled with the values you are adding to the code, call push_unordered_list, passing the number of values in Kode_UL (in this case 3) as the first argument, and the maximum value the numbers can be (both these values must be the same when loading and saving). In the GUI version, set Kode_Unordered_List_Length to be the length, and Kode_Unordered_List_Max to be the max, then execute "Kode Add Unordered List to Code".

Once all your data has been added to the code, call "create_code", passing the player the code is for (null if it can work for everyone). The returned value is the code. You may want to in turn pass that to format_code. In GUI, Kode_Owner is the player the code is for (set it to Matching Player if the code is for everyone), and Kode_Code will be set to the code when "Kode Create Code" is executed. Execute "Kode Format Code" with Kode_Dash_Spacing set to make the code pretty.​

3.2: Loading data
When the player types in their code, call "load_code", passing the player and what they typed in as arguments. In GUI, Kode_Owner is the player and Kode_Code is what they typed in, and "Kode Load Code" is the trigger.

Now that the raw data of the code has been loaded, you must retrieve the numbers you added to the code before. Call "pop_int", passing the maximum value the number can be as the argument; set Kode_Integer_Max, execute "Kode Get Integer from Code", and retrieve the integer in Kode_Integer in GUI. You MUST retrieve the numbers in reverse order they were put onto the code, and the maximum values must match exactly what they in the saving. For example, if you store a hero level, then the player's gold and lumber, you would pop off the lumber, then the gold, and then the hero level.

For progressive ints, call pop_progressive_int or execute "Kode Get Progressive Int from Code". Unordered lists can be accessed with pop_unordered_list, which will set the values of Kode_UL. Execute "Kode Get Unordered List from Code" in GUI.​

3.3: Examples
These are the examples found in the test maps.

3.3.1: Arbitrary Number.

The player types in "-savenum ###", and the number is saved into a code. Any player can load a code, and there is a low amount of security, so that if the player messes up their code, it won't display a random value.

We create a trigger with the event "Player - Player 1 (Red) types a chat message containing -savenum as A substring" and the condition "(Substring((Entered chat string), 1, 6)) Equal to -savenum ". The actions will then fire when the player types something that starts with "-savenum ".

The first thing we do is prepare the workspace by calling "clear_bits()", or by executing "Kode Prepare Workspace" in GUI. Then, we add the number the user typed in to the code. Because the number can be big or small, we will use a progressive int. The number will go up to 10 million - 10x10x10x10x10x10x10, or 10^7 - so the base could be 10 and the maximum exponent 7. In JASS, we call "push_progressive_int(S2I(SubString(GetEventPlayerChatString(), 9, 999)), 10, 7)", or, in GUI, we set Kode_Integer to "Integer((Substring((Entered chat string), 10, 999)))", Kode_PInteger_Base to 10, Kode_PInteger_Max_Exponent to 7, and execute "Kode Add Progressive Integer to Code".

That's the only thing that's going to be added to the code, so it's time to create the code and display it to the player. In JASS, we can do it with "call BJDebugMsg("The Code: " + format_code(create_code(null, 5), 4))". In GUI, it will be a few more lines. First, we set Kode_Owner to (Matching Player) to specify that the code will work for all players, and we execute "Kode Create Code". Then, we set Kode_Dash_Spacing to 4, and execute "Kode Format Code". The variable Kode_Code now stores a nicely formatted code that we can display to the player.

Now, we create a second trigger for loading. The event and condition are almost the same. We call "load_code", passing the player, security, and the code the player typed in as arguments (Kode_Owner, Kode_Security, and Kode_Code, respectively, with an execution of "Kode Load Code" in GUI). This function returns (sets Kode_Is_Valid in GUI) whether the code is a valid one or not. If the returned value isn't valid, we tell the user and skip remaining actions. Now, the raw data that was in the code has been loaded; we just need to pop the number off the data.

We call "pop_progressive_int", passing 10 and 7 - the same numbers that we used when saving the number - and the number that was pushed on before is returned ("Kode Get Progressive Integer From Code", taking Kode_PInteger_Base and Kode_PInteger_Max_Exponent, and setting Kode_Integer). We can then display that number to the player.


The finished triggers looks like this:

JASS
Code:
 Save Number
    Events
        Player - Player 1 (Red) types a chat message containing -savenum  as A substring
    Conditions
        (Substring((Entered chat string), 1, 9)) Equal to -savenum 
    Actions
        Custom script:   call clear_bits()
        Custom script:   call push_progressive_int(S2I(SubString(GetEventPlayerChatString(), 9, 999)), 36, 6)
        Custom script:   call BJDebugMsg(format_code(create_code(null, 5), 3))
Code:
 Load Number
    Events
        Player - Player 1 (Red) types a chat message containing -loadnum  as A substring
    Conditions
        (Substring((Entered chat string), 1, 9)) Equal to -loadnum 
    Actions
        Custom script:   if not load_code(null, 5, SubString(GetEventPlayerChatString(), 9, 999)) then
        Game - Display to (All players) the text: |cffff0000Invalid C...
        Skip remaining actions
        Custom script:   endif
        Custom script:   call BJDebugMsg("|cff00ff00The Value: |r" + I2S(pop_progressive_int(36, 6)))
GUI
Code:
 Save Number
    Events
        Player - Player 1 (Red) types a chat message containing -savenum  as A substring
    Conditions
        (Substring((Entered chat string), 1, 9)) Equal to -savenum 
    Actions
        Trigger - Run Kode Initialize Workspace <gen> (ignoring conditions)
        Set Kode_Integer = (Integer((Substring((Entered chat string), 10, 999))))
        Set Kode_PInteger_Base = 36
        Set Kode_PInteger_Max_Exponent = 6
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_Owner = (Matching player)
        Set Kode_Security = 5
        Trigger - Run Kode Create Code <gen> (ignoring conditions)
        Set Kode_Dash_Spacing = 3
        Trigger - Run Kode Format Code <gen> (ignoring conditions)
        Game - Display to (All players) for 60.00 seconds the text: Kode_Code
Code:
 Load Number
    Events
        Player - Player 1 (Red) types a chat message containing -loadnum  as A substring
    Conditions
        (Substring((Entered chat string), 1, 9)) Equal to -loadnum 
    Actions
        Set Kode_Code = (Substring((Entered chat string), 10, 999))
        Set Kode_Owner = (Matching player)
        Set Kode_Security = 5
        Trigger - Run Kode Load Code <gen> (ignoring conditions)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                Kode_Is_Valid Equal to False
            Then - Actions
                Game - Display to (All players) the text: |cffff0000Invalid C...
                Skip remaining actions
            Else - Actions
        Set Kode_PInteger_Base = 36
        Set Kode_PInteger_Max_Exponent = 6
        Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
        Game - Display to (All players) the text: (|cff00ff00The Value: |r + (String(Kode_Integer)))
3.3.2: Stats

Save the kills, damage dealt to the nearest hundred, and mana used by the player to the nearest ten into a code.

Most of the triggering is the same as in the previous example. We have a trigger to save that fires upon "-savestats" as an exact match, which calls "push_progressive_int" three times. When saving, the damage is divided by 100 and the mana by 10 to lower code size. When the loading happens, these values will be multiplied by 100 and 10 to restore their proper figures.

These are the triggers:

JASS
Code:
 Save Stats
    Events
        Player - Player 1 (Red) types a chat message containing -savestats as An exact match
    Conditions
    Actions
        Custom script:   call clear_bits()
        Custom script:   call push_progressive_int(udg_Demo_Kills, 5, 4)
        Custom script:   call push_progressive_int(R2I(udg_Demo_Damage / 100), 10, 4)
        Custom script:   call push_progressive_int(R2I(udg_Demo_Mana / 10), 10, 5)
        Custom script:   call BJDebugMsg(format_code(create_code(GetTriggerPlayer(), 7), 3))
Code:
 Load Stats
    Events
        Player - Player 1 (Red) types a chat message containing -loadstats  as A substring
    Conditions
        (Substring((Entered chat string), 1, 11)) Equal to -loadstats 
    Actions
        Custom script:   if not load_code(GetTriggerPlayer(), 7, SubString(GetEventPlayerChatString(), 11, 999)) then
        Game - Display to (All players) the text: |cffff0000Invalid C...
        Skip remaining actions
        Custom script:   endif
        Custom script:   set udg_Demo_Mana = I2R(pop_progressive_int(10, 5) * 10) + .99
        Custom script:   set udg_Demo_Damage = I2R(pop_progressive_int(10, 4) * 100)
        Custom script:   set udg_Demo_Kills = pop_progressive_int(5, 4)
        Multiboard - Set the text for (Last created multiboard) item in column 2, row 1 to (String(Demo_Kills))
        Multiboard - Set the text for (Last created multiboard) item in column 2, row 2 to (String((Integer(Demo_Damage))))
        Multiboard - Set the text for (Last created multiboard) item in column 2, row 3 to (String((Integer(Demo_Mana))))
        Custom script:   call BJDebugMsg("|cff00ff00Code Loaded Succesfully.|r")
GUI
Code:
 Save Stats
    Events
        Player - Player 1 (Red) types a chat message containing -savestats as An exact match
    Conditions
    Actions
        Trigger - Run Kode Initialize Workspace <gen> (ignoring conditions)
        Set Kode_Integer = Demo_Kills
        Set Kode_PInteger_Base = 5
        Set Kode_PInteger_Max_Exponent = 4
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_Integer = (Integer((Demo_Damage / 100.00)))
        Set Kode_PInteger_Base = 10
        Set Kode_PInteger_Max_Exponent = 4
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_PInteger_Max_Exponent = 5
        Set Kode_Integer = (Integer((Demo_Mana / 10.00)))
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_Owner = (Triggering player)
        Set Kode_Security = 7
        Trigger - Run Kode Create Code <gen> (ignoring conditions)
        Set Kode_Dash_Spacing = 3
        Trigger - Run Kode Format Code <gen> (ignoring conditions)
        Game - Display to (All players) for 60.00 seconds the text: Kode_Code
Save Stats
    Events
        Player - Player 1 (Red) types a chat message containing -savestats as An exact match
    Conditions
    Actions
        Trigger - Run Kode Initialize Workspace <gen> (ignoring conditions)
        Set Kode_Integer = Demo_Kills
        Set Kode_PInteger_Base = 5
        Set Kode_PInteger_Max_Exponent = 4
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_Integer = (Integer((Demo_Damage / 100.00)))
        Set Kode_PInteger_Base = 10
        Set Kode_PInteger_Max_Exponent = 4
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_PInteger_Max_Exponent = 5
        Set Kode_Integer = (Integer((Demo_Mana / 10.00)))
        Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
        Set Kode_Owner = (Triggering player)
        Set Kode_Security = 7
        Trigger - Run Kode Create Code <gen> (ignoring conditions)
        Set Kode_Dash_Spacing = 3
        Trigger - Run Kode Format Code <gen> (ignoring conditions)
        Game - Display to (All players) for 60.00 seconds the text: Kode_Code
Code:
 Load Stats
    Events
        Player - Player 1 (Red) types a chat message containing -loadstats  as A substring
    Conditions
        (Substring((Entered chat string), 1, 11)) Equal to -loadstats 
    Actions
        Set Kode_Code = (Substring((Entered chat string), 12, 999))
        Set Kode_Owner = (Triggering player)
        Set Kode_Security = 7
        Trigger - Run Kode Load Code <gen> (ignoring conditions)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                Kode_Is_Valid Equal to False
            Then - Actions
                Game - Display to (All players) the text: |cffff0000Invalid C...
                Skip remaining actions
            Else - Actions
        Set Kode_PInteger_Base = 10
        Set Kode_PInteger_Max_Exponent = 5
        Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
        Set Demo_Mana = ((Real((Kode_Integer x 10))) + 0.99)
        Set Kode_PInteger_Max_Exponent = 4
        Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
        Set Demo_Damage = (Real((Kode_Integer x 100)))
        Set Kode_PInteger_Base = 5
        Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
        Set Demo_Kills = Kode_Integer
        Multiboard - Set the text for (Last created multiboard) item in column 2, row 1 to (String(Demo_Kills))
        Multiboard - Set the text for (Last created multiboard) item in column 2, row 2 to (String((Integer(Demo_Damage))))
        Multiboard - Set the text for (Last created multiboard) item in column 2, row 3 to (String((Integer(Demo_Mana))))
        Game - Display to (All players) the text: |cff00ff00Code Load...
3.3.3: Hero

Save all the data of a hero, including experience, attributes, skills, and items, into a secure, player-specific code.

NOTE: This demo could easily be done in both GUI and JASS, but I prefer to use JASS and creating both a JASS version and a GUI version would take up too much of my time. Therefore, this demo is written in JASS only.

These are the things that will need to go into the code:
  • Type of hero (Archmage, Mountain King, etc.)
  • Hero experience
  • Level of each ability
  • Each attribute
  • Items
For the type of hero, we will have an array with each hero type ['Hamg', 'Hblm', 'Hmkg', 'Hpal'], and loop through it until we find the index of the hero type we are saving. This function will look like this:
JASS:
 function get_hero_type_index takes integer t returns integer
    local integer i = 0
    loop
        exitwhen i &gt;= 4
        if udg_Demo_Hero_Types<i> == t then
            return i
        endif
        set i = i + 1
    endloop
    return -1
endfunction</i>
The experience will simply be a progressive integer. Because the maximum experience for a level 10 hero is 5400, 9^4 will cover it nicely. For the abilities, we will have another array, which holds the abilities each hero can have. The forth one will always be the ultimate. We loop through each one and push the ability's level. Attributes are easy, just push the number. If they could go very high, a progressive int might be a good idea. For the items, we will have an array with each item ID stored it it, and loop through to find the item's index. This is the code:
JASS:
 function get_item_type_index takes integer t returns integer
    local integer i = 0
    loop
        exitwhen i &gt;= udg_Demo_Item_Types_Length
        if udg_Demo_Item_Types<i> == t then
            return i
        endif
        set i = i + 1
    endloop
    return -1
endfunction</i>
For each item, if it has charges, we save that, also. Then, we save how many items the hero had.

The final code:
JASS:
 function get_hero_type_index takes integer t returns integer
    local integer i = 0
    loop
        exitwhen i &gt;= 4
        if udg_Demo_Hero_Types<i> == t then
            return i
        endif
        set i = i + 1
    endloop
    return -1
endfunction

function get_item_type_index takes integer t returns integer
    local integer i = 0
    loop
        exitwhen i &gt;= udg_Demo_Item_Types_Length
        if udg_Demo_Item_Types<i> == t then
            return i
        endif
        set i = i + 1
    endloop
    return -1
endfunction

function save_hero takes unit u returns nothing
    local integer i = 0
    local integer item_count = 0
    local item m
    local integer t = get_hero_type_index(GetUnitTypeId(u))
    
    call clear_bits()
    
    set i = 0
    loop
        exitwhen i &gt;= 6
        set m = UnitItemInSlot(u, i)
        if GetItemCharges(m) != 0 then
            call push_progressive_int(GetItemCharges(m), 4, 3)
        endif
        if m != null then
            call push_int(get_item_type_index(GetItemTypeId(m)), udg_Demo_Item_Types_Length)
            set item_count = item_count + 1
        endif
        set i = i + 1
    endloop
    call push_int(item_count, 6)
    
    set i = 0
    loop
        exitwhen i &gt;= 3
        call push_int(GetHeroStatBJ(i, u, false), 60)
        set i = i + 1
    endloop
    
    set i = 0
    loop
        exitwhen i &gt;= 3
        call push_int(GetUnitAbilityLevel(u, udg_Demo_Hero_Abilities[t * 10 + i]), 4)
        set i = i + 1
    endloop
    call push_int(GetUnitAbilityLevel(u, udg_Demo_Hero_Abilities[t * 10 + 3]), 2)
    
    call push_progressive_int(GetHeroXP(u), 9, 4)
    call push_int(t, 4)
endfunction

function Trig_Save_Hero_Actions takes nothing returns nothing
    local group g = GetUnitsSelectedAll(Player(0))
    local unit u
    if CountUnitsInGroup(g) == 1 then
        set u = FirstOfGroup(g)
        if IsUnitType(u, UNIT_TYPE_HERO) then
            call save_hero(u)
            call RemoveUnit(u)
            call BJDebugMsg(format_code(create_code(GetTriggerPlayer(), 10), 4))
        endif
        set u = null
    endif
    call DestroyGroup(g)
    set g = null
endfunction

function InitTrig_Save_Hero takes nothing returns nothing
    set gg_trg_Save_Hero = CreateTrigger()
    call TriggerRegisterPlayerChatEvent(gg_trg_Save_Hero, Player(0), &quot;-savehero&quot;, true)
    call TriggerAddAction(gg_trg_Save_Hero, function Trig_Save_Hero_Actions)
endfunction</i></i>
JASS:
 function load_ability takes unit u, integer a, integer l returns nothing
    if l != 0 then
        call SelectHeroSkill(u, a)
        call SetUnitAbilityLevel(u, a, l)
        call UnitModifySkillPoints(u, -(l - 1))
    endif
endfunction

function load_hero takes player p, real x, real y returns unit
    local integer i = 0
    local integer item_count
    local item m
    local integer t = pop_int(4)
    local unit u = CreateUnit(p, udg_Demo_Hero_Types[t], x, y, 0)
    
    call SetHeroXP(u, pop_progressive_int(9, 4), false)    
    call BJDebugMsg(&quot;XP: &quot; + I2S(GetHeroXP(u)))
    
    call load_ability(u, udg_Demo_Hero_Abilities[t * 10 + 3], pop_int(2))
    set i = 0
    loop
        exitwhen i &gt;= 3
        call load_ability(u, udg_Demo_Hero_Abilities[t * 10 + 2 - i], pop_int(4))
        set i = i + 1
    endloop
    
    set i = 0
    loop
        exitwhen i &gt;= 3
        call SetHeroStat(u, 2 - i, pop_int(60))
        set i = i + 1
    endloop
    
    set item_count = pop_int(6)
    set i = 0
    loop
        exitwhen i &gt;= item_count
        set bj_forLoopAIndex = pop_int(udg_Demo_Item_Types_Length)
        set m = UnitAddItemById(u, udg_Demo_Item_Types[bj_forLoopAIndex])
        if GetItemCharges(m) != 0 then
            call SetItemCharges(m, pop_progressive_int(4, 3))
        endif
        set i = i + 1
    endloop
    
    return u
endfunction

function Trig_Load_Hero_Conds takes nothing returns boolean
    return SubString(GetEventPlayerChatString(), 0, StringLength(&quot;-loadhero &quot;)) == &quot;-loadhero &quot;
endfunction

function Trig_Load_Hero_Actions takes nothing returns nothing
    if not load_code(GetTriggerPlayer(), 10, SubString(GetEventPlayerChatString(), StringLength(&quot;-loadhero &quot;), 999)) then
        call BJDebugMsg(&quot;|cffff0000Invalid Code.|r&quot;)
        return
    endif
    call load_hero(GetTriggerPlayer(), 0, 0)
    call BJDebugMsg(&quot;|cff00ff00Code Loaded Succesfully.|r&quot;)
endfunction

function InitTrig_Load_Hero takes nothing returns nothing
    set gg_trg_Load_Hero = CreateTrigger()
    call TriggerRegisterPlayerChatEvent(gg_trg_Load_Hero, Player(0), &quot;-loadhero &quot;, false)
    call TriggerAddCondition(gg_trg_Load_Hero, Condition(function Trig_Load_Hero_Conds))
    call TriggerAddAction(gg_trg_Load_Hero, function Trig_Load_Hero_Actions)
endfunction


Final Notes

I know that the unordered list push/pop functions spit out a bunch of debugging messages, sorry about that. Just remove the lines with "BJDebugMsg" at the beginning of them if you plan to use unordered lists.

No, I am not going to make the hero demo in GUI any time soon. Read this document through and make one yourself.

If you use Kode in your map, please:
  1. Mention that you are using Kode in your map.
  2. Send me a message, as I'd like to know where Kode is being used.

Constructive criticism is more then welcome, along with bugs and suggestions. If you need help, please be specific.

I hope people find this useful!

-Korolen

[Note: 0.2.1 is the latest version; the unnamed ones are 0.1.0. GUI-Friendly 0.2.1 had a bug, and 0.2.1b has the fix.]
 

Attachments

Seannny

Why GUI when you can Jass?
my friend Elm St Fred right now made a working save load in my map that uses only 4 triggers...is that smaller than this?
 

Korolen

New User (Why do I keep getting those red bars?)
my friend Elm St Fred right now made a working save load in my map that uses only 4 triggers...is that smaller than this?
Well, I don't know how his code works. It probably just concatenates the values, which has three major disadvantages from more sophisticated systems like Kode:
  1. The codes will be long.
  2. The codes can be changed by the player.
  3. The code (may) be able to be loaded by someone besides the proper owner.
 

Korolen

New User (Why do I keep getting those red bars?)
hmm

Base74. ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_~?+")

The hash should include more maps.
("abcdefghijklmnopqrstuvwxyz1234567890_()")

Short and works.
GUI won't do shorter than this.
The default character map includes case-sensitiveness with "special" characters too; I don't see why not, as your going to be writing it down anyway, but if the user doesnt want case-sensitive they can just remove the lower-case a-z, and if they don't want the other characters, just strip them off the end... Kode will automaticly adjust.

What do you mean "the hash should include more maps"? Do you mean there are more characters that a username can have that are not included in the default map? BTW, I'm aware that it won't detect upper-case letters in the player's name; I'm going to fix this in the next version.

The Helper said:
I am moving it.
OK, thank you for correcting this.
 

elmstfreddie

The Finglonger
The code I made uses... Uhm... Well, it's shortish. For lvls and hero type and inventory slots it uses... 14 characters? Anyways, it's not 4 triggers it's 3. ANYHOW, it's all in GUI, and was pretty easy to make. I guess you couldn't really hack it unless you're like me and actually made it. Well, of course you could =/. So how many characters is yours exactly (I gotta go can't test sorry!)?
 

Korolen

New User (Why do I keep getting those red bars?)
So how many characters is yours exactly (I gotta go can't test sorry!)?
It depends on how much data you store in the code and how high security you want. For a simple hero this is the data that would be stored (value: maximum)

Hero Type: 10
Hero Level: 10
Hero Abilities: 4x3 and 2 (three normals and an ultimate)
Hero Inventory: 6 slots, each with 100 possible items, plus up to 100 charges on charged items

Thats 10*10*3*3*3*2*(100*100(if charged))**6 = 12800000000000000.

Represented in a code, that's about 9 characters, assuming their inventory if 100% full with uncharged items. (3 characters if their inventory is empty; 15 characters if their inventory is filled with items with 100 charges) These numbers would vary greatly depending on number of items in the game, maximum level, security, rounding of exp, etc. It could be much shorter or a bit longer depending on these many factors (it's rather pessimistic right now as far as code length).

EDIT: Oops, you probably would want to store hero experience, not their level. That might increase the code size slightly.

EDIT2: Probably would want to store money, also. Again, this would bump code size a bit.
 

elmstfreddie

The Finglonger
Cool.
Mine doesn't do anything sophisticated, I just made up a bunch of random 2 character codes for each item n stuff. That's why it's only 3 triggers lol.
No, saving levels is fine.
And yeah, I need to save money.

(checking the map out now)
Edit > WTF where is my TFT cd?
Edit > Found it =/

Ok, well the save/code looks fine, but I have no clue how to edit it. Could you add some comments or something?
I might still not be able to edit it though since I dunno JASS =/
 

Chocobo

White-Flower
What do you mean "the hash should include more maps"? Do you mean there are more characters that a username can have that are not included in the default map? BTW, I'm aware that it won't detect upper-case letters in the player's name; I'm going to fix this in the next version.
You wrote what I was going to write. (I meant case)
 

Korolen

New User (Why do I keep getting those red bars?)
*bump*

I fixed the bug of upper-case letters in player names, and created a "GUI-Friendly" version that does not require any knowledge of JASS.

bump

I added more FAQ questions, including a mini-tutorial for finding how long the code needs to be and how many bits are required.

I also submitted this to Free Trigger Code.

*major update*

0.2.0:
  • Increased security.
  • Added the "progressive int", which can dramatically reduce code length.
  • Functions are more streamlined, and the GUI-Friendly version is even more friendly.
  • Code size is no longer fixed, but automatically increases with amount of data.
  • Validity check now happens when the code is loaded.
  • Completely rewrote documentation.
  • Added better examples, including a Hero saver.
 
R

Ricky

Guest
Hi

Hi~Ricky

Hi. First off is this a tutorial?

1. I would appreciate it if you went through it with a spell checker

2. I have seen 3 shorter codes in this website

3. The layout of this Tutorial/Thing could have been better

4. If this is a tutorial shouldn't it be in tutorial submission?

5. The updates make it a lot better

6. Sorry for this but i just had to mention these things. I mean no offense I'm
just pointing things out
 

Chocobo

White-Flower
>1. I would appreciate it if you went through it with a spell checker
What don't you like in?

>2. I have seen 3 shorter codes in this website
Write them? It is (currently) imposible without compression algorithms to get smaller than this for the moment. (because it uses all the values possible between 1 and X, X=Amount of possibility in factors)

>4. If this is a tutorial shouldn't it be in tutorial submission?
Read the thread?
 

Korolen

New User (Why do I keep getting those red bars?)
> Hi. First off is this a tutorial?
No, it's a save/load code system. The first post is documentation. I suppose I wrote it in a tutorial-ish way, but it is still just documentation nevertheless.

> 1. I would appreciate it if you went through it with a spell checker
Actually, I composed this with Word. It's just there's a "spelling error" almost every other word with all the variables, []s for formatting, etc, so it makes it rather difficult to find the actual errors.

> 2. I have seen 3 shorter codes in this website
As Chocobo said, without compression, that is pretty much impossible. Kode has a very slight inefficiency with a few bits that aren't used to match the next code size. This can lead to at a maximum one character bigger codes, though, so it's not a huge deal. And it's impossible to fix; the only thing I can do is make it so instead of being wasted, it adds security to the code. That security wouldn't be very secure, though, as it's only there half the time, so you could just pick a code that has minimal extra security.

> 3. The layout of this Tutorial/Thing could have been better
I agree. I'm just tired of writing docs (about 2/3 of my time spent on Kode was writing the docs and making the test maps), so I decided "good enough". I don't think the documentation has to be a 5 star piece of writing, only clear enough to get the information across.

> 4. If this is a tutorial shouldn't it be in tutorial submission?
Not a tutorial.

> 5. The updates make it a lot better
Yes.

> 6. Sorry for this but i just had to mention these things. I mean no offense I'm just pointing things out
No, it's fine. As I said in the first post, constructive criticism is "more then welcome".



By the way guys, the 0.2.0 attachments are the newest version, not the unnamed ones.

EDIT: You may also note that in Kode 0.1.0, the default map that is used with the testing contains case-sensitive letters, numbers, and other characters, for a total of about 94 characters. Kode 0.2.0 includes a default map of just letters - 26. That means that the codes will be somewhat longer; that's not Kode, that's just the charmap. (I actually realized that having all the extra characters didn't make it as short as I thought - only about 72% the length of 26 chars)
 

Chocobo

White-Flower
EDIT: You may also note that in Kode 0.1.0, the default map that is used with the testing contains case-sensitive letters, numbers, and other characters, for a total of about 94 characters. Kode 0.2.0 includes a default map of just letters - 26. That means that the codes will be somewhat longer; that's not Kode, that's just the charmap. (I actually realized that having all the extra characters didn't make it as short as I thought - only about 72% the length of 26 chars)
26/94 ~ 0.27659.. in differencial

36/94 ~ 0.38297..... in differencial

36/26 ~ 1.384615... in differencial

94/1 = 94 in differencial

(differencial = possibilities of the default map divided by a pre-said char map, it is equal to the difference in factor of the handle of a char map)

Changing the default map just changes the differencial, which is "almost" useless in the size unless you go on very short values, like 0 and 1, leading to a big chain of 0 and 1. (94 times bigger if you use base2 against that base94? it should be log2(x)/log2(y) I think)
 

Korolen

New User (Why do I keep getting those red bars?)
26/94 ~ 0.27659.. in differencial

36/94 ~ 0.38297..... in differencial

36/26 ~ 1.384615... in differencial

94/1 = 94 in differencial

(differencial = possibilities of the default map divided by a pre-said char map, it is equal to the difference in factor of the handle of a char map)

Changing the default map just changes the differencial, which is "almost" useless in the size unless you go on very short values, like 0 and 1, leading to a big chain of 0 and 1. (94 times bigger if you use base2 against that base94? it should be log2(x)/log2(y) I think)
Well, I'm not quite sure what you mean by "differencial", but the number of bits a certain character map length can store is log2(length), so the ratio of "storage power" of two charmaps is log2(length1):log2(length2), not length1:length2 (although I'm not sure that's what you meant, your post is unclear :p)
 

Effane

Save/Load Code Tutorial
The real thing though is uppercase vs lowercase and the average user. While 94 char system may not seem complicated to us, it would be really complicated to a 13 year old with his old computer and WC3 to show for it. Expecting someone to type and find all those chars is a bit rediculous.

not too mention some chars look alike. |l[I1] all can be very similar looking to average people and yet all are different according to a computer. O0o is bad enough and that would fall into a upper/lowercase 36 char map.

Sometimes we get so involved with the technical aspects of mapmaking we forget the human aspects of it.
 

AceHart

Your Friendly Neighborhood Admin
> I'm not quite sure what you mean by "differencial"

The number of characters the code becomes shorter from using an increasingly longer set of characters to make the code from.

You win less and less, while the code's complexity, from a usability point of view, grows ever more...
 

Squll2

je'ne sais pas
Hi~Ricky

Hi. First off is this a tutorial?

1. I would appreciate it if you went through it with a spell checker

2. I have seen 3 shorter codes in this website

3. The layout of this Tutorial/Thing could have been better

4. If this is a tutorial shouldn't it be in tutorial submission?

5. The updates make it a lot better

6. Sorry for this but i just had to mention these things. I mean no offense I'm
just pointing things out
1. you dont

2. but this is more efficent

3. this isnt a tutorial .. not really

4. its not a tutorial :p

5. i wonder what updates are for :rolleyes:

6. :rolleyes:

anyway back to the code, its really good im probably going to use this in my orpg but ajust a few things of course ^^ +rep if i can =O

Edit: mabye add a few more examples though :p
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    The old shoutbox wasn't supported anymore. We updated xenForo, so it had to be replaced.
  • jonas jonas:
    let's see if everyone finds it... the nice thing about the shoutbox was I could check on it even while logged out, but the existence of this one is hidden when you're not logged in
  • Ghan Ghan:
    We can fix that.
  • Ghan Ghan:
    Chat should show on the sidebar when not logged in now.
  • Ghan Ghan:
    (You'll still need to log in to post messages)
  • Ghan Ghan:
    Test!
  • tom_mai78101 tom_mai78101:
    I must be in a test server.
  • tom_mai78101 tom_mai78101:
    Nice, Twitter tweets embedding now works
  • Wizard Wizard:
    Yup.
  • Ghan Ghan:
    Excellent.
  • Ghan Ghan:
    @tom_mai78101 Hello there.
  • Ghan Ghan:
    Tagging works in the chat too.
  • tom_mai78101 tom_mai78101:
    @Ghan Missed it.
  • Wizard Wizard:
    Still fixing things here and there. Added widgets to the portal, will make it match the ones here on the forum index tomorrow.
  • Ghan Ghan:
    The venerable World Editor Tutorials site has been converted to HTTPS at last.
  • jonas jonas:
    cool
  • jonas jonas:
    and I can even edit my messages, nice
  • seph ir oth seph ir oth:
    GENERAL CHIT CHAT, YOU ARE A BOLD ONE
  • Ghan Ghan:
    Hello there
  • The Helper The Helper:
    this new chatbox is great and the forum software update is great too
    +1
  • The Helper The Helper:
    upgrade has fixed forum registration spam problem
  • tom_mai78101 tom_mai78101:
    Something tells me we might be able to customize the chatbox a bit, considering that there's a gap under every message.
  • Wizard Wizard:
    Going to deploy a fix soon, just had to take some time for myself this weekend.
  • Varine Varine:
    Unbelievable. Time for yourself? How dare you!

    Members online

    No members online now.

    Affiliates

    Hive Workshop
    Top