My workaround for the Token ID bug

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

Post Reply
taustinoc
Dragon
Posts: 518
Joined: Mon Aug 03, 2015 6:30 pm

My workaround for the Token ID bug

Post by taustinoc »

I've seen several discussions on this bug, and the only "fix" I've seen suggested is to copy the token, delete it, and paste it back in, to create a new token ID. That seems like the "right" fix, but in my case, I had about 70 tokens on the map, and 16 of them had bad token IDs. While I'm sure there's an easy way to identify them and do the copy/delete/paste thing, I couldn't figure it out, and I'm lazy and stubborn about it. I figured it'd be easier to find a workaround usable within the macro, and use the tokens as is. I haven't seen anything in the forum on a way to do that, so here's what has worked (100% success, so far) for me. This way, I only have to do this once, and in the future, I don't care if I get bad token IDs.

The token ID is, apparently, 32 randomly assigned alpha-numeric characters. The bug occurs when it happens that all of those characters are numeric, so the ID from, say, getTokens, is treated as a number instead of a string. Since token IDs always seem to start with eight zeros, those get trimmed, leaving a 24 character number. (Is that correct? Does the bug always manifest in the form of trimming leading zeros? If so, this should work very consistently.)

The following uses getTokens to get list of all tokens on the Token layer. It then scrolls through them with foreach, checking the length of the ID (because I'm not 100% certain that it will always be eight leading zeros, and there's no reason not to make this work for any arbitrary number), and does a for() to construct a pad. However, the pad can't be constructed with zeros, because it will only, ever, be a single zero, so it constructs the pad using the letter "a" instead. When that's the proper length, it then ads a non-numeric character at the end (exclamation point, in this case), then replaces all the a's with zeros. So it constructs a pad that looks like this: "00000000!", forcing it to be treated as a string.

That pad, in a substring to remove the "!", is then used directly in the switchToken command. This works.

The output of the whole thing is a table showing token.name (from switchToken, so that I know it's working), the reported token ID, and the corrected 32 character token ID, in a table.

Code: Select all

[h:TokenList = "<TABLE BORDER=1><TR><TD>Token Name</TD><TD>Reported ID</TD><TD>Corrected ID</TD></TR>"]
[h: ids = getTokens(",",'{layer:["TOKEN"]}'))]
[h,foreach(id, ids,""), code:
{
  [h:Pad = ""]
  [if(length(id) != 32), code:
  {
    [h:PadLength=32 - length(id)]
    [for(i,0,PadLength), h:Pad = add(Pad,"a")]
    [h:Pad = Pad + "!"]
    [h:Pad = replace(Pad,"a","0")]
  };
  {
    [h:PadLength=0]
  }]
  [switchToken(substring(Pad,0,PadLength) + id)]
  [TokenList = TokenList + "<TR><TD>" + token.name + "</TD><TD>" + id + "</TD><TD>" + substring(Pad,0,PadLength) + id + "</TD></TR>"]
}]
[h:TokenList = TokenList + "</TABLE>"]
[r:TokenList]
(This must be a trusted macro, of course.)

I've only used this in getTokens, but it should work with any other function that uses token IDs. I strongly suspect it could be made more elegant, starting, perhaps, with the pad starting off as just the exclamation point and adding zeros in front of it, thus eliminating the repace() step), but this works for me.

I think it should be possible to put this whole thing into a user defined function, and use the output of that in switchToken, but that process may trim off leading zeros, too. I haven't tried yet. I'm fairly certain the complete, corrected token ID with a ! at the end could be stuffed into a JSON array, and the json.get inside a substring could be used. But I haven't tested that, either.

Does any of this make sense to anyone who actually knows what they're doing?

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: My workaround for the Token ID bug

Post by aliasmask »

If you still have tokens with numeric ids can you run getTokens("json") and then see if the ids are stored with or without the leading zeroes.

Code: Select all

[H: allTokens = getTokens("json",json.set("{}","layer","token"))]
[dialog("D"):{<pre>[R: json.indent(allTokens)]</pre>}]

taustinoc
Dragon
Posts: 518
Joined: Mon Aug 03, 2015 6:30 pm

Re: My workaround for the Token ID bug

Post by taustinoc »

json.indent shows the true Token ID, inside of quotes.

However, when I do a json.get for a particular one, I get the truncated one.

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: My workaround for the Token ID bug

Post by aliasmask »

Can you do this:

Code: Select all

[h: newId = "t" + json.get(allTokens,index)]
Does it remove the leading 0's? I think as long as you don't assign the id directly to a variable it will preserve the original string. I wonder how foreach will handle the ids. For example,

Code: Select all

[foreach(id,allTokens), code: {
   [H: switchToken(id)]
   [R: token.name]
}]

taustinoc
Dragon
Posts: 518
Joined: Mon Aug 03, 2015 6:30 pm

Re: My workaround for the Token ID bug

Post by taustinoc »

aliasmask wrote:Can you do this:

Code: Select all

[h: newId = "t" + json.get(allTokens,index)]
[/code]
t791438290914000000000000

(Which is to say, the json.get is stripping off the leading zeros.)
aliasmask wrote:
Does it remove the leading 0's? I think as long as you don't assign the id directly to a variable it will preserve the original string. I wonder how foreach will handle the ids. For example,

Code: Select all

[foreach(id,allTokens), code: {
   [H: switchToken(id)]
   [R: token.name]
}]
This errors out.

BTW, I've also tried using string(id) (tried it just now in switchToken), but by the time it gets into the id variable, the zeros are already stripped.

I've also tried using strFormat to force 32 digits, and that errored out, too, but I'm not 100% confident that I did it right. There appears to be a formatting option to force numbers to be a certain width, filled with leading zeros, but it either doesn't work or I can't figure it out.
0 Numeric digits indicates the field width for this format marker. If digits starts with a literal 0 (zero), the numeric value being formatted will be zero-filled within the field instead of space-filled. If the format type is floating point, this field may contain a decimal point and additional digits to signify the number of digits after the decimal in the output.


If I read this right, this:

Code: Select all

[r:strformat(id, "%00000000000000000000000000000000")
should force any number to be 32 digits long, but it doesn't seem to do anything.

So far, the only thing I can get to return all numbers with leading zeros is the substring function used directly in the switchToken command.

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: My workaround for the Token ID bug

Post by aliasmask »

I think the format is %32d but in order use it you have to make sure isNumber is true.

taustinoc
Dragon
Posts: 518
Joined: Mon Aug 03, 2015 6:30 pm

Re: My workaround for the Token ID bug

Post by taustinoc »

Ok, first, the correct syntax is:

Code: Select all

[strformat("%032d", id)]
with 32 being the width. The 0 in front is necessary to make it fill in the leading zeros. Otherwise, this basically does nothing (though I supposed if the number is longer than the width it will probably truncate it).

I think it would be a good idea to include that as an example in the wiki, which is otherwise extremely vague on that particular option. It sort of vaguely indicates that a 0 is involved somehow, but that's about all I got out of it.

Second,

Code: Select all

[if(isNumber(id)): switchToken(strformat("%032d", id));switchToken(id)]
works. I guess one single line of code replacing 17 lines is, indeed, more elegant. This six lines:

Code: Select all

[h: ids = getTokens(",",'{layer:["TOKEN"]}'))]
[foreach(id, ids,""), code:
{
  [h,if(isNumber(id)): switchToken(strformat("%032d", id));switchToken(id)]<BR>
  [r:token.name]
}]
will list all tokens on the token layer, broken IDs or not.

Thank you for the help. I couldn't have done this without you. Assuming that the bug always manifests as leading zeros being stripped off (and it sure likes like it does to me), so far as I'm concerned, this fixes it, period.

User avatar
Full Bleed
Demigod
Posts: 4736
Joined: Sun Feb 25, 2007 11:53 am
Location: FL

Re: My workaround for the Token ID bug

Post by Full Bleed »

taustinoc wrote:Assuming that the bug always manifests as leading zeros being stripped off (and it sure likes like it does to me), so far as I'm concerned, this fixes it, period.
isNumber() seems to think id's that start with "0E" after the leading 8 0's are numbers.

I made 726 copies of a token and using the isNumber() method it found the following "broken" ids.

000000000E2952549508000000000000
000000000E3552546216000000000000
000000000E3552546916000000000000


Btw... I seem to remember the BoT having a function to find broken ID's... maybe Wolph will chime in.
Maptool is the Millennium Falcon of VTT's -- "She may not look like much, but she's got it where it counts."

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: My workaround for the Token ID bug

Post by aliasmask »

So, instead of isNumber just use matches(id,"\\d+").

Another way is to just check the length of string and make sure it's 32 characters, but it can't be done with one function and the if() roll option doesn't like that. The only reason I suggest this is because if the id starts with a non-zero digit it is a valid id, but could potentially give a false negative. But I don't think I've ever seen a token id that didn't start with 0.

Code: Select all

[H: badId = if(length(string(id))) != 32,1,0)]

taustinoc
Dragon
Posts: 518
Joined: Mon Aug 03, 2015 6:30 pm

Re: My workaround for the Token ID bug

Post by taustinoc »

Full Bleed wrote:isNumber() seems to think id's that start with "0E" after the leading 8 0's are numbers.
I would imagine it thinks that's scientific notation. Does my workaround make it usable?
Last edited by taustinoc on Thu Aug 04, 2016 7:52 pm, edited 1 time in total.

taustinoc
Dragon
Posts: 518
Joined: Mon Aug 03, 2015 6:30 pm

Re: My workaround for the Token ID bug

Post by taustinoc »

aliasmask wrote:So, instead of isNumber just use matches(id,"\\d+").

Another way is to just check the length of string and make sure it's 32 characters, but it can't be done with one function and the if() roll option doesn't like that. The only reason I suggest this is because if the id starts with a non-zero digit it is a valid id, but could potentially give a false negative. But I don't think I've ever seen a token id that didn't start with 0.

Code: Select all

[H: badId = if(length(string(id))) != 32,1,0)]
If it starts with a non-zero number, I don't think that would be an issued with switchToken, because it would still be 32 characters, which appears to be all that matters.

Is there any way to set a token ID to a specific value? Then some comprehensive testing could be done.


Post Reply

Return to “Macros”