• 🏆 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!

Jungle Trolls - desync

Status
Not open for further replies.
Level 11
Joined
Apr 11, 2006
Messages
338
Hello, guys,
I'm the developer of Jungle Trolls Reborn - quite a hardcore survival game created more than eight years ago and frequently updated each few months since then.

The previous version of the game - JTR 5.5, was partially converted to JASS by the other developer, sadly not active anymore. The problem is that since then there is a desynchronisation at game start: while bot hosting at the moment the first player chooses game difficulty and all starting triggers are ran, some of the players get desynchronized. The more players in the game, the more desyncs (average 1 desync per 4 players).
Reverting back to GUI as it was is not an option - the game had too many interconnected improvements, so I must work with what I have.

I am fully aware that a desync issue may have many reasons behind it. I have studied similar topics (1, 2, 3), but I can't find a solution in them.
I'm not using GetLocalPlayer function.

As you may see, testing of this issue is quite impossible: I have to do the change, bot host a multiplayer game, gather at least 4-6 players and see what it happens - for every try to fix it. That's why I need help from an experienced JASS developer.

I suspect that something in this trigger might be wrong:
JASS:
function Trig_Player_ini_Func001C takes nothing returns boolean
    if ( not ( udg_MB_ModeAction == "Yes" ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func002C takes nothing returns boolean
    if ( not ( udg_MB_ModePlenty == "Yes" ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func004C takes nothing returns boolean
    if ( not ( udg_TeamNumber[1] > 0 ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func005C takes nothing returns boolean
    if ( not ( udg_TeamNumber[2] > 0 ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func006C takes nothing returns boolean
    if ( not ( udg_TeamNumber[3] > 0 ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func008Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func008C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(0)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func010Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func010C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(1)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func012Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func012C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(2)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func014Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func014C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(3)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func016Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func016C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(4)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func018Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func018C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(5)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func020Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func020C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(6)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func022Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func022C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(7)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func024Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func024C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(8)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func026Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func026C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(9)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func028Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func028C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(10)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func030Func006002 takes nothing returns nothing
    call RemoveUnit( GetEnumUnit() )
endfunction

function Trig_Player_ini_Func030C takes nothing returns boolean
    if ( not ( GetPlayerSlotState(Player(11)) == PLAYER_SLOT_STATE_PLAYING ) ) then
        return false
    endif
    return true
endfunction

function Trig_Player_ini_Func045A takes nothing returns nothing
    call ForceUICancelBJ( GetEnumPlayer() )
endfunction

function Trig_Player_ini_Actions takes nothing returns nothing
    local integer i = 0
    if ( Trig_Player_ini_Func001C() ) then
    else
        call DestroyTrigger( gg_trg_Action_Mode_JASS )
    endif
    if ( Trig_Player_ini_Func002C() ) then
    else
        call DestroyTrigger( gg_trg_Plenty_Mode_JASS )
        call TriggerRegisterTimerEventSingle( gg_trg_StopItemDrop_and_Visibility, 5000.00 )
    endif
    // Set Totem Kit for Teams
    if ( Trig_Player_ini_Func004C() ) then
        call SetItemInvulnerableBJ( udg_TotemKit[1], true )
    else
        call RemoveItem( udg_TotemKit[1] )
        set udg_TotemOwned[1] = true
    endif
    if ( Trig_Player_ini_Func005C() ) then
        call SetItemInvulnerableBJ( udg_TotemKit[2], true )
    else
        call RemoveItem( udg_TotemKit[2] )
        set udg_TotemOwned[2] = true
    endif
    if ( Trig_Player_ini_Func006C() ) then
        call SetItemInvulnerableBJ( udg_TotemKit[3], true )
    else
        call RemoveItem( udg_TotemKit[3] )
        set udg_TotemOwned[3] = true
    endif
    // Player1
    if ( Trig_Player_ini_Func008C() ) then
        call PanCameraToTimedLocForPlayer( Player(0), GetPlayerStartLocationLoc(Player(0)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0338, Player(0) )
    else
        set udg_PactWithTheDevil[1] = true
        set udg_Hero_Troll[1] = gg_unit_h009_0340
        call KillUnit( gg_unit_h009_0340 )
        call RemoveUnit( gg_unit_h017_0338 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(0)), function Trig_Player_ini_Func008Func006002 )
    endif
    // Player2
    if ( Trig_Player_ini_Func010C() ) then
        call PanCameraToTimedLocForPlayer( Player(1), GetPlayerStartLocationLoc(Player(1)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0066, Player(1) )
    else
        set udg_PactWithTheDevil[2] = true
        set udg_Hero_Troll[2] = gg_unit_h009_0341
        call KillUnit( gg_unit_h009_0341 )
        call RemoveUnit( gg_unit_h017_0066 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(1)), function Trig_Player_ini_Func010Func006002 )
    endif
    // Player3
    if ( Trig_Player_ini_Func012C() ) then
        call PanCameraToTimedLocForPlayer( Player(2), GetPlayerStartLocationLoc(Player(2)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0097, Player(2) )
    else
        set udg_PactWithTheDevil[3] = true
        set udg_Hero_Troll[3] = gg_unit_h009_0342
        call KillUnit( gg_unit_h009_0342 )
        call RemoveUnit( gg_unit_h017_0097 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(2)), function Trig_Player_ini_Func012Func006002 )
    endif
    // Player4
    if ( Trig_Player_ini_Func014C() ) then
        call PanCameraToTimedLocForPlayer( Player(3), GetPlayerStartLocationLoc(Player(3)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0150, Player(3) )
    else
        set udg_PactWithTheDevil[4] = true
        set udg_Hero_Troll[4] = gg_unit_h009_0343
        call KillUnit( gg_unit_h009_0343 )
        call RemoveUnit( gg_unit_h017_0150 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(3)), function Trig_Player_ini_Func014Func006002 )
    endif
    // Player5
    if ( Trig_Player_ini_Func016C() ) then
        call PanCameraToTimedLocForPlayer( Player(4), GetPlayerStartLocationLoc(Player(4)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0238, Player(4) )
    else
        set udg_PactWithTheDevil[5] = true
        set udg_Hero_Troll[5] = gg_unit_h009_0344
        call KillUnit( gg_unit_h009_0344 )
        call RemoveUnit( gg_unit_h017_0238 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(4)), function Trig_Player_ini_Func016Func006002 )
    endif
    // Player6
    if ( Trig_Player_ini_Func018C() ) then
        call PanCameraToTimedLocForPlayer( Player(5), GetPlayerStartLocationLoc(Player(5)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0204, Player(5) )
    else
        set udg_PactWithTheDevil[6] = true
        set udg_Hero_Troll[6] = gg_unit_h009_0345
        call KillUnit( gg_unit_h009_0345 )
        call RemoveUnit( gg_unit_h017_0204 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(5)), function Trig_Player_ini_Func018Func006002 )
    endif
    // Player7
    if ( Trig_Player_ini_Func020C() ) then
        call PanCameraToTimedLocForPlayer( Player(6), GetPlayerStartLocationLoc(Player(6)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0218, Player(6) )
    else
        set udg_PactWithTheDevil[7] = true
        set udg_Hero_Troll[7] = gg_unit_h009_0346
        call KillUnit( gg_unit_h009_0346 )
        call RemoveUnit( gg_unit_h017_0218 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(6)), function Trig_Player_ini_Func020Func006002 )
    endif
    // Player8
    if ( Trig_Player_ini_Func022C() ) then
        call PanCameraToTimedLocForPlayer( Player(7), GetPlayerStartLocationLoc(Player(7)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0229, Player(7) )
    else
        set udg_PactWithTheDevil[8] = true
        set udg_Hero_Troll[8] = gg_unit_h009_0347
        call KillUnit( gg_unit_h009_0347 )
        call RemoveUnit( gg_unit_h017_0229 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(7)), function Trig_Player_ini_Func022Func006002 )
    endif
    // Player9
    if ( Trig_Player_ini_Func024C() ) then
        call PanCameraToTimedLocForPlayer( Player(8), GetPlayerStartLocationLoc(Player(8)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0339, Player(8) )
    else
        set udg_PactWithTheDevil[9] = true
        set udg_Hero_Troll[9] = gg_unit_h009_0348
        call KillUnit( gg_unit_h009_0348 )
        call RemoveUnit( gg_unit_h017_0339 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(8)), function Trig_Player_ini_Func024Func006002 )
    endif
    // Player10
    if ( Trig_Player_ini_Func026C() ) then
        call PanCameraToTimedLocForPlayer( Player(9), GetPlayerStartLocationLoc(Player(9)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0232, Player(9) )
    else
        set udg_PactWithTheDevil[10] = true
        set udg_Hero_Troll[10] = gg_unit_h009_0349
        call KillUnit( gg_unit_h009_0349 )
        call RemoveUnit( gg_unit_h017_0232 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(9)), function Trig_Player_ini_Func026Func006002 )
    endif
    // Player11
    if ( Trig_Player_ini_Func028C() ) then
        call PanCameraToTimedLocForPlayer( Player(10), GetPlayerStartLocationLoc(Player(10)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0234, Player(10) )
    else
        set udg_PactWithTheDevil[11] = true
        set udg_Hero_Troll[11] = gg_unit_h009_0350
        call KillUnit( gg_unit_h009_0350 )
        call RemoveUnit( gg_unit_h017_0234 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(10)), function Trig_Player_ini_Func028Func006002 )
    endif
    // Player12
    if ( Trig_Player_ini_Func030C() ) then
        call PanCameraToTimedLocForPlayer( Player(11), GetPlayerStartLocationLoc(Player(11)), 0 )
        call SelectUnitForPlayerSingle( gg_unit_h017_0235, Player(11) )
    else
        set udg_PactWithTheDevil[12] = true
        set udg_Hero_Troll[12] = gg_unit_h009_0351
        call KillUnit( gg_unit_h009_0351 )
        call RemoveUnit( gg_unit_h017_0235 )
        set bj_wantDestroyGroup=true
        call ForGroupBJ( GetUnitsOfPlayerAll(Player(11)), function Trig_Player_ini_Func030Func006002 )
    endif
    call PlaySoundBJ( gg_snd_StartGameMusic )
    call StartTimerBJ( udg_TotemCheck, false, 60.00 )
    call StartTimerBJ( udg_DayTimer, true, 500.00 )
    call StartTimerBJ( udg_WeatherTimer, true, 360.00 )
    call StartTimerBJ( udg_HelpTimer, true, 25.00 )
    call StartTimerBJ( udg_Timer10sec, true, 10.00 )
    call DisplayTimedTextToForce( GetPlayersAll(), 10.00, "TRIGSTR_1213" )
    call StartTimerBJ( udg_Totem_timer, false, 600.00 )
    call CreateTimerDialogBJ( udg_Totem_timer, "TRIGSTR_1298" )
    set udg_Totem_window = GetLastCreatedTimerDialogBJ()
    call TimerDialogDisplayBJ( true, udg_Totem_window )
    call ConditionalTriggerExecute( gg_trg_Create_MultiBoard )
    call EnableTrigger( gg_trg_Update_MultiBoard )
    call EnableTrigger( gg_trg_Show_and_Swap_Multiboard )
    call ForForce( GetPlayersAll(), function Trig_Player_ini_Func045A )
    call DestroyTrigger( GetTriggeringTrigger() )
    call TriggerSleepAction( 0.10 )
    loop
        exitwhen i > 11
        if ( GetPlayerController(Player(i)) != MAP_CONTROL_USER ) then
            if IsUnitType(udg_Hero_Building[i+1], UNIT_TYPE_DEAD) == false then
                call IssueNeutralImmediateOrderById( Player(i), udg_Hero_Building[i+1], 'H001' )
                call TriggerSleepAction( 0.10 )
                set udg_PactWithTheDevil[i+1] = true
                call KillUnit( udg_Hero_Troll[i+1] )
            else
                set udg_PactWithTheDevil[i+1] = true
                call KillUnit( udg_Hero_Troll[i+1] )
                call KillUnit( udg_Troll_Spirit[i+1] )
            endif
        endif
        set i = i + 1
    endloop
endfunction

//===========================================================================
function InitTrig_Player_ini takes nothing returns nothing
    set gg_trg_Player_ini = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Player_ini, function Trig_Player_ini_Actions )
endfunction

If you are able to help and willing to review more triggers, I would send you an unprotected copy of the game and I'll give my best to explain whatever needed (the game is huge, complicated and having many interconnected functions).

I have put much efforts in this game, even though I have no profit from it. Still, I am willing to rent a bot for hosting it, because it is loved by its fans. It deserves to live.
 
Last edited:
Level 11
Joined
Apr 11, 2006
Messages
338
Okay, thanks, I have put it in JASS tags.
It isn't done by me and I'm not good at JASS. I can export from previous versions the GUI trigger that matches this one, but it isn't 100% matching it. Some content was added or altered due to the whole reworking of the starting triggers by one person.
They even shouldn't be reviewed separately, I just suppose this one is the faulty one. See something wrong?
 
Slot states have been said to desync, sometimes, when checked on map initialization.

My guess: map initialization runs before the map has started (while it is still in the loading screen). For some people, everything might not be sync'd yet (e.g. a player with a slower computer might not have loaded the entire map yet). This can be problematic since people will be running code at different times, perhaps even before a player was 'registered' as playing.

Imagine this: Player 1 (Red) runs a supercomputer that loads the map in 1 sec. Player 2 (Blue) runs his Grandma's old computer that is powered by hamster-wheel technology. When Player 1 (Red) encounters the map initialization code, it might show that Player 2 (Blue) is not playing (he hasn't loaded yet!). So it won't pass that condition and it won't run whatever code is within it. Once Player 2 finally loads, he'll go through the same condition, and the condition will say that Player 2 is playing. So Warcraft 3 proceeds to run the code within it. But if that code is something that may affect gameplay, then Warcraft 3 will realize "why did this run for Player 2, and not for Player 1?" As a result, Warcraft 3 will kick Player 2, disconnecting him from the game.

Keep in mind that this is all theoretical, but it makes sense in my head.

Anyway, I assume one possible fix would be to delay the initialization until after the game has started. This may cause a little bit of starting lag, but it is better than a desync!

Just uncheck "Run on Map Initialization", and then replace the initialization function (bottom of the script) with this:
JASS:
function InitTrig_Player_ini takes nothing returns nothing
    set gg_trg_Player_ini = CreateTrigger(  )
    call TriggerRegisterTimerEvent( gg_trg_Player_ini, 0.01, false )
    call TriggerAddAction( gg_trg_Player_ini, function Trig_Player_ini_Actions )
endfunction

Hopefully that'll work. If it doesn't, then post again and I'll take a closer look at the code. If the starting lag-spike is unbearable, then let me know because there are ways to improve it.

Good luck!
 
Level 11
Joined
Apr 11, 2006
Messages
338
Considering your logic - it really makes sense. My knowledge is not enough to say if it would work.
The problem is that this trigger is not being run on map initialization - nor the desync happens at initialization (at least most of them). It happens at the point when a dialog screen has been created and to one of the players has been given the opportunity to choose game parameters - difficulty, mods etc. - and the choosing player presses OK. At this point a bunch of triggers run, including this one.
But still - your code looks as a good solution of other possible problems. You think it is safe to implement it to initialization trigger?
 
Level 12
Joined
Mar 17, 2007
Messages
412
Considering your logic - it really makes sense. My knowledge is not enough to say if it would work.
The problem is that this trigger is not being run on map initialization - nor the desync happens at initialization (at least most of them). It happens at the point when a dialog screen has been created and to one of the players has been given the opportunity to choose game parameters - difficulty, mods etc. - and the choosing player presses OK. At this point a bunch of triggers run, including this one.
But still - your code looks as a good solution of other possible problems. You think it is safe to implement it to initialization trigger?

I'm not experienced in the jass area however I "think" I notice something

JASS:
if ( not ( udg_TeamNumber[1] > 0 ) ) then

I'm not exactly sure what this is referring to but I've had multiple instances when trying to use team numbers the game would otherwise freeze up on me so I stopped using it.

I say this because you noted
"The more players in the game, the more desyncs (average 1 desync per 4 players)"

Edit: I didn't point this out because I wasn't sure but this might cause a problem
JASS:
    call DestroyTrigger( GetTriggeringTrigger() )
    call TriggerSleepAction( 0.10 )

However that's just my theory.
 
Last edited:
Level 4
Joined
Dec 13, 2010
Messages
70
Wow, you created that map? It got over 45k downloads, thats much, I guess.
Then it's surprising that you didn't come learning Jass yet. It's not that hard as it looks, in my opinion.
Anyways, good job at developing your map over 8 years!
 
Level 11
Joined
Apr 11, 2006
Messages
338
Thank you a lot! I'm the most persistant of all, at least :)
I'm happy that people appreciate it. I'm doing my best, just I'm more a gameplay designer... And I suck at programming. As for JASS - I'm starting to learn, but my time is divided between my daytime job, JTR and two other projects (they're not computer games though).

On the topic - I really apologize to all people above for misleading you: some more tests proved that actually the desyncs do happen also at initialization before choosing difficulty settings. So PurgeandFire may be right!

I teamed up with a great guy with the nickname Frotty who is willing to help - and much more able than me. He did PurgeandFire's update and we'll see if it helps.
I have some gameplay improvements to finish in JTR 5.6 and if desync gets fixed, it would finally become a nice and working game. Then I'd rent a hostbot to keep the community satisfied and we'll focus on a thorough rework of the game.
 
Last edited:
Status
Not open for further replies.
Top