| 08-06-2003, 11:47 PM | #91 |
I have made a test map that keeps track of the common handles (units, players, triggers) and some Blizzard handles. Just some warnings, as soon as I exit this map the game crashes. A good sign? No. Run this map **at your own risk**, i.e. it's not my fault if your harddrive reformatts itself, although I bet it's pretty unlikely. Just had to say that :) Scroll up and down in the multiboard with Uparrow/Downarrow, create 5 peasants by saying "create" and remove all units you have selected by saying "remove". The left column in the multiboard is a description of the handle/handles in the right column. Ah and if anyone intends to flame me for this then don't download the map ok? It's not like I'm proud of it. |
| 08-07-2003, 12:52 AM | #92 |
Sweet, I'll have to check this out as soon as I'm done coding my leap skill =) BTW, someone should make these (and I would if I had time): Code:
function NumDigits takes integer whichInteger returns integer function RetrieveDigit takes integer whichInteger, integer whichDigit returns integer And update: It looks like setting up a PHP/MySQL database for all this stuff wouldn't be that difficult. There's a few specific problems I foresee but it shouldn't be too terribly difficult... |
| 08-07-2003, 01:17 AM | #93 |
Useful if you are testing cinematics, and you forget to put a turn off cinematic mode... you have to restart warcraft3. It happens alot to me. :ggani: Code:
function QuitCinematic takes nothing returns nothing
call CinematicModeBJ( false, GetPlayersAll() )
call SetUserControlForceOn( GetPlayersAll() )
endfunction
function quitNow takes nothing returns nothing
local integer i = 0
local trigger qq = CreateTrigger( )
loop
exitwhen i > 11
call TriggerRegisterPlayerEventEndCinematic(qq, Player(i) )
set i = i + 1
endloop
call TriggerAddAction( qq, function QuitCinematic )
endfunctionAnother that always gives me problems is dialogs, I either forget to put a button, or set a title and thus another crash. Code:
function dialogInit takes nothing returns dialog
local dialog d = DialogCreate()
call DialogSetMessageBJ( d, "Dialog Init" )
call DialogAddButtonBJ( d, "Okay")
return d
endfunctionIf someone could make a grow function that would be cool. I see the prototype something like this: function grow takes widget w, real duration, real rate returns nothing |
| 08-07-2003, 01:51 PM | #94 |
Here is the dialog engine. I guess it's not super user-friendly, and I have only tested it for one player (I've tested different slots though.) It needs no user defined globals as it uses the trigger queue array for that. Still I don't think it would disturb the trigger queue as it places itself at the end of the limit of the queue. You might also want to check out Scripts\Cheats.j in War3x.mpq. It contains some interesting functions. EDIT: Added the bugfixed versions of ToUpper and ToLower |
| 08-07-2003, 07:52 PM | #95 |
@dataangel (and Peppar of course): Thanks for pointing out. When I was writing my function, I didn't take into account, that when using my functions, the trigger has to wait until it's completed. The only point we can argue is the thing about including blizzard.j functions or calling them. About benchmarking: Maybe there is a way to benchmark (it will be one, where you can't say whether the result is reliable or not). Peppar posted that WC3 terminates functions (even if they work fine), if they need too much time (I didn't know this before). Which amount of time is that?. I assume that this amount of time is always the same. So we can use this to benchmark something. Like make a loop, where you put the code to benchmark and count the number of executions. But I need to test that and maybe I can post tomorrow a benchmark function (but it won't be generic). |
| 08-08-2003, 03:57 PM | #96 |
@starcraftfreak: Benchmarking would be really helpful also when optimizing code. It would be really great if you could find a way how to do it! I'm not certain if the lifespan of a function is long enough, though. @dataangel: How are things going with the website? :P Code:
// New version, the old one locked up if s was "" or null
function ToLower takes string s returns string
local string lower = "abcdefghijklmnopqrstuvwxyz" // Modify all you want
local string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local integer i = 0
local integer n = 0
local string c = ""
local string a = ""
local string o = ""
loop
set c = SubString( s, i, i + 1 )
exitwhen c == "" or c == null
set n = 0
loop
set a = SubString( upper, n, n + 1 )
exitwhen a == "" or a == null
if ( a == c ) then
set c = SubString( lower, n, n + 1 )
exitwhen true
endif
set n = n + 1
endloop
set o = o + c
set i = i + 1
endloop
return o
endfunction
function ToUpper takes string s returns string
local string lower = "abcdefghijklmnopqrstuvwxyz" // Modify all you want
local string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
local integer i = 0
local integer n = 0
local string c = ""
local string a = ""
local string o = ""
loop
set c = SubString( s, i, i + 1 )
exitwhen c == "" or c == null
set n = 0
loop
set a = SubString( lower, n, n + 1 )
exitwhen a == "" or a == null
if ( a == c ) then
set c = SubString( upper, n, n + 1 )
exitwhen true
endif
set n = n + 1
endloop
set o = o + c
set i = i + 1
endloop
return o
endfunction
// Converts string str with a special base to integer
// Requires: ToLower
function S2IBase takes string str, integer base returns integer
local string charMap = SubString("0123456789abcdefghijklmnopqrstuvwxyz", 0, base)
local integer i = 0
local integer p = 0
local integer m = 1
local string c = null
local string a = null
local integer d = 0
local boolean t
if (base<2 or base>36) then
return 0
elseif (base==10) then
return S2I(str)
endif
loop
set c = ToLower(SubString(str, p, p+1))
exitwhen c=="" or c==null
set t = false
set d = 0
loop
set a = SubString(charMap, d, d+1)
exitwhen a=="" or a==null
if (a==c) then
set t = true
exitwhen true
endif
set d = d + 1
endloop
exitwhen not t
set p = p + 1
endloop
loop
exitwhen p==0
set c = ToLower(SubString(str, p-1, p))
set d = 0
loop
set a = SubString(charMap, d, d+1)
exitwhen a=="" or a==null or a==c
set d = d + 1
endloop
exitwhen a=="" or a==null
set i = i + d * m
set m = m * base
set p = p - 1
endloop
return i
endfunction
// Converts integer i to string with a special base
// Requires: Nothing
function I2SBase takes integer i, integer base returns string
local string charMap = "0123456789abcdefghijklmnopqrstuvwxyz"
local string str = ""
local integer n = i
local integer digit
if (base<2 or base>36) then
return null
elseif (base==10) then
return I2S(i)
endif
loop
set digit = ModuloInteger(n, base)
set n = n / base
set str = SubString(charMap, digit, digit+1) + str
exitwhen n==0
endloop
return str
endfunction
// Returns the amount of digits in a number
// Requires: strlen, I2SBase
function NumDigitsBase takes integer i, integer base returns integer
return strlen(I2SBase(i, base))
endfunction
// Returns the value of te n:th digit in i where digit 0 is the least
// significant digit
// Requires: strlen, I2SBase
function RetrieveDigitBase takes integer i, integer n, integer base returns integer
local string str = I2SBase(i, base)
local integer len = strlen(str)
return S2I(SubString(str, len-n-1, len-n))
endfunction
// Returns the value of the n:th digit in i where digit 0 is the most
// significant digit
// Requires: I2SBase
function RetrieveDigitReverseBase takes integer i, integer n, integer base returns integer
return S2I(SubString(I2SBase(i, base), n, n+1))
endfunction
//(decimal) Returns the amount of digits in a number
// Requires: strlen
function NumDigits takes integer i returns integer
return strlen(I2S(i))
endfunction
//(decimal) Returns the n:th digit in i where digit 0 is the least significant digit
// Requires: strlen
function RetrieveDigit takes integer i, integer n returns integer
local string str = I2S(i)
local integer len = strlen(str)
return S2I(SubString(str, len-n-1, len-n))
endfunction
//(decimal) Returns the n:th digit in i where digit 0 is the most significant digit
// Requires: Nothing
function RetrieveDigitReverse takes integer i, integer n returns integer
return S2I(SubString(I2S(i), n, n+1))
endfunction |
| 08-08-2003, 07:06 PM | #97 |
But you said, WC3 terminates a function if it needs too much time, right? Why do you know this? Is this amount always the same (for different functions)? I wrote a benchmark function that counts the executions and displays it in every execution and for different functions it stops executing them after a distinc amount of executions: DisplayTextToForce: 5000 (approx.) DisplayTextToPlayer: 8000 (approx.) But I can't really say if we can rely on these results. |
| 08-08-2003, 07:31 PM | #98 |
I think it is pretty reliable, when I tested this on the same function it always gave the exact same number of executions. I don't know if this differs from computer to computer though. I guess we can run a standardized simple test before running the test of the function we want to benchmark, and afterwards compare the results (preferrably with division) and get a number. |
| 08-09-2003, 07:37 PM | #99 |
Yes, that's a good idea. I suppose you tried to do the same as I did: Set up a function, that has a loop, that runs a function (the function to benchmark) until the timer is over. After some testing I discovered that the code after the loop is not executed. Then I read your post. Should I create these benchmark functions? @dataangel: I found a PHP software, that is quite useful for storing code snippets. You can find it here: http://php-csl.sourceforge.net But be aware that I had to hack it a bit (I can't PHP, but that was easy) so that the title of each code snippet can hold more than 20 chars. |
| 08-10-2003, 07:20 PM | #100 | |
Quote:
BTW dataangel, wouldn't it be better, if you move this thread to the AI/Jass Vault. Maybe then more Jass experts come to this thread. I think it's not very Map Development related (well, you can put every modding stuff into map development, but this is definitely a thread for the AI/Jass Vault. Benchmark results: EUT = executions until termination Function__________________EUT_____________Number of Parameters -------------------------------------------------------------------- IssuePointOrder___________16666___________4 IssuePointOrderLoc_________21427__________3 IssuePointOrderLocBJ_______11110__________3 IssuePointOrderLocBJDummy__7499__________3 Code:
function IssuePointOrderLocBJDummy takes unit whichUnit, string order, location whichLocation returns boolean
return IssuePointOrderLocBJ( whichUnit, order, whichLocation )
endfunctionFor the functions that need a location as parameter, I initialized a local location with the coordinates 0,0 before the loop. Here is an example of my benchmark trigger (I edited it to do the various tests): Code:
function Trig_Test_Actions takes nothing returns nothing
local location l
set l = Location(0,0)
set udg_i = 0
loop
call IssuePointOrderLoc(gg_unit_Hmkg_0000, "attack", l)
set udg_i = udg_i + 1
endloop
endfunction
//===========================================================================
function InitTrig_Benchmark takes nothing returns nothing
set gg_trg_Benchmark = CreateTrigger( )
call TriggerRegisterPlayerChatEvent( gg_trg_Benchmark, Player(0), "bench", true )
call TriggerAddAction( gg_trg_Benchmark, function Trig_Test_Actions )
endfunctionit doesn't execute the remaining actions. I have to tell you that I think, that we can't benchmark functions. When benchmarking the same function (or even some lines of Jass code) many times, you get always the same result. But I no longer believe that Warcraft III terminates a trigger, if it needs too much time, but rather, if it has too much "instructions" in it. That's why blizzard.j functions produce far lower results than common.j functions (intentionally I took a function, which belongs to type 1 of blizzard.j functions - "Dummy Functions"; read my explanation above in my long post). In some way blizzard.j functions have to be slower than those defined in common.j, as they call them. But I think they are not that slow as the results show. I said earlier that I think that Warcraft III terminates a function, if it has too much "instructions" (I don't really know how to express it) in it. I came up with this idea, when comparing the result for IssuePointOrder and IssuePointOrderLoc. Code:
native IssuePointOrder takes unit whichUnit, string order, real x, real y returns boolean native IssuePointOrderLoc takes unit whichUnit, string order, location whichLocation returns boolean You can see, that IssuePointOrder takes 4 parameters, while IssuePointOrder takes 3 parameters. In the game it makes no difference, whether you do the first or the second of the following Jass code in a trigger. The unit will make an attack-move to the point 0,0: First way: Code:
call IssuePointOrder(myunit,"attack",0,0) Second way: Code:
local location l set l = Location(0,0) call IssuePointOrderLoc(myunit,"attack",l) Now look at the results. You will wonder, the second one is faster (you have to take into account, that I did the initialization before the loop). Technically for IssuePointOrder are two parameters needed to tell the function the coordinates (0,0), for IssuePointOrderLoc one variable, that holds more data (so it must need more memory). Lets do a bit calculating: We multiply the EUT with the number of parameters and divide the result by the number of the function to be compared with: 16666 * 4 = 66664 / 3 = 22221.3 Now look at the EUT of the function to be compared with: 21427 These numbers are quite similiar. OK, I can't use this to prove anything, but look at the following: When we want to apply that on blizzard.j functions, we have to add the number of parameters of the blizzard.j function with the number of parameters of the function it calls (3+3 = 6). So: 11110 * 6 = 66660 / 3 = 22220 The result is again similiar. Now lets apply it for the dummy function I created: 7499 * 9 = 67491 / 3 = 22497 Again similiar. There is another thing, that is quite weird: The common.j functions need very much real time (our time), while the blizzard.j function needed less and the dummy function was the fastest. So time can certainly not the factor for Warcraft III to terminate a trigger. Maybe it IS the number of parameters. Or the number of parameters in a certain amount of time?! Now tell me either I'm mad or I'm quite right. I can't think of anything else. |
| 08-11-2003, 12:08 PM | #101 |
@starcraftfreak: You certainly made a huge effort with this issue. I'd like to thank you for taking the time. I think that you are right, and I am just as confused as you are on the timing issue. I would believe that the Jass2 interpreter sets the lifetime of a function (thread?) when it starts executing, and then at different key points decreases it, until it reaches zero. Where these key points are seems to be impossible to find out, as you have found out. As you said, this would mean that we cannot use this method to benchmark functions. Would there be any other way? The short lifetime of a function would ultimately mean that we cannot do it the 'normal' way, i.e. by running it a large amount of times sequentially and then get the mean execution time. |
| 08-11-2003, 02:46 PM | #102 |
This may or may not be relevant, but Bob Fitch (the Blizzard programmer who coded the JASS interpreter) once told me how Threads are managed in JASS (for AI scripts anyway): - JASS is compiled into a series of opcodes like any interpreted language (e.g., Java to bytecode) - The interpreter runs a thread for a certain number of opcodes or 1 second (which ever comes first) and then context switches to another thread. - The Sleep(N) function forces the current thread to yield. I would imagine that there is a dedicated thread that manages the trigger queue (in addition to the threads running the AI scripts, etc.). If it functions in the same way then it would explain why your "realtime" benchmarks are inconsistent -- depending on how fine-grained the "opcodes" are, they may take vastly different amounts of time to execute (e.g., "add" would be fast but "display_pixel" would be slow and "network_send" could have noticible latency). In addition, if it is waiting for the 1 sec timeout for context switching, you might get really wierd results since that is a very coarse grainularity. Off-topic, but I would also imagine the various "wait" functions used in triggers yeild the interpreter like the Sleep function in AI scripts do. This is probably the cause of all the (old and new) wait buginess. |
| 08-11-2003, 08:43 PM | #103 |
@Peppar: Before I did that (about a week or two before), I did something like that: Start a timer Make a loop for one or some functions, with an exitwhen TimerGetRemaining(t) == 0 I wondered why it never worked. Then I started to make DisplayTextToPlayer on every execution to display the remaining time. The result was the whole duration of the time the whole time. So that meant, that the timer did not go down. The reason for that is that the timer is interupted, when you call another function. The only thing we can do with what I posted in my last post is to check whether a trigger, that needs a long loop will run to it's end or not. You increase this "trigger-lifespan" when using common.j functions (which should also slightly faster in my opinion). |
| 08-12-2003, 05:28 PM | #104 |
I'm gonna make the site so people can submit functions as soon as Skull gives me ftp access (it's been 3 weeks... ://// ). Anyway, speaking of waits, somebody ought to figure out how to do a GroupWait(duration) function. That is, a wait that will work inside player and unit groups. Currently using one causes all actions below it not to execute. I've tried TriggerSleep, PolledWait, and even my own function using timers, but still none of them work. Edit: Also ought to add two-dimensional array index function, where you pass two indices you want and max size of second dimension. |
| 08-12-2003, 08:28 PM | #105 |
Will you use the software I told you, another one or some self-programmed stuff? |
