• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Crash before map loading

Status
Not open for further replies.

rgf

rgf

Level 3
Joined
Dec 27, 2008
Messages
21
Hey everyone,

I have been facing an issue for two weeks now, when I save the map it compiles fine but when I try to test it, it crashes even before the loading screen. When I try to access the map ingame, the game crashes at the moment it is supposed to display player slots and map description.

In the map I use a custom handler system for managing unit events and when I tried to locate the issue, it seems that when I deactivate some particular lines it works fine. Plus the bug doesn't seem to occur before a certain handler limit (if I remove one handler in the code, no matter which, it doesn't bug anymore) although I'm not sure.
The system duplicate code for each handler (through modules and macros) but it still feels weird given that the line (which is duplicated) I deactivate is not executed at the initialization of the map (it can be but I tried without the init function and it still bugs, I have to remove the line from the code for it not to bug).
I even tried to empty the function called by the line and it still bugs.

So I really don't understand what is going on with this bug.

I give you the link for the map's script (20 Mo) : war3map.j
I also tried to recompile this script with jasshelper but it doesn't display any problems.

Help would be very appreciated, I've been working on this map for years and it is really frustrating not to be able to solve this bug.
I can give you the map in PM if you need it.
(And excuse my english, I'm french).

Thank you !
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,192
I doubt WC3's JASS compiler can parse a 20 MB map script... You might want to try optimizing it to be smaller.

It is also possible JassHelper and other third party tools cannot properly build a 20 MB JASS script. This is sort of outside their design/testing.
 
  • Like
Reactions: rgf

rgf

rgf

Level 3
Joined
Dec 27, 2008
Messages
21
Actually it worked pretty well before I add the few last lines I added, but I think it might be something else given when I delete some other and more voluminous part of code it doesn't stop bugging, but when I remove some specific lines the bug vanish.
I also tried removing all new lines (and comments also) down to 14 Mo and it still doesn't work.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I have experienced map crashes with a script that contains certain characters or character sequences when i tried building an ascii library.
The jasshelper thought it was fine, but the game crashed on perfectly fine code.
It just couldnt handle certain characters for some reason.

I cant see the script atm as i am on mobile and dont want to download 20mb here :D
But i will take a look at it when i get home.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I guess you are having problems with function inlining.

If a function follows a few (very specific) rules, such as,
- the function contains only one line of code
- the function uses its parameters in the same order as they are passed in
etc
the function is inlined (so it will save a function call).

However, there are a few things it doesnt take into account.
I found one when I made my Event library.
JASS:
function doSomething takes nothing returns nothing
    call CreateUnit(Player(0), 'hfoo', 0, 0, 0)
endfunction

function addEventListener takes code func returns triggercondition
    return TriggerAddCondition(myTrigger, Condition(func))
endfunction

function foo takes nothing returns nothing
    //... do some stuff
    
    call addEventListener(function doSomething)
    
    //... do some other stuff
endfunction
The function TriggerAddCondition() takes in a boolexpr instead of a code, so this compiles correctly.
A code function that is converted to a boolexpr will always return false (as it doesnt return true logic wise) so the trigger will never run its actions, but we only use conditions anyway.
The JASSHelper cannot know that the return value of "code" is a boolean... in this case it could, but in the case of reading it from a global variable, it couldnt in most cases.
So it doesnt complain about it.

However, because the function addEventListener() follows the rules of inline functions, it gets inlined. (This does not happen if you compile the map in debug mode as inlining is turned off in it.)
The compiled code will look like this:
JASS:
function doSomething takes nothing returns nothing
    call CreateUnit(Player(0), 'hfoo', 0, 0, 0)
endfunction

function foo takes nothing returns nothing
    //... do some stuff
    
    call TriggerAddCondition(myTrigger, Condition(function doSomething)) // INLINED!!
    
    //... do some other stuff
endfunction
In this case, the internal JASSHelper of WC3 checks the script for any suspicious stuff etc, and it sees that you are using a function that doesnt return a boolean as condition, so it will crash the map.

A few of the instances that were found are:
s__Death_BuffStack_onDeindexEvent
s__DamagedStack_onDeindexEvent
s__DamageStack_onDeindexEvent
s__DamageHandler_FireDamage
s__DamagedHandler_FireDamaged
s__Bonus_onDeindexEvent
s__Status_onIndexEvent
s__Unit_onIndexEvent
s__Unit_onDeindexEvent
s__DeathAroundStack_onDeindexEvent
s__CastAroundStack_onDeindexEvent
s__AttackAroundStack_onDeindexEvent
s__ImmunedAroundStack_onDeindexEvent
s__ImmunedAroundHandler_FireImmunedAround
s__UnimmunedAroundStack_onDeindexEvent
s__UnimmunedAroundHandler_FireUnimmunedAround
s__SpellSucceedAroundStack_onDeindexEvent
s__SpellSucceedAroundHandler_FireSpellSucceedAround
You can search for them in the war3map.j and compare them what they should have been.
I am missing the // INLINED!! stuff which I ussually expect after inlined functions, but I removed some stuff from the file to make it easier to process.
(Which means 1.619.048 less lines.)

EDIT:
This may not be the entire problem, but at least will remain being a problem until it is solved.
 

Attachments

  • war3map2.j
    13.6 MB · Views: 68
  • Like
Reactions: rgf

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
I guess you are having problems with function inlining.

If a function follows a few (very specific) rules, such as,
- the function contains only one line of code
- the function uses its parameters in the same order as they are passed in
etc
the function is inlined (so it will save a function call).

However, there are a few things it doesnt take into account.
I found one when I made my Event library.
JASS:
function doSomething takes nothing returns nothing
    call CreateUnit(Player(0), 'hfoo', 0, 0, 0)
endfunction

function addEventListener takes code func returns triggercondition
    return TriggerAddCondition(myTrigger, Condition(func))
endfunction

function foo takes nothing returns nothing
    //... do some stuff
 
    call addEventListener(function doSomething)
 
    //... do some other stuff
endfunction
The function TriggerAddCondition() takes in a boolexpr instead of a code, so this compiles correctly.
A code function that is converted to a boolexpr will always return false (as it doesnt return true logic wise) so the trigger will never run its actions, but we only use conditions anyway.
The JASSHelper cannot know that the return value of "code" is a boolean... in this case it could, but in the case of reading it from a global variable, it couldnt in most cases.
So it doesnt complain about it.

However, because the function addEventListener() follows the rules of inline functions, it gets inlined. (This does not happen if you compile the map in debug mode as inlining is turned off in it.)
The compiled code will look like this:
JASS:
function doSomething takes nothing returns nothing
    call CreateUnit(Player(0), 'hfoo', 0, 0, 0)
endfunction

function foo takes nothing returns nothing
    //... do some stuff
 
    call TriggerAddCondition(myTrigger, Condition(function doSomething)) // INLINED!!
 
    //... do some other stuff
endfunction
In this case, the internal JASSHelper of WC3 checks the script for any suspicious stuff etc, and it sees that you are using a function that doesnt return a boolean as condition, so it will crash the map.

A few of the instances that were found are:
s__Death_BuffStack_onDeindexEvent
s__DamagedStack_onDeindexEvent
s__DamageStack_onDeindexEvent
s__DamageHandler_FireDamage
s__DamagedHandler_FireDamaged
s__Bonus_onDeindexEvent
s__Status_onIndexEvent
s__Unit_onIndexEvent
s__Unit_onDeindexEvent
s__DeathAroundStack_onDeindexEvent
s__CastAroundStack_onDeindexEvent
s__AttackAroundStack_onDeindexEvent
s__ImmunedAroundStack_onDeindexEvent
s__ImmunedAroundHandler_FireImmunedAround
s__UnimmunedAroundStack_onDeindexEvent
s__UnimmunedAroundHandler_FireUnimmunedAround
s__SpellSucceedAroundStack_onDeindexEvent
s__SpellSucceedAroundHandler_FireSpellSucceedAround
You can search for them in the war3map.j and compare them what they should have been.
I am missing the // INLINED!! stuff which I ussually expect after inlined functions, but I removed some stuff from the file to make it easier to process.
(Which means 1.619.048 less lines.)

EDIT:
This may not be the entire problem, but at least will remain being a problem until it is solved.


That Filter usage was deemed safe in recent patches, which is also why i removed the check from pjass (can still do it via +filter). And afaik it never crashed the game but merely caused a desync.
The only other problem i found was some integer/real returnbug but it was far away from main i think.
Normally if wc3 crashes it means you have an endless loop of TriggerEvals/TriggerExecutes/ExecuteFunc/etc.
If it crashes very early in the map and you can't use ingame messages use preload to write debug messages into some file.


e: Hmm, it crashes on map selection? That's a problem of config then, no? Or maybe anything else corrupt.
 
Last edited:
  • Like
Reactions: rgf

rgf

rgf

Level 3
Joined
Dec 27, 2008
Messages
21
I guess you are having problems with function inlining.

If a function follows a few (very specific) rules, such as,
- the function contains only one line of code
- the function uses its parameters in the same order as they are passed in
etc
the function is inlined (so it will save a function call).

However, there are a few things it doesnt take into account.
I found one when I made my Event library.
JASS:
function doSomething takes nothing returns nothing
    call CreateUnit(Player(0), 'hfoo', 0, 0, 0)
endfunction

function addEventListener takes code func returns triggercondition
    return TriggerAddCondition(myTrigger, Condition(func))
endfunction

function foo takes nothing returns nothing
    //... do some stuff
  
    call addEventListener(function doSomething)
  
    //... do some other stuff
endfunction
The function TriggerAddCondition() takes in a boolexpr instead of a code, so this compiles correctly.
A code function that is converted to a boolexpr will always return false (as it doesnt return true logic wise) so the trigger will never run its actions, but we only use conditions anyway.
The JASSHelper cannot know that the return value of "code" is a boolean... in this case it could, but in the case of reading it from a global variable, it couldnt in most cases.
So it doesnt complain about it.

However, because the function addEventListener() follows the rules of inline functions, it gets inlined. (This does not happen if you compile the map in debug mode as inlining is turned off in it.)
The compiled code will look like this:
JASS:
function doSomething takes nothing returns nothing
    call CreateUnit(Player(0), 'hfoo', 0, 0, 0)
endfunction

function foo takes nothing returns nothing
    //... do some stuff
  
    call TriggerAddCondition(myTrigger, Condition(function doSomething)) // INLINED!!
  
    //... do some other stuff
endfunction
In this case, the internal JASSHelper of WC3 checks the script for any suspicious stuff etc, and it sees that you are using a function that doesnt return a boolean as condition, so it will crash the map.

A few of the instances that were found are:
s__Death_BuffStack_onDeindexEvent
s__DamagedStack_onDeindexEvent
s__DamageStack_onDeindexEvent
s__DamageHandler_FireDamage
s__DamagedHandler_FireDamaged
s__Bonus_onDeindexEvent
s__Status_onIndexEvent
s__Unit_onIndexEvent
s__Unit_onDeindexEvent
s__DeathAroundStack_onDeindexEvent
s__CastAroundStack_onDeindexEvent
s__AttackAroundStack_onDeindexEvent
s__ImmunedAroundStack_onDeindexEvent
s__ImmunedAroundHandler_FireImmunedAround
s__UnimmunedAroundStack_onDeindexEvent
s__UnimmunedAroundHandler_FireUnimmunedAround
s__SpellSucceedAroundStack_onDeindexEvent
s__SpellSucceedAroundHandler_FireSpellSucceedAround
You can search for them in the war3map.j and compare them what they should have been.
I am missing the // INLINED!! stuff which I ussually expect after inlined functions, but I removed some stuff from the file to make it easier to process.
(Which means 1.619.048 less lines.)

EDIT:
This may not be the entire problem, but at least will remain being a problem until it is solved.

I've never experienced crashes or desync because of these, the functions are executed properly usually, are you sure this is still a "valid" bug ?

Actually I think I have found the problem !
At first I thought it came from the config function but it seems like it comes from the main function.
The first line of the main function is :
JASS:
call ExecuteFunc("jasshelper__initstructs3890076765")

And this is the line which initializes all the triggers used for methods call inlining.
Here is the start of the function :
JASS:
function jasshelper__initstructs3890076765 takes nothing returns nothing
set st__Magnetize_startLoop=CreateTrigger()
call TriggerAddCondition(st__Magnetize_startLoop,Condition( function sa__Magnetize_startLoop))
set st__Magnetize_instanciate=CreateTrigger()
call TriggerAddCondition(st__Magnetize_instanciate,Condition( function sa__Magnetize_instanciate))
set st__Magnetize_UnitToStruct=CreateTrigger()
call TriggerAddCondition(st__Magnetize_UnitToStruct,Condition( function sa__Magnetize_UnitToStruct))
set st__Magnetize_onDeindex=CreateTrigger()
call TriggerAddCondition(st__Magnetize_onDeindex,Condition( function sa__Magnetize_onDeindex))
set st__Magnetize_AddSpecificMethod=CreateTrigger()
call TriggerAddCondition(st__Magnetize_AddSpecificMethod,Condition( function sa__Magnetize_AddSpecificMethod))
set st__Magnetize_addMethod=CreateTrigger()
call TriggerAddCondition(st__Magnetize_addMethod,Condition( function sa__Magnetize_addMethod))
set st__Magnetize__staticgetindex=CreateTrigger()
call TriggerAddCondition(st__Magnetize__staticgetindex,Condition( function sa__Magnetize__staticgetindex))
set st__Magnetize_addHandler=CreateTrigger()
call TriggerAddCondition(st__Magnetize_addHandler,Condition( function sa__Magnetize_addHandler))
set st__PullForce_endUnit=CreateTrigger()
call TriggerAddCondition(st__PullForce_endUnit,Condition( function sa__PullForce_endUnit))
set st__PullForce_endEffect=CreateTrigger()
call TriggerAddCondition(st__PullForce_endEffect,Condition( function sa__PullForce_endEffect))
set st__PullForce_addUnit=CreateTrigger()
call TriggerAddCondition(st__PullForce_addUnit,Condition( function sa__PullForce_addUnit))
set st__Tentacle_startLoop=CreateTrigger()
call TriggerAddCondition(st__Tentacle_startLoop,Condition( function sa__Tentacle_startLoop))

And it went on and on for 6966 lines of this. I think this was just too much operations for one thread.
Every handler I declared generated many triggers like those and at some point there was too much handlers and too much triggers. So now I'm tracking method calls before their declaration to reduce the number of initialized triggers and it seems to do the trick !
Thank you all for your help and time ! +rep
 
Status
Not open for further replies.
Top