Crash when Compiling Verse Code

Summary

For the past 3 days we have been observing engine hard crashes every 1 to 3 times we compiled our verse code. We initially thought it was related to the v38.10 patch, but it seems to be something related to a very specific Verse issue. The strange thing is it has nothing to do with the logic or syntax of the Verse code, but rather the location of class definitions in the file.

Please select what you are reporting on:

Unreal Editor for Fortnite

What Type of Bug are you experiencing?

Verse

Steps to Reproduce

We’re uncertain how to reproduce this in a new project, but we know how to reproduce it in our project. Here is the exact code that is causing our project to crash after 2-3 compiles. Note that it’s specifically due to the existence of the two classes buff_regeneration_health and buff_regeneration_shield. If we delete either one, the project does not crash.

Ironically, the fix we identified was by moving those two class definitions to the top of the file (above buff_damage_multiplier), and all of a sudden our project no longer crashes. The only difference is the location of where in this file our classes are defined, no other code is changed.

using { /Fortnite.com/Characters }
using { /Fortnite.com/Game }
using { /Verse.org/Assets }
using { /Verse.org/Simulation }
using { Kini.Algo }
using { Kini.Extensions }

buff_damage_multiplier := class(buff, buffable_dealt_damage_listener):
    DisplayName<override><localizes>:message = "Damage Multiplier"
    Texture<override>:texture = Textures.T_UI_Damage

    var Multiplier:float = 0.0

    OnBuffableDealtDamage<override>(DamagedBuffable:buffable, DamageResult:damage_result):void =
        AdditionalDamage := DamageResult.Amount * Multiplier
        
        if (AdditionalDamage > 0.0, Character := DamagedBuffable.GetAgent().GetFortCharacter[]):
            Character.Damage of damage_args:
                Amount := AdditionalDamage
                Instigator := DamageResult.Instigator
                Source := option{Buffable}

buff_damage_over_time := class(buff):
    DisplayName<override><localizes>:message = "DOT"
    Texture<override>:texture = Textures.T_UI_Damage

    DamagePerSecond:float = 0.0
    Duration:float = 0.0
    Instigator:?game_action_instigator = false
    Source:?game_action_causer = false

    var<private> StartTime:float = 0.0

    GetWidgetTitle<override>():message =
        TitleMessage()
    
    OnAddedToBuffable<override>():void =
        (super:)OnAddedToBuffable()
        spawn{ResolveDamageOverTime()}

    HasExpired()<transacts><decides>:void =
        Duration > 0.0
        CurrentTime := GetSimulationElapsedTime()
        CurrentTime - StartTime > Duration

    ResolveDamageOverTime()<suspends>:void =
        Character := Buffable.GetAgent().WaitForFortCharacter()
        set StartTime = GetSimulationElapsedTime()
        loop:
            Sleep(1.0)
            if (IsCancelled?). break

            Character.Damage of damage_args:
                Amount := DamagePerSecond
                Instigator := Instigator
                Source := Source

            if (HasExpired[]). break

    TitleMessage<localizes>():message = "{DamagePerSecond}/s\n{Duration}s"

buff_regenerate_health := class(buff):
    var AmountPerSecond:float = 0.0
    
    GetWidgetTitle<override>():message =
        BuffMessage.ValuePerSecond(AmountPerSecond.Format(1))

    OnAddedToBuffable<override>():void =
        (super:)OnAddedToBuffable()
        spawn{ResolveRegenerate()}

    ResolveRegenerate()<suspends>:void =
        Character := Buffable.GetAgent().WaitForFortCharacter()
        loop:
            if (IsCancelled?). break

            if (not Character.IsDownButNotOut[]):
                MaxHealth := Character.GetMaxHealth()
                CurrentHealth := Character.GetHealth()
                if (CurrentHealth < MaxHealth):
                    Character.SetHealth(CurrentHealth + AmountPerSecond)
            
            Sleep(1.0)

buff_regenerate_shield := class(buff):
    var AmountPerSecond:float = 0.0
    
    GetWidgetTitle<override>():message =
        BuffMessage.ValuePerSecond(AmountPerSecond.Format(1))

    OnAddedToBuffable<override>():void =
        (super:)OnAddedToBuffable()
        spawn{ResolveRegenerate()}

    ResolveRegenerate()<suspends>:void =
        Character := Buffable.GetAgent().WaitForFortCharacter()
        loop:
            if (IsCancelled?). break

            if (not Character.IsDownButNotOut[]):
                MaxShield := Character.GetMaxShield()
                CurrentShield := Character.GetShield()
                if (CurrentShield < MaxShield):
                    Character.SetShield(CurrentShield + AmountPerSecond)
            
            Sleep(1.0)
            
buff_siphon := class(buff, buffable_dealt_damage_listener):
    DisplayName<override><localizes>:message = "Siphon"
    Texture<override>:texture = Textures.T_UI_Siphon
    
    var Percentage:float = 0.0
    
    GetWidgetTitle<override>():message =
        TitleMessage(Percentage)

    OnBuffableDealtDamage<override>(DamagedBuffable:buffable, DamageResult:damage_result):void =
        Agent := Buffable.GetAgent()
        if (Percentage > 0.0, DamageResult.Amount > 0.0, Character := Agent.GetFortCharacter[]):
            MaxHealth := Character.GetMaxHealth()
            CurrentHealth := Character.GetHealth()

            var Remainder:float = DamageResult.Amount * Percentage 

            if (CurrentHealth < MaxHealth):
                NewHealth := CurrentHealth + Remainder
                Character.SetHealth(NewHealth)
                set Remainder = NewHealth - MaxHealth

            if (Remainder > 0.0):
                MaxShield := Character.GetMaxShield()
                CurrentShield := Character.GetShield()
                if (CurrentShield < MaxShield):
                    Character.SetShield(CurrentShield + Remainder)
    
    TitleMessage<localizes>(Value:float):message = "+{Value}%"

buff_incendiary_ammo := class(buff, buffable_dealt_damage_listener):
    DisplayName<override><localizes>:message = "Incendiary Ammo"
    Texture<override>:texture = Textures.T_UI_Incendiary_Ammo

    var DamagePerSecond:float = 0.0
    var Duration:float = 0.0
    var Probability:float = 0.0

    OnBuffableDealtDamage<override>(DamagedBuffable:buffable, DamageResult:damage_result):void =
        if (Kini.Math.RollProbability[Probability]):
            Burn := buff_burn:
                Buffable := DamagedBuffable
                DamagePerSecond := DamagePerSecond
                Duration := Duration
                Instigator := DamageResult.Instigator
                Source := option{Buffable}
            Burn.Add()
    
    TitleMessage<localizes>():message = "{Probability}%\n{DamagePerSecond}/s\n{Duration}s"
        
buff_burn := class(buff_damage_over_time):
    DisplayName<override><localizes>:message = "Burn"
    Texture<override>:texture = Textures.T_UI_Incendiary_Ammo

    OnAddedToBuffable<override>():void =
        (super:)OnAddedToBuffable()

        Agent := Buffable.GetAgent()
        if (BuffManager := GetBuffManager[]):
            BuffManager.PowerupBurn.Pickup(Agent)

    OnRemovedFromBuffable<override>():void =
        (super:)OnRemovedFromBuffable()

        Agent := Buffable.GetAgent()
        if (BuffManager := GetBuffManager[]):
            BuffManager.PowerupBurnRemove.Pickup(Agent)

BuffMessage<public> := module:
    ValuePerSecond<public><localizes>(Value:string):message = "{Rate}/s"

Expected Result

We expect the engine not to crash due to the existence of these classes

Observed Result

It crashes 100% of the time after a maximum of 3 compiles

Platform(s)

PC UEFN

Island Code

Unpublished

Video

Note the crash around 0:24, this is when the 2 class definitions are lower in the file.

At 0:58, I cut/paste the 2 class definitions higher up in the same verse file, no other changes are made

At 1:55 I finish restarting the project after the crash, reopen VS Code to confirm the two class definitions are now at the top of the file, and then hit compile 5+ times without issue.

At 2:36, I cut/paste the 2 class definitions back down lower in the file to where it originally was, hit compile, and the editor immediately crashes.

Additional Notes

We spent nearly 8 hours troubleshooting this today to finally identify what was causing the crash, but the solution does not seem reasonable and makes us nervous that it will happen again. Any help would be appreciated, please do reach out and we can point you to our project and version number to reproduce it yourselves.

Hey @OKomili are we able to get some logs from you for this issue?

I went back to the project and tried to reproduce the issue following the exact steps from my recording and this is no longer happening even when I move the Verse code. Significant changes to our code base have been made since the temporary fix we implemented, plus the hotfix of v38.11 dropped, so it’s possible one or the other has addressed the issue.

Appreciate the follow up.

FORT-1018055 is ‘Closed’ as ‘Fixed’. The issue will be addressed in 38.11.