| 03-07-2009, 02:38 PM | #1 |
I know there are a few handle counters out there but they're simply just copied out from some test map or created for temporary usage. And since there isn't one at this site's Scripts section I decided to make one. It shows maximum handles reached and game time. The game time is optional and can be disabled if you would like. Refresh rate of the handle counter can be specified, as well as the leaderboard's colours. You have access to the leaderboard if you'd like to change/destroy it (HandleCounter_LEADERBOARD). This is useful in debug cases where you'd like to see if your map is leaking a lot of handles since it probes only the top of the free stack, otherwise it isn't really needed. Requires a vJASS compiler (JASS NewGen Pack). To implement just create a trigger named HandleCounter, convert it to custom script, delete everything and paste this text: (has 2 "versions" - for 1.23 and bellow and for 1.24 (and 1.24b) (due to the new GetHandleId() native and no return bug)) JASS://=========================================================================== // A simple handle counter that shows: // * Maximum handles reached // * Elapsed seconds // //=== FOR VERSION 1.23 AND BELLOW ONLY === // // The leaderboard can be accessed with // HandleCounter_LEADERBOARD // The clock (timer) can be accessed with // HandleCounter_CLOCK // // This could be useful only if you'd like to see // if your map is leaking a lot of handles. // Otherwise it isn't really needed. // // Requires a vJASS compiler to work. // // To use: // Create a trigger named HandleCounter, // convert to custom script and paste this. //=========================================================================== library HandleCounter initializer Init //=========================================================================== // Configurables //=========================================================================== globals // Leaderboard title/name. private constant string TITLE = "Handle Counter" // Text that represents the time. private constant string TIME_TEXT = "Elapsed Time:" // Should the leaderboard show time as well? True or false. private constant boolean SHOW_TIME = true // How often should the handle count update (of course this is not for the clock). private constant real UPDATE_PERIOD = 0.25 // Number of locations created for better counting result. private constant integer STACK_END_TRESHOLD = 15 // Player(0) = Player 1 (Red); Player(1) = Player 2 (Blue); etc. // Player color of the maximum handles text. private constant player MAX = Player(0) // Player color of the time text. private constant player TIME = Player(1) endglobals //=========================================================================== // Do not edit bellow this point unless you know what you're doing. //=========================================================================== globals public leaderboard LEADERBOARD public timer CLOCK private integer CURRENT_HANDLE = 0 private integer MAX_HANDLE = 0 private integer SECONDS = 0 endglobals private function L2I takes location loc returns integer return loc return 0 endfunction private function UpdateTime takes nothing returns nothing set SECONDS = SECONDS + 1 call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT) call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS) endfunction private function Callback takes nothing returns nothing local location array loc local integer a = 0 local integer index local integer prevIndex = 0 local integer next = 0 loop set loc[a] = Location(0., 0.) set index = L2I(loc[a]) - 0x100000 set a = a + 1 if index == prevIndex + 1 then set next = next + 1 else set next = 0 endif set prevIndex = index exitwhen next >= STACK_END_TRESHOLD endloop call LeaderboardSetItemValue(LEADERBOARD, 0, index - a) loop set a = a - 1 call RemoveLocation(loc[a]) set loc[a] = null exitwhen a <= 0 endloop endfunction private function Actions takes nothing returns nothing local timer tim = GetExpiredTimer() set LEADERBOARD = CreateLeaderboard() call LeaderboardSetLabel(LEADERBOARD, TITLE) call PlayerSetLeaderboard(GetLocalPlayer(), LEADERBOARD) call LeaderboardDisplay(LEADERBOARD, true) call TimerStart(tim, UPDATE_PERIOD, true, function Callback) call LeaderboardAddItem(LEADERBOARD, "Max Handles", 0, MAX) call LeaderboardSetSizeByItemCount(LEADERBOARD, 1) if SHOW_TIME then set CLOCK = CreateTimer() call TimerStart(CLOCK, 1., true, function UpdateTime) call LeaderboardAddItem(LEADERBOARD, "", 1, TIME) // Prevent leaderboard showing "1" during the first second. call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT) call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS) call LeaderboardSetSizeByItemCount(LEADERBOARD, 2) endif set tim = null endfunction private function Init takes nothing returns nothing call TimerStart(CreateTimer() , 0., false, function Actions) endfunction endlibrary JASS://=========================================================================== // A simple handle counter that shows: // * Maximum handles reached // * Elapsed seconds // //========== FOR VERSION 1.24+ ONLY ========== // // The leaderboard can be accessed with // HandleCounter_LEADERBOARD // The clock (timer) can be accessed with // HandleCounter_CLOCK // // This could be useful only if you'd like to see // if your map is leaking a lot of handles. // Otherwise it isn't really needed. // // Requires a vJASS compiler to work. // // To use: // Create a trigger named HandleCounter, // convert to custom script and paste this. //=========================================================================== library HandleCounter initializer Init //=========================================================================== // Configurables //=========================================================================== globals // Leaderboard title/name. private constant string TITLE = "Handle Counter" // Text that represents the time. private constant string TIME_TEXT = "Elapsed Time:" // Should the leaderboard show time as well? True or false. private constant boolean SHOW_TIME = true // How often should the handle count update (of course this is not for the clock). private constant real UPDATE_PERIOD = 0.25 // Number of locations created for better counting result. private constant integer STACK_END_TRESHOLD = 15 // Player(0) = Player 1 (Red); Player(1) = Player 2 (Blue); etc. // Player color of the maximum handles text. private constant player MAX = Player(0) // Player color of the time text. private constant player TIME = Player(1) endglobals //=========================================================================== // Do not edit bellow this point unless you know what you're doing. //=========================================================================== globals public leaderboard LEADERBOARD = null public timer CLOCK = null private integer CURRENT_HANDLE = 0 private integer MAX_HANDLE = 0 private integer SECONDS = 0 endglobals private function UpdateTime takes nothing returns nothing set SECONDS = SECONDS + 1 call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT) call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS) endfunction private function Callback takes nothing returns nothing local location array loc local integer a = 0 local integer index local integer prevIndex = 0 local integer next = 0 loop set loc[a] = Location(0., 0.) set index = GetHandleId(loc[a]) - 0x100000 set a = a + 1 if index == prevIndex + 1 then set next = next + 1 else set next = 0 endif set prevIndex = index exitwhen next >= STACK_END_TRESHOLD endloop call LeaderboardSetItemValue(LEADERBOARD, 0, index - a) loop set a = a - 1 call RemoveLocation(loc[a]) set loc[a] = null exitwhen a <= 0 endloop endfunction private function Actions takes nothing returns nothing local timer tim = GetExpiredTimer() set LEADERBOARD = CreateLeaderboard() call LeaderboardSetLabel(LEADERBOARD, TITLE) call PlayerSetLeaderboard(GetLocalPlayer(), LEADERBOARD) call LeaderboardDisplay(LEADERBOARD, true) call TimerStart(tim, UPDATE_PERIOD, true, function Callback) call LeaderboardAddItem(LEADERBOARD, "Max Handles", 0, MAX) call LeaderboardSetSizeByItemCount(LEADERBOARD, 1) if SHOW_TIME then set CLOCK = CreateTimer() call TimerStart(CLOCK, 1., true, function UpdateTime) call LeaderboardAddItem(LEADERBOARD, "", 1, TIME) // Prevent leaderboard showing "1" during the first second. call LeaderboardSetItemLabel(LEADERBOARD, 1, TIME_TEXT) call LeaderboardSetItemValue(LEADERBOARD, 1, SECONDS) call LeaderboardSetSizeByItemCount(LEADERBOARD, 2) endif set tim = null endfunction private function Init takes nothing returns nothing call TimerStart(CreateTimer() , 0., false, function Actions) endfunction endlibrary |
| 03-07-2009, 03:14 PM | #2 |
JASS:call TimerStart(GetExpiredTimer(), UPDATE_PERIOD, true, function Callback) The rest seems fine. |
| 03-07-2009, 03:36 PM | #3 |
> And forget about the part that pauses/destroys the timer. Right. Just give me a minute. EDIT: Ready. |
| 03-07-2009, 05:48 PM | #4 |
What about making it, to show the time in the usual way? 03:01:32 instead of 3:1:32 you should btw set loc to null and not leak the timer in init ~ |
| 03-07-2009, 05:58 PM | #5 |
> What about making it, to show the time in the usual way? Like, uh, how? > you should btw set loc to null What's the point, it's going to be reassigned anyway? > and not leak the timer in init I thought it gets used during the whole time since I start it again in Actions... and it runs forever. |
| 03-07-2009, 06:44 PM | #6 |
Usual time: 00:30:03 I think that would be a waste of resources, in fact, just show elapsed seconds :) |
| 03-07-2009, 07:33 PM | #7 |
You should mention that since this probes only the top of the free stack it is exact only for heavily leaking maps, though I'm sure it works pretty well in practice anyway. War3err achieves exactness by cheating. |
| 03-07-2009, 09:35 PM | #8 |
>Does not require anything. It requires a vJASS compiler, though I know your thinking systemwise :P |
| 03-08-2009, 09:09 AM | #9 |
> It requires a vJASS compiler, though I know your thinking systemwise :P Correct, I forgot about that. Though it should be obvious when someone sees the beginning "library". > You should mention that since this probes only the top of the free stack it is exact only for heavily leaking maps Done. > in fact, just show elapsed seconds Might be better since it wouldn't be used permanently in maps (I guess). Done as well. EDIT: I added a new picture :P |
| 03-09-2009, 02:51 PM | #10 |
You leak the timer used in Init if SHOW_TIME is set to false. If you fix that, it appears Vex liked it well enough that it can be approved. |
| 03-09-2009, 06:43 PM | #11 |
> You leak the timer used in Init if SHOW_TIME is set to false. Thanks for pointing it out. Will fix. EDIT: Is it fine now? |
| 03-09-2009, 07:01 PM | #12 |
Right now, the CURRENT_HANDLE is a rather meaningless value as it can recycle pretty much a random handle index; it is only useful for approximating the max handle index but it can take a while to do so. Have you considered instead creating multiple locations until a certain (calibratable) number of them get consecutive handle adresses, which would imply the top of the stack with greater certainty, then you could get the exact number of handles by subtracting the number of locations created from the max location handle id? It's more work for the computer but such a function wouldn't need to be run as often. |
| 03-09-2009, 07:42 PM | #13 |
From that part I understood that I should create more locations, right? How am I going to substract the number from all of them? I may need an example here since I'm not very familiar with handles and such... |
| 03-09-2009, 10:18 PM | #14 |
Hmm ...i created a handle counter for myself some time ago .. it displayed: -max handle counter (well the same as yours) -average handle counter of last X updates (i use 50 most of the time ..obviously the value of that X depends on the update interval) worked quite well for me.. |
| 03-10-2009, 12:10 AM | #15 | |
Quote:
JASS:globals private constant integer STACK_END_THRESHOLD = 5 endglobals private function Callback takes nothing returns nothing local integer consecutive=0 local integer i=0 local location array l local integer locIndex local integer prevIndex=0 loop set l[i] = Location(0., 0.) set locIndex = L2I(l[i]) - 0x100000 set i = i + 1 if locIndex = prevIndex + 1 then set consecutive = consecutive + 1 else set consecutive = 0 endif set prevIndex=locIndex exitwhen consecutive >= STACK_END_THRESHOLD endloop call LeaderboardSetItemValue(LEADERBOARD, 0, (locIndex - i)) loop set i = i - 1 call RemoveLocation(l[i]) set l[i] = null exitwhen i == 0 endloop endfunction The assumption is that once you fill all the "holes" (left behind by removed handles) in the handle stack and reach the "top", the new indexes that will get assigned to the locations after that will be consecutive, however before that point getting multiple consecutive handle ids is unlikely. This way, the handle id obtained in the end has a high probability (the higher the threshold, the higher the probability, but also the more overhead) of being the highest handle id, by subtracting from it the number of locations it took to get to it you get the exact total number of handles. |
