mIRC Home    About    Download    Register    News    Help

Print Thread
Joined: Aug 2005
Posts: 525
S
Fjord artisan
OP Offline
Fjord artisan
S
Joined: Aug 2005
Posts: 525
I'm working on a game server query dialog, which for the most part is complete. I have only a small problem here. The server sends back information in the following form:

\player_5\-=[TDK]=-XBLADE\frags_5\0\ping_5\ 95\team_5\0\mesh_5\Female Commando\skin_5\FCommandoSkins.aphe\face_5\FCommandoSkins.Indina\ngsecret_5\true\player_6\lCPAl_(=D/\n][EL=)\frags_6\0\ping_6\ 132\team_6\0\mesh_6\Male Soldier\skin_6\SoldierSkins.sldr\face_6\SoldierSkins.Brock\ngsecret_6\true\player_7\O-KCUHP\frags_7\0\ping_7\ 54\team_7\1\mesh_7\Male Commando\skin_7\CommandoSkins.goth\face_7\CommandoSkins.Grail\ngsecret_7

The above is only a small portion of what it sends back. What I'm trying to do here is grab the token which comes directly after player_x (where x can be a number), which is the player name. I was using $gettok for this, but then I ran into this problem because it's possible to use a \ in playername. So in the above bit you see there is a player named lCPAl_(=D/\n][EL=) and I'm sure you can figure out that in the dialog, it gets displayed as only lCPAl_(=D/ since \ is the separating character for bits of info.

So basically what I'm after, is a method to obtain the text that is between player_x\ and \frags_. I suppose regex would be a good way, but I don't understand it well enough to pull this off, while still displaying all names correctly. Or if there is some other method that would work equally as well, that'd be fine.

Any help appreciated.

Edit: If needed, you can use this for testing purposes. There should be players on this server at any given time.

sockudp -k gquery 205.238.253.143 7778 \status\

Joined: Sep 2003
Posts: 4,230
D
Hoopy frood
Offline
Hoopy frood
D
Joined: Sep 2003
Posts: 4,230
A regex would be best, but if one doesnt appear this should work.

Code:
;$1 = player number
;$2 = string the data is in
alias get.player.name {
  var %player = $int($1)
  if ((%player isnum 1-) && ($pos($2,$+(player_,%player),1)) {
    var %player.name = $mid($2,$calc($v1 + $len($+(player_,%player,\))))
    if ($pos(%player.name,\frags_,1)) {
      var %player.name = $left(%player.name,$calc($v1 - 1))
    }
    return %player.name
  }
}

* code untested *

use as follows, assuming %text contains the line from the server
//echo -a $get.player.name(5,%text)
-=[TDK]=-XBLADE
//echo -a $get.player.name(6,%text)
lCPAl_(=D/\n][EL=)

Joined: Oct 2005
Posts: 1,741
G
Hoopy frood
Offline
Hoopy frood
G
Joined: Oct 2005
Posts: 1,741
Is the information always in the same order? In the example below, are the red parts always in that order exactly?

...\player_6\lCPAl_(=D/\n][EL=)\frags_6\...

If those portions are always in the same place, it should be easy to retrieve the info using a regex.

Try this code:
Code:
  ; $1 = player ID number
  var %r = /player_( $+ $$1 $+ )\\(.+)\\frags_\1/i
  if ($regex(a,[color:red]%text[/color],%r)) echo -a ID: $regml(a,1) Nick: $regml(a,2)
  else echo -a Unknown Player ID: $1


%text should contain the data returned from the server, $1 is the ID number of the player. $regml(a,2) contains the nick of the player.

-genius_at_work

Last edited by genius_at_work; 02/02/06 02:43 AM.
Joined: Aug 2005
Posts: 525
S
Fjord artisan
OP Offline
Fjord artisan
S
Joined: Aug 2005
Posts: 525
Yes, for each player the information is always in the same order. The ID may not always be consecutive, since players come and go during the same map. So it can start with player_5 and next player_9, but the other info always follows in the same order.

\player_5\-=[TDK]=-XBLADE\frags_5\0\ping_5\ 95\team_5\0\mesh_5\Female Commando\skin_5\FCommandoSkins.aphe\face_5\FCommandoSkins.Indina\ngsecret_5\true\

\player_6\lCPAl_(=D/\n][EL=)\frags_6\0\ping_6\ 132\team_6\0\mesh_6\Male Soldier\skin_6\SoldierSkins.sldr\face_6\SoldierSkins.Brock\ngsecret_6\true\

\player_7\O-KCUHP\frags_7\0\ping_7\ 54\team_7\1\mesh_7\Male Commando\skin_7\CommandoSkins.goth\face_7\CommandoSkins.Grail\ngsecret_7\true\

Joined: Aug 2005
Posts: 525
S
Fjord artisan
OP Offline
Fjord artisan
S
Joined: Aug 2005
Posts: 525
Thanks Dave for the effort, but I've used genius's code. Works great.

Joined: Oct 2005
Posts: 1,741
G
Hoopy frood
Offline
Hoopy frood
G
Joined: Oct 2005
Posts: 1,741
I noticed that I missed something in the regex. It should be like this:

var %r = /player_( $+ $$1 $+ )\\(.+)\\frags_\1\\/i

If that isn't done, it would return incorrect data if you supplied a number that was the beginning of another number, like "1" is the first digit of "15". The extra \\ will prevent that from happening.

-genius_at_work

Joined: Feb 2004
Posts: 2,019
Hoopy frood
Offline
Hoopy frood
Joined: Feb 2004
Posts: 2,019
The following regex will be quite more efficient in terms of backtracking:

$regex(a,$1,/\\player_ $2\\(.+?)\\frags_ $2\\/xi)

When you specify .+, the engine will consume every character till the end of the possibly quite long string, and then backtrack until it can match your expression. In the case of ungreedy matching (use a ? after the quantifier), it will only take one char at a time and "backtrack" forwardly until it finds a match. You can see in his example string, that this will result into far less backtracking, basically it'll move along forwardly for only the length of the player name (which we want to capture).

The word "backtrack" is somewhat confusing for people new at regex, because it seems to suggest that the engine goes back. It simply refers to the regex engine moving either forwardly or backwards when a quantifier is used, when it cannot match the expression. In the case of ungreedy matching, the backtracking will be forward, it will move forward until it finds a satisfying match for the rest of the expression. In case of greedy matching, the backtracking will be backwards.

There's another reason why .+? is prefered over .+ in cases such as this, although for this specific case it'll probably be fine. Observe the following regex:

//echo -a $regml($regex("this string" contains "multiple quotes",/(".+")/))
--> "this string" contains "multiple quotes"

The result is the entire string, instead of "this string" what a lot of people would have expected. The reason is that the engine again consumed all characters, and then backtracks to find a match. This test string ended in a " so it immediately was satisfied and returned results. When using (".+?") it would have started at the first quote ", and keep matching the pattern until it finds another quote, and then return result. In his string, normally the same player will never occur, so it shouldn't be an issue, but I wanted to mention it anyway. You never know if it sends multiple sorts of statistics, which could mean the same player is in the string multiple times. Efficiency remains the more important point here though.

Also as you can see, I'm not using $+ thanks to the special behaviour of $n that can have text appended to it as long as it doesn't have a number or - in it, and thanks to the x modifier, which ignores unescaped whitespace in the expression, so those spaces before the $2's are ignored.

; Usage: $player(string,N)

alias player return $regml(pl,$regex(pl,$1,/\\player_ $2\\(.+?)\\frags_ $2\\/xi))

$player will return 0 in case there was no match, so you can easily use: if (!$player(string,N)). Since a nickname cannot be a number, there can never be a player "0" so that's not an issue either.


Gone.
Joined: Aug 2005
Posts: 525
S
Fjord artisan
OP Offline
Fjord artisan
S
Joined: Aug 2005
Posts: 525
Thanks Fiber for the well explained example. However this isn't concerning nicks on mIRC, but rather a game server where a nick can be any character or string of characters in no particular order.

Joined: Feb 2004
Posts: 2,019
Hoopy frood
Offline
Hoopy frood
Joined: Feb 2004
Posts: 2,019
Oh it returning 0 was a design choice due to the $regex embedded in the $regml, not related to the regular expression itself. You could have made the change easily yourself for it not to return 0, as it doesn't require knowledge of regex.

alias player if ($regex(pl,$1,/\\player_ $2\\(.+?)\\frags_ $2\\/xi)) return $regml(pl,1)

Now it returns nothing if no match was found, or it returns the player in case there is a match.

Btw, does that gameserver have some restrictions on player's names or can they be anything? They should atleast restrict the players names so that they can't be "player_<digits>" or "\player_digits\" etc. because that will become potentially annoying for matching.


Gone.
Joined: Aug 2005
Posts: 525
S
Fjord artisan
OP Offline
Fjord artisan
S
Joined: Aug 2005
Posts: 525
The game is Unreal Tournament. The only real character not allowed is a space, however any spaces in the name are automatically changed to underscores when you join a server. I think there are a few others which get displayed as a box or a ?, but for the most part you can use any string of characters.

Joined: Feb 2004
Posts: 2,019
Hoopy frood
Offline
Hoopy frood
Joined: Feb 2004
Posts: 2,019
I see.

Well then the people responsible for returning the results in the way that you showed in your initial post have overlooked something very important. There is no distinguishable aspect between a player's name, and the string that they send. This is a fundamental design mistake, but you shouldn't worry about it too much as it is out of your hands.

You could, if you felt like it, enhance the regex, because I do see that a lot of properties from a player are accompanied by the player's digit, so it is actually possible to create a more secure match, though I don't know if it's really worth it. In theory however, since a nickname can be *anything*, you will never be 100% sure, although in practice it would probably never fail.


Gone.
Joined: Sep 2003
Posts: 4,230
D
Hoopy frood
Offline
Hoopy frood
D
Joined: Sep 2003
Posts: 4,230
Yeah i thought that was a bit of a flaw I wonder if it could be used to an advantage, sicne i dont know what any of the items do anyway, ill just make a stupid one

player 9 enters the game, name = "DaveC.fragmaster\frags_1000000"

\player_9\DaveC.fragmaster\frags_1000000\frags_0\0\ping_5\ 95\team_5 etc etc etc

Wooohooo im top of the frag pool!

That or the game desyncs or something stupid [ or maybe nothing frown ]

Joined: Jan 2003
Posts: 2,523
Q
Hoopy frood
Offline
Hoopy frood
Q
Joined: Jan 2003
Posts: 2,523
Actually the format is a bit different:
\player_N\player name here\frags_N\frags value here\....

but yeah, it looks like it's breakable and probably exploitable. 'Probably' because it depends on the parsing script; it could be made greedy, meaning that when \player_N is encountered, it would look for the very last \frags_N in the string, where the 2 N's are the same (like regex does with quotes, eg /some/pattern/: here the actual pattern is "some/pattern"). This is where using .+ instead of .+? would be an advantage, even though the latter is more efficient. Even then though, the next player might have a "\frags_N" in his name (the same N from the previous player) or something of the sort... Without forbidding certain characters (or sequences) in player names, it would be hard to make it robust.


/.timerQ 1 0 echo /.timerQ 1 0 $timer(Q).com

Link Copied to Clipboard