mIRC Home    About    Download    Register    News    Help

Print Thread
Page 1 of 2 1 2
#206888 28/11/08 06:39 AM
Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
I just started learning hash tables and it looks easy, I just can't figure out exactly how to use them and add data to tables.

I currently have an IP script which stores all user IP's in an INI file. Now that there are more users stored in the INI, it takes longer to retrieve information from the INI as its size increases. I've made a script that literally completely converts my method of IP storage from INI's to hash tables, but I can't see how adding data to tables work.

Some users have dynamic IP (hence multiple IP's must be stored under their name), and others have static. I'm trying to add the information to the tables like this: /hadd userips <nick> <ip>, which adds the first IP, but then when I go to add another, it overwrites the current IP with the new one even when the IP is different. So I guess my question is why can't I add multiple values? How can I store IP's using hash tables since my method doesn't work?

Any help is always appreciated. Thanks =)

Joe_Dean #206889 28/11/08 07:20 AM
Joined: Aug 2004
Posts: 7,252
R
Hoopy frood
Offline
Hoopy frood
R
Joined: Aug 2004
Posts: 7,252

You can add multiple values for the situation that you're describing by using a combination of /hadd, $hget, and $addtok

The format you're looking for would look like:
Code:
/hadd -m Table $nick $addtok($hget(Table,$nick),$address,32)


If you don't understand that, here's a simplified, but multiple line version of the same thing.

Code:
var %addresses = $hget(Table,$nick)
var %addresses = $addtok(%addresses,$address,32)
/hadd -m Table $nick %addresses

See /help /hadd
/help $hget
and /help $addtok

This is the easiest way of doing what you're asking, but there are other options.

As to why the IP address was over-written, since you're familiar with INI files I'll use an INI file comparison.

You use
Code:
/writeini -n file.ini section item data
to write information to the ini file.
Now, if you specify the same section and item, but different data, the data that was in the ini file gets over-written

Hash tables can directly load sections from an ini file using the -i switch. This makes each item from the ini file the item in the hash table, and the same with the data, but the information regarding what section is not retained in the hash table.


RusselB #206895 28/11/08 10:01 AM
Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
Ok, I understand how to use the tables now. In my current script, there is a feature that loops through the entire INI and returns the name of the person who used the specified IP.

Is it possible to loop through the table too? I know there's $hfind, but I imagine that would only return the first result it finds, correct?

Joe_Dean #206898 28/11/08 02:59 PM
Joined: Oct 2005
Posts: 1,741
G
Hoopy frood
Offline
Hoopy frood
G
Joined: Oct 2005
Posts: 1,741
You can loop through hash tables as well by using $hfind(table,*,0,w) to get the number of entries in the hash. However, if you choose to store your data differently in the hash table, you can eliminate constantly looping through each entry. For example, if you wanted to know who has used a certain IP before, instead of using $nick as the ITEM and a list of addresses as the DATA, you could use a single address as the ITEM and a list of $nick's as the DATA.

12.34.56.78 = Bob,Sam,Mike,Betty
23.45.67.89 = Alan,Steve,Sally,Betty

Using that format if the address you were looking for was stored in %addr, you could find the list of names by using $hget(table,%addr).

If you are looking to be able to search for who has used a certain IP before, AND what IPs a certain person has used, I would recommend storing the info in the hash table BOTH ways. By using some kind of distinguishing prefix in the ITEM, you can store multiple sets of data in a single table. Example:

i-12.34.56.78 = Bob,Sam,Mike,Betty
i-23.45.67.89 = Sam,Steve,Sally,Betty
n-Bob = 12.34.56.78
n-Sam = 12.34.56.78,23.45.67.89
n-Mike = 12.34.56.78
n-Betty = 12.34.56.78
n-Steve = 23.45.67.89
n-Sally = 23.45.67.89


To find out who has used an IP, as above, you would now use $hget(table,$+(i-,%addr)). And to find out what IPs a certain nick has used, you would type $hget(table,$+(n-,%nick)).

It is much more efficient (in terms of speed) to do all the work twice when you are adding the data, rather than 10000 times each time you need to loop through every entry to find some data.

-genius_at_work

Joe_Dean #206904 28/11/08 04:46 PM
Joined: Jan 2007
Posts: 1,156
D
Hoopy frood
Offline
Hoopy frood
D
Joined: Jan 2007
Posts: 1,156
I agree with Genius.

Regardless, $hget(table,0).item will return total items.

Code:
var %r = $hget(table,0).item | while (%r) {

dec %r
}

DJ_Sol #206915 28/11/08 07:40 PM
Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
genius_at_work, it's a good idea but it'd be too much work. The INI has almost 2,000 lines and adding that much information to a hash table the way you're describing would take a long time.

This is what my hash table looks like:
Table: userips
Items: <nicks> (almost 2,000 nicks to be exact)
Values: IP(s) (which are separated by commas if there are multiple IP's)

So say if I did !searchip <nick>, I would use $hget(userips,$2) which would return all the IP's that user has used.

But the part I can't get working is if you search for an IP, it should return the nick (or the item name in the table). For example: !searchip 1.2.3.4

...would return all item names that have 1.2.3.4 anywhere in the value. I don't want to return the number of items (which btw still didn't work, I tried using what you said and it returned 0 when I searched for my IP and my IP exists in many items), but I want to return the actual name of the item.

Joe_Dean #206928 29/11/08 01:32 AM
Joined: Aug 2004
Posts: 7,252
R
Hoopy frood
Offline
Hoopy frood
R
Joined: Aug 2004
Posts: 7,252
Code:
on *:text:!searchip <nick>:*:{
  if $hget(userips,$1) {    .msg $nick $1 $+ 's IPs are $v1  }
  elseif !$hfind(userips,$+(*,$1,*),0,w) {    .msg $nick No match found for $1  }
  else {
    var %a = 1, %b = $hfind(userips,$+(*,$1,*),0,w)
    .msg $nick Matches for $1 are:
    while %a <= %b {
      .timer 1 %a msg $nick $hfind(userips,$+(*,$1,*),%a,w)
      inc %a
    }
  }
}


Dejavu is kicking in with this topic, yet I'm unable to find the topic that I'm (almost) certain I gave almost identical replies to.

Joe_Dean #206929 29/11/08 02:32 AM
Joined: Oct 2005
Posts: 1,741
G
Hoopy frood
Offline
Hoopy frood
G
Joined: Oct 2005
Posts: 1,741
It would be very little work to transfer from an INI file to the dual-format HASH table that I described above.

Code:

alias initohash {
  var %if = myfile.ini, %is = mysection
  var %hn = myhash, %hs = 100
  ;;;;
  if ($hget(%hn)) {
    if ($?!="Hash Table ' %hn ' already exists. $crlf $+ OVERWRITE the existing data?") hfree %hn
    else return
  }
  var %ts = $ticks
  hmake %hn %hs
  var %ii, %id, %di, %dk, %hi, %hk
  var %a = 0, %aa = $ini(%if,%is,0)
  while (%a < %aa) {
    inc %a
    %ii = $ini(%if,%is,%a)
    %id = $readini(%if,%is,%ii)
    var %b = 0, %bb = $numtok(%id,44)
    while (%b < %bb) {
      inc %b
      %di = $gettok(%id,%b,44)
      %dk = %ii
      %hi = $hget(%hn,%dk)
      hadd %hn %dk $addtok(%hi,%di,44)
      %hk = $hget(%hn,%di)
      hadd %hn %di $addtok(%hk,%dk,44)
    }
  }
  echo -at Transferred %if to %hn in $calc(($ticks - %ts) / 1000) $+ s
}



This code assumes that your ini file is structured like this:

Originally Posted By: myfile.ini


[OnlyOneSection]
nick1=1.2.3.4
nick2=2.3.4.5
nick3=3.4.5.6,4.5.6.7,5.6.7.8
nick4=1.2.3.4,4.5.6.7
nick5=4.5.6.7



Set the variables at the top of the alias, then call /initohash.

It will transfer into the hash table like this:

Originally Posted By: myhash


1.2.3.4 nick1,nick4
3.4.5.6 nick3
5.6.7.8 nick3
2.3.4.5 nick2
4.5.6.7 nick3,nick4,nick5

nick5 4.5.6.7
nick4 1.2.3.4,4.5.6.7
nick1 1.2.3.4
nick3 3.4.5.6,4.5.6.7,5.6.7.8
nick2 2.3.4.5




To get a list of IPs used by %nick:
echo -a $hget(myhash,%nick)

To get a list of nicks who used %addr:
echo -a $hget(myhash,%addr)


-genius_at_work


Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
@RusselB: Doesn't work as expected.

Quote:
Joe_Dean · !hget Joe_Dean
Naed · Joe_Dean's IPs are 76.106.215.49
Joe_Dean · !hget 76.106.215.49
Naed · No match found for 76.106.215.49
Joe_Dean · !hget 76.106.215
Naed · No match found for 76.106.215
Joe_Dean · !hget 76.106
Naed · No match found for 76.106
Joe_Dean · !hget 76
Naed · Matches for 76 are:
Naed · Guest769
Naed · t12P76G
Naed · kwiatu1767
Naed · t12P76Gnew


- Doesn't show names using my IP when I reference my IP.
- Doesn't support wildcard searches (also it kinda does when you only specify the first octet, but still doesn't show all names using that range).

@genius: I really appreciate the help and it sounded like a great idea, but I don't think I like it so much. I also believe there's something wrong in your code. It returns the same name twice sometimes and also returns IP addresses when I reference a range. Plus, it returns every other name using the IP except mine.

Bah, this shit is confusing the heck out of me. There isn't a simpler way? My original idea was to store the nicks as the table items and the IP's as the values separated by commas (if there are multiple IP's). How do I do a wildcard search in the value and return the item name? Would make things much much easier.

Joe_Dean #206947 29/11/08 09:52 PM
Joined: Aug 2004
Posts: 7,252
R
Hoopy frood
Offline
Hoopy frood
R
Joined: Aug 2004
Posts: 7,252
oops.. thought there was something missing. Wildcard searches are not included in my code, as you didn't specify that you wanted/needed that option, and it will make the code more difficult.

I'll think about the alterations required to allow for wildcard searches, but in the mean time, try this version
Code:
on *:text:!searchip <nick>:*:{
  if $hget(userips,$1) {    .msg $nick $1 $+ 's IPs are $v1  }
  elseif !$hfind(userips,$+(*,$1,*),0,w).data {    .msg $nick No match found for $1  }
  else {
    var %a = 1, %b = $hfind(userips,$+(*,$1,*),0,w).data
    .msg $nick Matches for $1 are:
    while %a <= %b {
      .timer 1 %a msg $nick $hfind(userips,$+(*,$1,*),%a,w).data
      inc %a
    }
  }
}

RusselB #206950 29/11/08 10:58 PM
Joined: Nov 2006
Posts: 1,559
H
Hoopy frood
Offline
Hoopy frood
H
Joined: Nov 2006
Posts: 1,559
The problem with a wildcard scan is that it will ignore the comma delimiters, and match on the whole data part.

Here's a "rough" hfind(r) [regex search] approach; I adapted these regexes from this script.
It may not be optimized for performance (one would have to follow Genius' suggestions), and the output may be improved with some $addtok-routine instead of a "one nick - one line" play.
Another idea would be to limit a max. number of matches (prevent playback of the whole dataset if someone does a !serchip *.*.*.* or the like)...

Code:
on *:text:!searchip &:*: {
  if (. !isin $2) {
    if (($2 !isnum) && ($hget(userips,$2))) { .msg $nick $2 $+ 's IPs are: $v1 }
    else { .msg $nick No matches for nick $qt($2) }
  }
  else { 

    ; $1 is a valid (wildcard-)IP "A.A.A.A" (where each A is 1-3 digits/wildcards)
    var %isipreg = /^(?:(?:\d|\*|\?){1,3}\.){3}(\d|\*|\?){1,3}$/

    if ($regex($2,%isipreg)) {
      ; replaces to build a regex that will match wild-IP $2 in a comma-delimited string of IPs
      var %findreg = $replace($regsubex($2,/\*+/g,*),.,\.,*,\d*,?,\d) 
      var %findreg = $+(/(?<=^|\54),%findreg,(?=$|\54)/)

      write -c searchip.txt

     ; perform hfind loop, write results to a file
      var %n = 1
      while ($hfind(userips,%findreg,%n,r).data) {
        write searchip.txt $v1
        inc %n
      }

      var %l = $lines(searchip.txt)
      if (%l = 0) { .msg $nick There are no matches for IP $qt($2) }
      else { 
        ; sort file, play results to nick
        filter -ffct 1 32 searchip.txt searchip.txt
        .msg $nick There $iif((%l == 1),is %l match,are %l matches) for IP $qt($2) : 
        play -m2 $nick searchip.txt 300
      }
    }
    else { .msg $nick Wrong Syntax. use !searchip <nick> or !searchip <IP>. <IP> has to be N.N.N.N and may contain wildcards. }
  } 
}


...hope it's of use

Last edited by Horstl; 29/11/08 11:14 PM.
Horstl #207041 03/12/08 01:12 AM
Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
Ehh, I have a problem. What would cause all my tables to be automatically deleted when the client is opened/closed? I had a table, then restarted my computer and opened mIRC to find the table was gone.

Any ideas?

Joe_Dean #207044 03/12/08 01:28 AM
Joined: Aug 2004
Posts: 7,252
R
Hoopy frood
Offline
Hoopy frood
R
Joined: Aug 2004
Posts: 7,252
mIRC does not retain hash table information when the client is closed. I use the ON EXIT event to save hash tables to the hard drive so that the data is not lost.
Additionally, hash tables are stored in RAM, so when you restarted your computer, the ram was cleared.

This is par of the reason why I (usually) have the following in any script that uses a hash table

Code:
on *:start:{
  if !$hget(TABLE) { .hmake TABLE 100 }
  if $exists($scriptdirTABLE.hsh)) { .hload TABLE $scriptdirTABLE.hsh }
}
on *:exit:{
  .hsave TABLE $scriptdirTABLE.hsh
}
on *:disconnect:{
  .hsave TABLE $scriptdirTABLE.hsh
}
Replace TABLE with the actual name of the hash table you want referenced.

RusselB #207049 03/12/08 03:46 AM
Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
And what happens when the client stops responding and you have to end the task, or if your power goes out, etc?

Joe_Dean #207051 03/12/08 04:17 AM
Joined: Oct 2005
Posts: 1,741
G
Hoopy frood
Offline
Hoopy frood
G
Joined: Oct 2005
Posts: 1,741
You can run a repeating timer that periodically saves the hash table to file. The frequency of the timer will depend on how often you change the contents of the file, and how worried you are about potentially losing a few entries in the table.

-genius_at_work

RusselB #207052 03/12/08 04:24 AM
Joined: Jan 2007
Posts: 1,156
D
Hoopy frood
Offline
Hoopy frood
D
Joined: Jan 2007
Posts: 1,156
Just as an aside and an opinion. I use hash tables a lot and I never use hfind. I create my hash tables to return information directly.

For instance, I choose the most common denominator for the item. Take user managment. On some servers the nick will never change, on others you can grab a persons IP. On my server we use a gatekeeper address assigned by the server.

I know they can change their nick and everything, but that account will always have the same gatekeeper, so I set the item as the gatekeeper.

hadd table gatekeeper data

So now, $hget(table,$address) returns all data I collected on the user. I save all the data in the same order so I can use $gettok or something to choose what I want from the data.

The point I am trying to make is that I design my tables to be accessed quickly and easily. I think there is a value in considering this tactic.

I'm not saying there is anything wrong with $hfind, I just consider directly calling the item in the hash table a bit faster and less work for mIRC.

Edit: Sorry this wasn't a reply to Russelb in particular, it was in response for the request to search for wildcards in a hash table.

Last edited by DJ_Sol; 03/12/08 04:26 AM.
DJ_Sol #207106 06/12/08 12:45 PM
Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
Ok genius, I did what you said and it's all working fine. There's a small thing that may become an issue sooner or later though. When the bot closes or disconnects, it saves the table to the INI file. On load it loads the INI, etc (as you already know).

But the thing is, it's loading the INI to the hash and the INI has over 10k lines which will probably soon be 20k, etc. The number of lines will just keep increasing and the time it takes to load the INI into the hash will take even longer.

My idea was on disconnect or exit, look at the current INI and use $lines to see how many lines it has. If it exceeds 10k lines, create a second INI and on load, it could just load all INI's. But I would need the script to know which INI was the last INI to be created so it could check that file to see if it exceeds the line limit which will determine whether or not it needs to create a new INI.

So if I named the INI's: userips1.ini, userips2.ini, userips3.ini, etc, how could I make the script know that 'userips3.ini' was the last file to be created?

Joe_Dean #207107 06/12/08 01:31 PM
Joined: Aug 2004
Posts: 7,252
R
Hoopy frood
Offline
Hoopy frood
R
Joined: Aug 2004
Posts: 7,252
I may have missed seeing this, but are you specifying the section when loading/saving the hash table from/to the ini file?

If you aren't, then why not just use the /hsave command, and not worry about the fact that you're currently using an ini file.

This, imo, has the following advantages:
1) There's no need to worry about the number of lines in the file
2) You don't have to worry about the 64k "limit" on ini files.

Regarding speed, I have a bot which currently has 8 hash tables, of which 3 of them use files that are nearly 25,000 lines (when checking the number of lines in the saved file), and it only takes about 3 seconds to load all 8 tables.

Joe_Dean #207111 06/12/08 05:32 PM
Joined: Oct 2005
Posts: 1,741
G
Hoopy frood
Offline
Hoopy frood
G
Joined: Oct 2005
Posts: 1,741
Hopefully, you are already using the /hsave and /hload commands to save and load your hash table. Either way, you should be saving and loading the hash table using commands like this:

/hload myhash myhash.htb

/hsave myhash myhash.htb


-genius_at_work

Joined: May 2008
Posts: 127
J
Vogon poet
OP Offline
Vogon poet
J
Joined: May 2008
Posts: 127
I'm saving to and loading from INI files (and yes, I'm specifying the INI section name).

So don't use INI's? Any extension I choose will work?

Page 1 of 2 1 2

Link Copied to Clipboard