mIRC Home    About    Download    Register    News    Help

Print Thread
Joined: Nov 2011
Posts: 38
B
Bast Offline OP
Ameglian cow
OP Offline
Ameglian cow
B
Joined: Nov 2011
Posts: 38
Now i save the data to an ini file and that works great and really no problem, but im thinking of rewriting the entire script with hashtables instead.
I have tried to read up on the hashtables, i think i understand how they work.


This is how my ini looks like.

Code
[Striker]
RealName=Striker 45
Hits=1988
Kills=1007
KD=1.14
HeadShots=122
Accuracy=0.17
Shots=12034
Deaths=881

[Bizon]
RealName=PP19 Bizon
Hits=3398
Kills=881
KD=0.87
HeadShots=130
Accuracy=0.17
Shots=19561
Deaths=1017

[Fennec]
RealName=Fennec Mk9
Hits=2503
Kills=486
KD=0.86
HeadShots=68
Accuracy=0.18
Shots=13949
Deaths=565

[Uzi]
RealName=Uzi
Hits=4205
Kills=980
KD=0.89
HeadShots=148
Accuracy=0.23
Shots=18287
Deaths=1097

[Weekly]
kills=11
KillsPerGame=1.57
Death=19
KD=0.58
GulagDeath=4
TimePlayed=17541
AvgLife=674.65
ScorePerMinute=68.58
HeadShotPercent=0.09
TeamWiped=1
Revives=1
KioskBuys=10
Executions=0
Matches=7
KillsInGulag=3
DamageDone=5222
DamageTaken=6104

It about 600 rown in every ini file i got and i name them to the username so iknow which one is which.

So do i need to make a new tabe for evey entry in this ini? like
striker - my.data
Bizon - my.data
Fennec - my.data
and then save it as username.file or is there any other way of saving it to a hashtable?
then when i want the data i just load it.

Last edited by Bast; 01/11/20 05:09 PM.
Joined: Dec 2002
Posts: 252
T
Fjord artisan
Offline
Fjord artisan
T
Joined: Dec 2002
Posts: 252
One idea is a token delimiter you won't run into within your scheme. Like a character illegal to your names. Like most likely a period "." In which case you could keep one table for everything by building tokenized item names to have a value so You'd have something like:

Code
Striker.RealName = ...
Striker.Hits = ...
Striker.Kills = ...
Bizon.RealName = ...
Bizon.Hits = ...
Bizon.Kills = ...
Fennec.RealName = ...
Fennec.Hits = ...
Fennec.Kills = ...
Uzi.RealName = ...
Uzi.Hits = ...
Uzi.Kills = ...


Iterating a list like this might be more cumbersome than like using $ini(), for instance maybe you need to iterate topics. You'd commonly look at: $ini(file,0) which would in this case from what you provided, return 5. and $ini(file,1) would return "Striker"
changing from 1 to any of these: 2 = "Bizon", 3 = "Fennec", 4 = "Uzi", 5 = "Weekly"

You can Mimic this behavior also with hash tables, but you'd need to use $hfind(). I'm assuming you're never going to be more than 1 tokenized level deep, and "RealName" is a required field for every topic which gives us a nice search parameter.

$hfind(Table,*.RealName,0,w) == 4 (since Weekly has no RealName)

Now you have a value to loop through, so for instance:
$hfind(Table,*.RealName,1,w) == Striker.RealName

But really we're after the ini Equivalent of "Topic" IE: [Striker] so we really don't want the .RealName part...

var %Topic = $gettok($hfind(Table,*.RealName,1,w),1,46)

Now we have %Topic set as "Striker" which now we can do useful stuff like

echo -a Kills for: $qt($hget(Table,%Topic $+ .RealName)) == $hget(Table,%Topic $+ .Kills)

and get an output result like:

Kills for: "Striker 45" == 1007

===================================================================================================

Sometimes You want to iterate items within a topic as you don't know how many there are, you'd commonly do like this:
$ini(file,Striker,0) which by your provided list, would return 8.
$ini(file,Striker,1) would return RealName.

In order to do this we'll have to utilize $hfind() again to mimic this behavior. I'm going to assume you found the "topic" and have it set in the variable "%Topic".
$hfind(table,%Topic $+ .*,0,w) will return us our number: 8.

Now assuming you're using some form of a loop to iterate all these items, to get it like $ini(..,..,1) we're going to need to grab the 2nd token from the returned value:
var %Item = $gettok($hfind(table,%Topic $+ .*,1,w),2,46)

Now we know: %Topic is "Striker" and %Item is "RealName" and we could piece these together to grab the output like:

echo -a From Topic: $qt(%Topic) Item: $qt(%Item) == $hget(table,%Topic $+ . $+ %Item)

and get a result like:

From Topic: "Striker" Item: "RealName" == Striker 45


This is just very brief, and an introductory of one possibility to keep all the data within one hash table. I hope these examples help you morph your INI version into a hash table version. Keep in mind also that hash tables are within memory, if you do not /hsave them periodically, in an event of computer crash, power outage, accidentally exiting mIRC, etc... you could lose any potential updates pushed into your table.

Also don't forget to load your saved table before attempting to update any values if you just started mIRC. mIRC does not re-load any tables you had in memory on start. You only need to load once, then it's in memory until you delete the table or close mIRC again.

Typically you'd load saved tables on start of mIRC, or you could implement a checker to check for the existence of the table, and if it doesn't exist, load it first.

Joined: Jan 2004
Posts: 2,127
Hoopy frood
Offline
Hoopy frood
Joined: Jan 2004
Posts: 2,127
You can either keep items in separate hashtables like "hadd striker hits 123" or you can combine all into 1 table like "hadd tablename striker.hits 123". However with the latter you'd have the extra burden of continuously parsing the itemname into its components.

When saving the data, you could either save each section name to its own hashtable name, or you could save them all individually to the same file.

hsave -i striker filename.hsh striker

If you keep several tables in 1 file, It's important that you don't save without using the section name, or else they all get saved to the same [hashtable] section replacing each other's contents. You can automate the save and load like

tokenize 32 Striker Bizon Fennec Uzi Weekly
hsave -si $* filename.hsh $*

Joined: Nov 2011
Posts: 38
B
Bast Offline OP
Ameglian cow
OP Offline
Ameglian cow
B
Joined: Nov 2011
Posts: 38
thanks guys.
I didnt think of saving it like

Code
hadd -m  MP5.RealName MP5
hadd -m %Gameid MP5.Hits 1
hadd -m %Gameid MP5.Kills 2
hadd -m %Gameid MP5.KD 3
hadd -m %Gameid MP5.HeadShoots 4
hadd -m %Gameid MP5.Accuracy 5
hadd -m %Gameid MP5.Shots 6
hadd -m %Gameid MP5.Deaths 7

Joined: Nov 2011
Posts: 38
B
Bast Offline OP
Ameglian cow
OP Offline
Ameglian cow
B
Joined: Nov 2011
Posts: 38
My first thougt was to save it like i said above and then save every file for every user.
But if i would do

Code
OneTable Gameid.MP5.Hits 1


this way i just have one big file. and Gameid will have about 600 entrys.
What should i put as a bucketsize for this when i dont know how many entrys i will have in it?
since it could be 10*600 or 50*600

or is it just a better way of saving all as individual files?
If thats the case would it be heavy on the client to load let say 50 different files with 600 entrys in each?

Last edited by Bast; 02/11/20 07:30 AM.
Joined: Jan 2004
Posts: 2,127
Hoopy frood
Offline
Hoopy frood
Joined: Jan 2004
Posts: 2,127
The default number of buckets is 101, and the max allowed is 10000. I've seen recommendations that the number of buckets be the expected number of items - divided by 78%. The rationale for this is to have a high chance that each item gets assigned to its own bucket. Having more buckets has the potential to speed up the lookups, since the idea is to have 50000 items divided into 100 buckets each having 500 items, so it's faster to find an item among the 500 items assigned to that bucket instead of hunting within 1 bucket where you have a size 50000 haystack hiding the needle you're looking for.

If the number of items increases over time, something you can have your script do during ON START is to estimate the number of items by checking $lines(hashtablefilename), then calculating the number of buckets while making sure that number doesnt exceed 10000.

Joined: Nov 2011
Posts: 38
B
Bast Offline OP
Ameglian cow
OP Offline
Ameglian cow
B
Joined: Nov 2011
Posts: 38
is hashtables the best way of saving and reading data if you have lets say 100-200 different tables?

My bot atm is getting data from an api and then i save it as username.file and i keep them open so i can get them quick.
So for now i just got like 10 different tables.
But lets say my DB is getting 100 or even 200 tables. Is it still a good idea to keep it as a hastable or should i really go sql for this?
every single file contain about 600 entrys.

Joined: Feb 2003
Posts: 2,812
Hoopy frood
Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
You can flatten it all to one table instead of maintaining multiple segregated tables.

Code
var %item = $+(%username,.,payments,.,%datetime)
var %data = %sender-id %amountpaid
hadd MyTable %item %data


Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Joined: Nov 2011
Posts: 38
B
Bast Offline OP
Ameglian cow
OP Offline
Ameglian cow
B
Joined: Nov 2011
Posts: 38
well im guessing i could do

Code
/hadd -ms longtable username kill.,.1.,.Deaths.,.2.,.KD.,.3.,.Wins.,.4.,.losses.,.5.,.gulag.,.6.,.item.,.600


then to clear .,.
Code
var %table $hget(longtable,username)
var %repl $replace(%table,$chr(46) $+ $chr(44) $+ $chr(46),$chr(32))


but then how do i get the data i want?
I want the Wins that is 4 for example

Joined: Feb 2003
Posts: 2,812
Hoopy frood
Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
If you want to stack multiple values to a single string, you can do a nested string of tokenized item:values.
I use space for the outer token, and colon for the inner token.

kills:5 deaths:2 kd:2 wins:9 losses:8 gulag:6 items:589

And then I use a combination of $wildtok(%string,wins:*,1,32) to get the item:value token, and then $gettok(%token,2,58) to extract the value from the item:value pair. 58 is the ascii value of colon (:).

Code
var %username = $nick
var -s %toks = $hget(MyTable,%username)
>> kills:5 deaths:2 kd:2 wins:9 losses:8 gulag:6 items:589
var -s %tok = $wildtok(%toks,wins:*,1,32)
>> wins:9
var -s %wins = $gettok(%tok,2,58)
>> 9
inc -s %wins 1
>> 10
var %newtok = wins: $+ %wins
>> wins:10
var %toks = $reptok(%toks,%tok,%newtok,1,32)
>> kills:5 deaths:2 kd:2 wins:10 losses:8 gulag:6 items:589
hadd MyTable %username %toks

Now, you can also write a function that does all this in one go, so you can just use that simple function everywhere else in your code instead of repeating each of these steps each time. But I'll leave that to you, since it's half the fun. I would advise creating a function to increment a named value, decrement a named value, get and add/overwrite a named value. You can even do this by writing one main support function and then 4 wrapper-functions above it. Go nuts!

There are other ways, such as using $regsubex() or $regex() and $regml() or $regmlex(), but it's more convoluted and often slower and harder to read / debug.

Hope this help.

Quick tip: Instead of hard-coding "wins:" you will want to use a variable %stat that contains the word "wins", so this is what that looks like.
Code
from:  var -s %tok = $wildtok(%toks,wins:*,1,32)
to..:  var -s %tok = $wildtok(%toks,%stat $+ :*,1,32)

from:  var %newtok = wins: $+ %wins
to..:  var %newtok = %stat $+ : $+ %value
or..:  var %newtok = $+(%stat,:,%value)

Last edited by Raccoon; 10/11/20 06:16 PM.

Well. At least I won lunch.
Good philosophy, see good in bad, I like!
Joined: Feb 2003
Posts: 2,812
Hoopy frood
Offline
Hoopy frood
Joined: Feb 2003
Posts: 2,812
So I decided to rewrite those functions just to make them simple, fast and multi-purpose.
Here they are for anyone playing at home.

(Spoiler Alert)
Cheater
Code
; TokStrings are strings of space delimited colon separated item:value pairs.
; These functions allow you maintain lists of named values in a single variable.
; Limitations: Items and values *must not* contain spaces or colons.  Beware.
; There is no error checking for speed sake. If you feed in garbage you get garbage.
; 
; GetTokStr -- Get an item's value.
; $gettokstr(tokenstring, itemname) == value
; 
; SetTokStr -- Add, replace or delete an item and its value.
; $settokstr(tokenstring, itemname, [data]) == tokenstring
; 
; IncTokStr -- Increase or decrease an item's value.  Item is created if new.
; $inctokstr(tokenstring, itemname, [-]number) == tokenstring
; 
; $gettokstr(the:5 quick:3 brown:2 fox:7, fox)       == 7
; $settokstr(the:5 quick:3 brown:2, fox, 6)          == the:5 quick:3 brown:2 fox:6
; $settokstr(the:5 quick:3 brown:2 fox:7, brown, 4)  == the:5 quick:3 brown:4 fox:7
; $settokstr(the:5 quick:3 brown:2 fox:7, brown)     == the:5 quick:3 fox:7
; $inctokstr(the:5 quick:3 brown:2 fox:7, quick, -2) == the:5 quick:1 brown:2 fox:7

gettokstr {
  return $gettok($wildtok($1,$+($2,:*),1,32),2,58) 
} ; Raccoon 2020

settokstr {
  if ($3 != $null) {
    if ($wildtok($1,$+($2,:*),1,32)) {
      return $reptok($1,$v1,$+($2,:,$3),1,32)
    }
    return $addtok($1,$+($2,:,$3),32)
  }
  if ($wildtok($1,$+($2,:*),1,32)) {
    return $remtok($1,$v1,1,32)
  }
  return $1
} ; Raccoon 2020 speed

inctokstr {
  if ($wildtok($1,$+($2,:*),1,32)) {
    return $reptok($1,$v1,$+($2,:,$calc($gettok($v1,2,58) + $3)),1,32)
  }
  return $addtok($1,$+($2,:,$3),32)
} ; Raccoon 2020

; P.S. the Str might also stand for Structure. As in TokStructures.



Well. At least I won lunch.
Good philosophy, see good in bad, I like!

Link Copied to Clipboard