Depends on whether you are going codeless or not.
Don't mix similar characters then, like l and I. Only pick characters that look different from each other ; ). Either that or color code uppercase letters differently from lowercase letters I suppose ; o.
I have a base I use in my save/load tutorial. It's under my resources. You can open it up and look for it in the map ; ).
You are meant to mix them. The only exception is 'I' (uppercase iI) and 'l' (lowercase lL). This is because in the font used by WC3 there is absolutely no distinction between those two characters at all. This is not the case for every other letter as aA is unique and bB is unique etc.but btw, have reason why dont u mixed with lower case aphabet letters? like abcdef...?
The most optimal is a power of two as it avoids all the mess of non binary bases (you can allocate fractions of a character to purposes). Personally I like 64 characters as it is easy to enter 64 easily accessible and different looking characters from a standard UK keyboard and should be on most others. 128 could possibly be used however this would probably involve a lot of less standard characters which may not be easily accessible for some keyboard localized layouts.
As mentioned earlier the fact it is bit aligned makes a lot of operations very easy. If you allocate bitfields to specific purposes you can construct a save code highly efficiently. The key here is efficiency and ease of use.
The codes produced will be more optimal than most GUI save systems seen in most old maps. However there exist superior compression techniques such as those developed by Nestharus. These uses various kinds of compression to generate dynamic length codes which will mostly be smaller and a lot more flexible supporting any character base and any value range in a highly optimal way. However the encoding and decoding process is highly inefficient (involves dynamic length division and multiplication which are computationally expensive).
If you want to write your own save system I would strongly recommend the binary approach (data is allocated bitfields, all characters represent a certain number of its). The result is certainly acceptable and if you know what you are doing a reasonable system can be made in no more than an hour.
For name locking I recommend simply factoring a hash of the player name into the checksum (well its not really a checksum, more a signature). This is easy to compute, adds no extra data to the code (as the checksum is there anyway) and also is case insensitive (like account names, many silly map makers forgot Blizzard lets you change the case of your account without having to make a new one). The length of the checksum does not need to be big, it just needs to have little visible relationship to the rest of the code.
Do not bother with any kind of encryption or complex slow stuff as in the end the weakest part of the save system is the triggers itself. The only protection a save/load system needs is name and checksum (signature). This cannot stop skilled mappers like myself from cracking it by modifying the map file directly or even mr cryptography expert from cracking the code system but will stop average joe from guess a rigged code or stealing a code he sees typed in chat.
You are meant to mix them. The only exception is 'I' (uppercase iI) and 'l' (lowercase lL). This is because in the font used by WC3 there is absolutely no distinction between those two characters at all. This is not the case for every other letter as aA is unique and bB is unique etc.
Computers work in powers of two, so these align to some integer fraction of the maximum storage value of the type "integer".am not expert in this stuff thats why not know about high compressed code and why better the power of 2 stuff or how handle it
Far more complicated than that. Basically you generate a number which a small change in input results in a large change in the number. This is to stop people doing the following...with respect, i wouldn't understanded everything what u said, i am nooby, also for code checksum, i don't know exactly what is it, i guess something what check if a code got valid form or no?
for this i same the code length at end, like if code length 40 char and 'Y' char got that value then save 'Y' at end.
You map them from large 32 bit integers down into a smaller number more appropriate for your map (use an array and hashtable). For example 8 bits would give you the ability to resolve 256 different items (more than most maps need). If you have an RPG with a lot of items 10 bits (1024 items) may be required which corresponds to about 2 characters per items (64 characters for 6 bits per character so 2 characters with 2 bits remainder. You want to avoid raw number encode as that would be 4 characters.BTW how to code the item id's?
i used 73 string length for code the integers, then 73^5 is close to max integerComputers work in powers of two, so these align to some integer fraction of the maximum storage value of the type "integer".
You map them from large 32 bit integers down into a smaller number more appropriate for your map (use an array and hashtable). For example 8 bits would give you the ability to resolve 256 different items (more than most maps need). If you have an RPG with a lot of items 10 bits (1024 items) may be required which corresponds to about 2 characters per items (64 characters for 6 bits per character so 2 characters with 2 bits remainder. You want to avoid raw number encode as that would be 4 characters.
Far more complicated than that. Basically you generate a number which a small change in input results in a large change in the number. This is to stop people doing the following...
1. Loading a code used by someone else (the number is based on player name).
2. Changing parts of the code to get stuff they did not earn. Stops newbies from cheating.
3. Detecting errors when entering the code. No human is perfect and I know from personal experience that some times characters are swapped, input incorrectly or skipped.
function CodeType takes string t returns string
if t == "a" then
return "vmc3+L{sh8JgQyOZ1KG2HX[x5r60]BEafCl9Pn#j4@D()YV}dUpFt*-kRN7WIqwzeAS"
elseif t=="q" then
return "9R@O[Sty&lArn`UNSADONEUIF=gHhYwdTu}D+kKZ-mB]Jv^xbacz#E$sePoCjWVLMp_(fq{GFiIX"
elseif t=="Y" then
return "p=`U$k}n@1SJe_[dsv)z-ojLalYV5QPKwxtb/#hTq*{&^ON4y7u6~Zr3RiX2mc!80fgM9"
elseif t=="j" then
return "iX2]r*J=ZCf&pm9aAvFxVg6{kH1^t[Enoqc7D-`5Bs4}WjYde@lG38bhIzwu/y#0!U"
elseif t=="7" then
return "T}{0gJlv5PM8[~L2SO9]B_$s^DGh-tjmriIF4=7pR/fU1qEy36zN(KA!Qu)@+w&noxCkH"
elseif t=="3" then
return "1z8wKYPQ&OEe_TM[{+3Ho*ptCyV6Z@9sbcx45#`dB$LWv2q=7}rSXIGFu0-/~NDaA]JUR!"
elseif t=="l" then
return "wiHSIaEMX[jxpo{1eR]=5fLGFZ7JCu6kzBTvd24h8n+lysVmDO-qc3tWAgb9Y0NPUKrQ"
elseif t=="B" then
return "4Dm3U)6Rgv-ZWP]7q9ueFEkzwlIV2rMJYbnjG{aNLK0pxSQOHh1Cf=TyB5t@8soAcX"
elseif t=="z" then
return "*ezW2fA=GpaKZOT8rc6@dgB[{H3F+05bImCq-1y(JDxVQXMv4NUEh7w}]nioujlPstYk9"
elseif t=="[" then
return "@DE9z)QkbC4mH7T1I[=iY({x-FwRXBU]8OuPMqnZA0ghV*sadv5lp3y2Jc6KGNrWfSeL"
else
return "#"
endif
//return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+*_$#@{}()"
//return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$&/+-=[]^_`{}~()*"
//"!#$%&()*+'-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`{}~"
endfunction
function ColorCodeString takes string s, string numColor, string lowerColor, string upperColor, string specColor, integer start returns string
local string ns = ""
local string c
local integer m = StringLength(s)
local integer i = start
local boolean l
loop
exitwhen m == i
set c = SubString(s, i, i + 1)
if (c!=" ") then
set l = StringCase(c, false)==c
//special or number
if (c==StringCase(c, true) and l) then
//number
if ("0"==c or 0!=S2I(c)) then
set ns = ns + "|cff" + numColor + c
//special
else
set ns = ns + "|cff" + specColor + c
endif
//lowercase
elseif (l) then
set ns = ns + "|cff" + lowerColor + c
//uppercase
else
set ns = ns + "|cff" + upperColor + c
endif
else
set ns = ns + " "
endif
set i = i + 1
endloop
return ns
endfunction
function ItoS takes integer i, integer len, string t returns string
local string s = ""
local integer m = 0
local integer c = 0
local string LOADCODE = CodeType(t)
local integer l = StringLength(LOADCODE)
local integer v = l * l * l * l * l
local string n = SubString(LOADCODE, 0, 1)
loop
exitwhen i==0
if i > v then
set m = i / v
set i = i - v * m
if m > 0 then
set s = s + SubString(LOADCODE, m, m + 1)
endif
elseif v==1 then
set m = i
set i = i - m
set s = s + SubString(LOADCODE, m, m + 1)
set i = 0
endif
set v = v / l
endloop
if len > 0 then
loop
exitwhen StringLength(s)==len
set s = n + s
endloop
endif
return s
endfunction
function SPos takes string s, string t returns integer
local string LOADCODE = CodeType(t)
local integer l = StringLength(LOADCODE)
local integer i = 0
local string spos = ""
loop
exitwhen i == l or spos == s
set spos = SubString(LOADCODE, i, i + 1)
set i = i + 1
endloop
if spos != s then
set i = - 1
endif
set spos = null
return i - 1
endfunction
function StoI takes string c, string t returns integer
local integer l = StringLength(c)
local integer m = StringLength(CodeType(t))
local integer i = 0
local integer char
local integer int = 0
if l > 0 then
loop
exitwhen i == l
set char = SPos(SubString(c, i, i + 1), t)
if char !=0 or i > 0 then
set int = int + R2I(Pow(m, l - i - 1)) * char
endif
set i = i + 1
endloop
endif
return int
endfunction
function Trig_Save_Actions takes nothing returns nothing
local string CODEID
local integer MAX
local player p = GetTriggerPlayer()
local integer CODEL //length of the primary code
local integer CLASS
local integer LEVEL
local integer EXP
local integer STR
local integer AGI
local integer INT
local integer GOLD
local integer LUMBER
local string C_LEVEL
local string C_STR
local string C_AGI
local string C_INT
local string C_CLASS
local string C_GOLD
local string C_LUMBER
local string C_EXP
local integer H_LEVEL
local integer H_STR
local integer H_AGI
local integer H_INT
local integer H_CLASS
local integer H_GOLD
local integer H_LUMBER
local integer H_EXP
local string H
local string C_H
local integer H_LEN
local integer array CLASSTYPE
local integer i
local integer MAXCLASS
local integer ui
local string FOLDER = "SaveLoadTestForORPG"
local string PATH
local string finals
local string OUTPUT = ""
local group g = CreateGroup()
local unit u
local unit s
call SyncSelections()
call GroupEnumUnitsSelected(g, p, null)
set u = FirstOfGroup(g)
call GroupRemoveUnit(g, u)
set s = FirstOfGroup(g)
call DestroyGroup(g)
if u != null and s == null then
if IsUnitType(u, UNIT_TYPE_HERO) then
//randomize the code key then load the code
set i = GetRandomInt (1, 10)
if i == 1 then
set CODEID = "a"
elseif i == 2 then
set CODEID = "z"
elseif i == 3 then
set CODEID = "B"
elseif i == 4 then
set CODEID = "["
elseif i == 5 then
set CODEID = "l"
elseif i == 6 then
set CODEID = "3"
elseif i == 7 then
set CODEID = "7"
elseif i == 8 then
set CODEID = "j"
elseif i == 9 then
set CODEID = "y"
elseif i == 10 then
set CODEID = "q"
endif
set MAX = StringLength(CodeType(CODEID))
//declare the heroes rawcode to the CLASSTYPE
set CLASSTYPE[1] = 'Hpal' //human paladin
set CLASSTYPE[2] = 'Hamg' //human archmage
set CLASSTYPE[3] = 'Hmkg' //human mountain king
set CLASSTYPE[4] = 'Hblm' //human blood mage
set CLASSTYPE[5] = 'Obla' //orc blademaster
set CLASSTYPE[6] = 'Ofar' //orc far seer
set CLASSTYPE[7] = 'Otch' //orc tauron chieftain
set CLASSTYPE[8] = 'Oshd' //orc shadow hunter
set CLASSTYPE[9] = 'Udea' //undead death kinght
set CLASSTYPE[10] = 'Ulch' //undead lich king
set CLASSTYPE[11] = 'Udre' //undead dread knight
set CLASSTYPE[12] = 'Ucrl' //undead crypt lord
set CLASSTYPE[13] = 'Ekee' //nightelf keeper of grove
set CLASSTYPE[14] = 'Emoo' //nightelf moon priestess
set CLASSTYPE[15] = 'Edem' //nightelf demon hunter
set CLASSTYPE[16] = 'Ewar' //nightelf warden
set MAXCLASS = 16
//we find out what is the class type of our selected hero
set i = 0
set ui = GetUnitTypeId(u)
set CLASS = - 1
loop
exitwhen i > MAXCLASS or CLASS != - 1
if ui == CLASSTYPE[i] then
set CLASS = i
endif
set i = i + 1
endloop
if CLASS != - 1 and MAX > 1 then
set LEVEL = GetHeroLevel(u)
set EXP = GetHeroXP(u)
set STR = GetHeroStr (u, false)
set AGI = GetHeroAgi (u, false)
set INT = GetHeroInt (u, false)
set GOLD = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
set LUMBER = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
set C_LEVEL = ItoS(LEVEL, - 1, CODEID) //GetHeroLevel(udg_Hero[pl])
set C_STR = ItoS(STR, - 1, CODEID) //GetHeroStr (udg_Hero[pl], false)
set C_AGI = ItoS(AGI, - 1, CODEID) //GetHeroAgi (udg_Hero[pl], false)
set C_INT = ItoS(INT, - 1, CODEID) //GetHeroInt (udg_Hero[pl], false) //udg_Class2[pl]
set C_GOLD = ItoS(GOLD, - 1, CODEID)
set C_LUMBER = ItoS(LUMBER, - 1, CODEID)
set C_EXP = ItoS(EXP, - 1, CODEID)
set H_LEVEL = StringLength(C_LEVEL)
set H_STR = StringLength(C_STR)
set H_AGI = StringLength(C_AGI)
set H_INT = StringLength(C_INT)
set H_GOLD = StringLength(C_GOLD)
set H_LUMBER = StringLength(C_LUMBER)
set H_EXP = StringLength(C_EXP)
set C_CLASS = ItoS(CLASS, 1, CODEID)
set H_CLASS = 1
set H = I2S(H_LEVEL) + I2S(H_EXP) + I2S(H_STR) + I2S(H_AGI) + I2S(H_INT) + I2S(H_GOLD) + I2S(H_LUMBER)
set C_H = ItoS(S2I(H), - 1, CODEID)
set H_LEN = StringLength(C_H)
set OUTPUT = CODEID + C_CLASS + ItoS(H_LEN, 1, CODEID) + C_H + C_LEVEL + C_EXP + C_STR + C_AGI + C_INT + C_GOLD + C_LUMBER
set OUTPUT = OUTPUT + ItoS(StringLength(OUTPUT) + 2, 2, CODEID)
set PATH = FOLDER + "\\" + GetUnitName(u) + " - " + I2S(LEVEL) + ".txt"
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Code:|r -Load " + ColorCodeString(OUTPUT, "FFFFFF", "FFFF00", "00FFFF", "FF00FF", 0) )
call DisplayTextToPlayer ( p, 0, 0, "|cff7777ffNote:|r Code saved into |cffff8888" + PATH + "|r")
if GetLocalPlayer() == p then
call PreloadGenClear()
call PreloadGenStart()
call Preload("\r\n\t\t\t\tHero: " + GetUnitName(u) + "\r\n\t\t\t\t" + "Level: " + I2S(LEVEL) + "\t\t\r\n\t\t\t\t" + "Code: -Load " + OUTPUT + "\r\n\n\t\t ")
call PreloadGenEnd(PATH)
call ClearSelection()
endif
else
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r This Hero type isn't stored into array. Can't save" )
endif
else
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r Select only 1 unit and must be a Hero." )
endif
else
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r Select a Hero." )
endif
set s = null
set u = null
set g = null
set finals = null
set p = null
endfunction
//===========================================================================
function InitTrig_Simple_Save takes nothing returns nothing
set gg_trg_Simple_Save = CreateTrigger( )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(0), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(1), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(2), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(3), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(4), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(5), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(6), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(7), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(8), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(9), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(10), "-save", true )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(11), "-save", true )
call TriggerAddAction( gg_trg_Simple_Save, function Trig_Save_Actions )
endfunction
function Trig_Simple_Load_Conditions takes nothing returns boolean
return StringLength(GetEventPlayerChatString()) > 6
endfunction
function Trig_Simple_Load_Actions takes nothing returns nothing
local player p = GetTriggerPlayer()
local string CODE = SubString(GetEventPlayerChatString(), 6, 0)
local string CODEID = SubString(CODE, 0, 1)
local integer CODELEN = StringLength(CODE)
local integer MAX = StringLength(CodeType(CODEID))
local string C_CODE
local integer CLASS
local integer H_LEN
local integer CODELEN1
local string H
local integer DATA_AMOUNT_IN_H = 7 //H = holder number (ex: 12112010), where every digit show how long the following 8 stat:LEVEL, EXP, STR, AGI, INT, GOLD, LUMBER
local integer ERROR = 0
local integer i
local integer array CLASSTYPE
local integer MAXCLASS = 16
local real X
local real Y
local unit u
local integer LEVEL
local integer EXP
local integer STR
local integer AGI
local integer INT
local integer GOLD
local integer LUMBER
local integer H_LEVEL
local integer H_EXP
local integer H_STR
local integer H_AGI
local integer H_INT
local integer H_GOLD
local integer H_LUMBER
local string C_H
if MAX > 10 then
if CODELEN < 6 then
set ERROR = 1
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code." )
else
set C_CODE = SubString(CODE, 2, 0)
set CLASS = StoI (SubString(CODE, 1, 2), CODEID)
set H_LEN = StoI (SubString(CODE, 2, 3), CODEID)
set CODELEN1 = StoI(SubString(CODE, CODELEN - 2, 0), CODEID)
if CODELEN != CODELEN1 or CLASS < 0 or CLASS > MAXCLASS then
set ERROR = 2
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code." )
else
set C_H = SubString(CODE, 3, 3+H_LEN)
set H = I2S(StoI(C_H, CODEID))
if StringLength(H) < DATA_AMOUNT_IN_H then
set ERROR = 1
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code.")
else
set H_LEVEL = S2I(SubString(H, 0, 1))
set H_EXP = S2I(SubString(H, 1, 2))
set H_STR = S2I(SubString(H, 2, 3))
set H_AGI = S2I(SubString(H, 3, 4))
set H_INT = S2I(SubString(H, 4, 5))
set H_GOLD = S2I(SubString(H, 5, 6))
set H_LUMBER = S2I(SubString(H, 6, 7))
set EXP = 0 //0 values needed for avoid null variable bugs
set STR = 0 //0 values needed for avoid null variable bugs
set AGI = 0 //0 values needed for avoid null variable bugs
set INT = 0 //0 values needed for avoid null variable bugs
set GOLD = 0
set LUMBER = 0
if H_LEVEL < 1 or H_STR < 1 or H_AGI < 1 or H_INT < 1 then
set ERROR = 1
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code.")
else
set i = H_LEN + 3
set LEVEL = StoI(SubString(CODE, i, i + H_LEVEL), CODEID)
set i = i + H_LEVEL
if H_EXP > 0 then
set EXP = StoI(SubString(CODE, i, i + H_EXP), CODEID)
set i = i + H_EXP
endif
if H_STR > 0 then
set STR = StoI(SubString(CODE, i, i + H_STR), CODEID)
set i = i + H_STR
endif
if H_AGI > 0 then
set AGI = StoI(SubString(CODE, i, i + H_AGI), CODEID)
set i = i + H_AGI
endif
if H_INT > 0 then
set INT = StoI(SubString(CODE, i, i + H_INT), CODEID)
set i = i + H_INT
endif
if H_GOLD > 0 then
set GOLD = StoI(SubString(CODE, i, i + H_GOLD), CODEID)
set i = i + H_GOLD
endif
if H_LUMBER > 0 then
set LUMBER = StoI(SubString(CODE, i, i + H_LUMBER), CODEID)
set i = i + H_LUMBER
endif
// call DisplayTextToPlayer ( p, 0, 0,"LeveL"+I2S(LEVEL)+"exp"+I2S(EXP)+"str"+I2S(STR)+"agi"+I2S(AGI)+"int"+I2S(INT)+"gold"+I2S(GOLD)+"Lumber"+I2S(LUMBER))
//declare the heroes rawcode to the CLASSTYPE
set CLASSTYPE[1] = 'Hpal' //human paladin
set CLASSTYPE[2] = 'Hamg' //human archmage
set CLASSTYPE[3] = 'Hmkg' //human mountain king
set CLASSTYPE[4] = 'Hblm' //human blood mage
set CLASSTYPE[5] = 'Obla' //orc blademaster
set CLASSTYPE[6] = 'Ofar' //orc far seer
set CLASSTYPE[7] = 'Otch' //orc tauron chieftain
set CLASSTYPE[8] = 'Oshd' //orc shadow hunter
set CLASSTYPE[9] = 'Udea' //undead death kinght
set CLASSTYPE[10] = 'Ulch' //undead lich king
set CLASSTYPE[11] = 'Udre' //undead dread knight
set CLASSTYPE[12] = 'Ucrl' //undead crypt lord
set CLASSTYPE[13] = 'Ekee' //nightelf keeper of grove
set CLASSTYPE[14] = 'Emoo' //nightelf moon priestess
set CLASSTYPE[15] = 'Edem' //nightelf demon hunter
set CLASSTYPE[16] = 'Ewar' //nightelf warden
set X = GetCameraEyePositionX()
set Y = GetCameraEyePositionY()
set u = CreateUnit( p, CLASSTYPE[CLASS], X, Y, 0)
call SetHeroLevel(u, LEVEL, false)
call SetHeroXP (u, EXP, false)
call SetHeroStr (u, STR, true)
call SetHeroAgi (u, AGI, true)
call SetHeroInt (u, INT, true)
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GOLD)
call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, LUMBER)
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Load:|r "+GetUnitName(u)+" loaded." )
if GetLocalPlayer() == p then
call ClearSelection()
call SelectUnit(u, true)
call PanCameraTo(X, Y)
endif
set u = null
endif
endif
endif
endif
else
call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code." )
endif
set p = null
endfunction
//===========================================================================
function InitTrig_Simple_Load takes nothing returns nothing
set gg_trg_Simple_Load = CreateTrigger( )
call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Load, Player(0), "-Load", false )
call TriggerAddCondition( gg_trg_Simple_Load, Condition( function Trig_Simple_Load_Conditions ) )
call TriggerAddAction( gg_trg_Simple_Load, function Trig_Simple_Load_Actions )
endfunction
Yes except using such numbers efficiently is considerably more difficulty. Where as a base 64 system could be broken down into 6 separately allocate-able data elements, a base 73 system cannot directly. To efficiently compact data in such a system (break it into smaller elements which you can allocate) you need to go to long multiplication and division which is slow and not the easiest to write.i used 73 string length for code the integers, then 73^5 is close to max integer
If your map has 1024 items, you can do the following.i think i stay with rawcode if other way storeing to array/hashtable all item what could be huge work for every item for rpg also if have no class restriction except weapon item type or i want make custom items then array isnt option and where is noobie 6 item in inventory stuff/restriction stuff there could be more efficient the hash/array but there anyway thecode is short, no?
can u show a short example to this? a easiest example
call SaveInteger(somehashtable, itemtype, 0, 0)
set itemmap[0] = itemtype
call SaveInteger(somehashtable, itemtype2, 0, 1)
set itemmap[1] = itemtype2
//etc
You do none of the sort since Blizzard made a function which does this for you far more efficiently.this sound interesting, but how number based on player name? is this revertable thing or something like add the letter values to each other and code it? i mean example shadow, s=2,h=11,a=3... 2+11+3.. etc result lets say 317 and save that to last line?
or make "0123456789abcdef..." string into array with random order each string like "3anoq4ks1..."?
native StringHash takes string s returns integer
Yes except using such numbers efficiently is considerably more difficulty. Where as a base 64 system could be broken down into 6 separately allocate-able data elements, a base 73 system cannot directly. To efficiently compact data in such a system (break it into smaller elements which you can allocate) you need to go to long multiplication and division which is slow and not the easiest to write.
If your map has 1024 items, you can do the following.
To get an item save identifier use a hashtable where the parent key is the item type and child some constant. To get an item type from an item save identifier simply use the item save identifier as an index in an integer array. Note this requires use of JASS since GUI hides that all types in the game are integers.
This means at map initialization you need to define two mappings.
JASS:call SaveInteger(somehashtable, itemtype, 0, 0) set itemmap[0] = itemtype call SaveInteger(somehashtable, itemtype2, 0, 1) set itemmap[1] = itemtype2 //etc
You do none of the sort since Blizzard made a function which does this for you far more efficiently.
And it does have a GUI counterpart.JASS:native StringHash takes string s returns integer
The idea is not to get the player name back as that just annoys players since they have to type more. Instead you store a number which has a very low probability of being the same between any two players yet is deterministic (same output for same input). This is exactly what a hash is.
All that is required is for you to incorporate the hash of the player name into the code signature at some stage and it will name protect the code. If a player tries to load with a different account chances are the signature will mismatch so you abort his loading with an invalid code error. Obviously the chances that two accounts can load each other's codes is based on the signature size you use up to a maximum of 32 bits after which a custom hashing algorithm is required. The main advantage of this approach is that as little as 2 characters can be sufficient for a reasonable signature that both name locks and checks code integrity. Another advantage is that it is computed very fast (a single function call).
A signature is a number which is stored in the code that is computed as well during decoding. After all values of the code are decoded the stored signature is compared with the computer signature. If they are different then the code has been tampered with, is corrupt or not under the same signature rules as when it was generated. The signature should factor in the value of each piece of data encoded and decoded so as to detect change in that data in the form of a signature mismatch. The simplest form of such a signature could be something like a checksum however I would not use such an algorithm as it does not provide effective error detection.
Both are 32 bit integers. I have no clue why it would be "awesome". Hashes are not reversible as they represent a loss of information. There exists a large number of inputs that produce the same output hash however these inputs are not easily related to each other ("DrSuperGood" could possible have the same hash as "1vsdfg1vxcv5sdfs1cczx" howver the chances of someone using that name is statistically as good as impossible. Since 32 bits might be larger than the signature you are using you can compact it by form of modulus which will result in a lower bit hash that has more inputs corresponding to the same output. This is very useful for small codes however for long codes using 32 bit or more for signature is acceptable (6 characters) so is not an issue.btw this would be awasome if item raw code could be stringhash and revers, from stringhash to rawcode
Not sure what you are saying. However the purpose is to reduce information of each item from 32 bits (raw code types are 32 bit integers) down to only 10 or so bits. Yes it is a pain to enter every item, however some kind of loop could be used if you used JNGP to assign all custom items raw codes that are near each other.this same story... so long till i define manually every item, +2k line for 1k item :/
also example this dont solve if i want use for my own map where same item raw code items got different stats based on data in hashtable where key1: item handle id, this make me able produce unlimited items (like one with 10 dmg, i put gem and became 20 dmg) just icon same
It does not mater as the hash the same string will always return the same number. If the typeface is missing it will still work as hashes use actual character values and not meanings. To repeat what I said earlier, upper and lower cases must be treated the same since you can change the case of your account name at the login screen without making a new account so the same account can be used as lettering of any case. An example is my account "DrSuperGood" can be used as "dRsUPERgOOD" or "DrSuPeRgOoD" and it will still authenticate on BattleNet as the same account. A lot of newbie map makers fail to realise this so instead unrightfully class the same account as different people purely because on the day the guy decided to login with lettering in different case.next question, in warcraft the player name could be only 0-9, a-z, A-Z characters or could be non english letters for player name?
Not sure what you are saying. However the purpose is to reduce information of each item from 32 bits (raw code types are 32 bit integers) down to only 10 or so bits. Yes it is a pain to enter every item, however some kind of loop could be used if you used JNGP to assign all custom items raw codes that are near each other.
As far as I know it is not possible for two entries with the same raw code value to exist as the game would probably not know which to choose.i talk about my signature map what is in spell section or custom stat system by Doomlord where 2 same raw coded item got different effect or just changed the effect during the game.
Yes.i can use string hash for long string?
Adding the values together is not very good for error detection.also what do u think with adding letters value to each other like was in my above post?
As far as I know it is not possible for two entries with the same raw code value to exist as the game would probably not know which to choose.
Adding the values together is not very good for error detection.
That is irrelevant because the chances of two strings generating the same hash is statistically impossible (you will have more luck winning the lottery). Sure someone could brute force it eventually however people smart enough to do that can just extract the save system from your map and generate whatever they want with less effort.i arrived to home, i tested the hash string id, seems work and interger not overflow just max digit, and here come the question, if integer cant be higher then the limit but i can type longer string (100 char) then with huge string dont give more often the same string hash id than example with a 20-30 char long string?
You still need the hash because that is a number generated from player name. If it is too big simply discard the most significant figures (which results in more strings generating the same result).this would be better maybe than hash because i guess 3-4 character enough and for me the hash string need 5
That is irrelevant because the chances of two strings generating the same hash is statistically impossible (you will have more luck winning the lottery). Sure someone could brute force it eventually however people smart enough to do that can just extract the save system from your map and generate whatever they want with less effort.
You still need the hash because that is a number generated from player name. If it is too big simply discard the most significant figures (which results in more strings generating the same result).
The idea for the error detection is that it should not be easily relatable to value changes. A good example would be a cyclic redundancy check (CRC) where a single bit change can cause a very different CRC to be produced. You could then add this CRC to the player hash to generate your signature. Problem is that CRCs are difficult to compute using JASS due to the lack of bitwise operators.
As I stated already, discard the most significant figures from it until it is small enough. Modulus does this.after i watch long string with hash id is nice but if it is give to me a number between 73^5 (2073071593) and max integer (2147483648) then i stucked :/
Hash does everything you need for player name locking, no need to try and re-invent a wheel possibly making it a different shape when you already have circular wheels.but maybe if my ideea above would be good then maybe have way to convert player name to encoded character and do same thing with check character values in both encodeing string
Hash does everything you need for player name locking, no need to try and re-invent a wheel possibly making it a different shape when you already have circular wheels.
set HH = OUTPUT + StringCase(GetPlayerName(p), false)
//------------------------------------------------------------------------------------------
set HASHID = StringHash(HH)//StringHash(OUTPUT + StringCase(GetPlayerName(p), false))
//------------------------------------------------------------------------------------------
Do me a favor since i'm seeing some silly stuff that can be remedied. My interactive save/load tut ansers all of the questions you have asked and has discussed some of the things DSG has mentioned.
Open the map in WE
Do not edit, save it, or look at the triggers
Hit Test Map. In game, it will guide you through short lessons that will teach you about save/load.
Lessons include CRC, checksum, encryption, name specific, caeser ciphers, and compression, among other things. The lessons will also show the real results when applying these techniques, so you can decide for yourself what is worth it. Encryption using scrambler is old technique. If code greater than or equal to 128 bits, use AES. If not, use Scrambler. If you don't care about uber short codes and 128 bit size is fine for minimum, just use AES. Stick with knuth for checksum. MD5 is too big.
Scrambler is cool in that it scrambles your number up using the player's nickname, meaning you don't have to store anything. AES does the same.
Either do a checksum using player's nick and code, or do encryption on code using player's nick as a key.
There are many other things. You can skip math in tutorial, unless you really wanna learn it. Just do that tutorial and then get back with us. That's part of the reason i wrote it .
BigInt is long division
BitInt is bitwise operators on huge numbers
Just do the tutorial. It doesn't teach code, it teaches save/load. Use what you learn however you wish.
Just stop arguing and run the map, lol. If you don't like what you see, then you can exit the map. Skip the math, just learn the concepts and what the results are.
BigInt is only ine thing, compression. There is still everything else . Plus you can use bitwise operators, which we have libs for, if you wanna do base 64.