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

My map desync sometimes after I imported a custom spell, why? And how can I solve it?

Status
Not open for further replies.
Level 14
Joined
Jul 19, 2007
Messages
770
Well since I imported a new custom spell into my map, it starts to desync sometimes. It doesn't happening all the time but still it's very annoying and I have no idéa how I can solve it. My custom spell required alot of systems to make the spell work correctly. Is any of these systems known for causing desync? How can I solve it then??? I have imported everything required of the systems to work btw.
systems.png
 
Well since I imported a new custom spell into my map, it starts to desync sometimes. It doesn't happening all the time but still it's very annoying and I have no idéa how I can solve it. My custom spell required alot of systems to make the spell work correctly. Is any of these systems known for causing desync? How can I solve it then??? I have imported everything required of the systems to work btw.
View attachment 405676
I have most of the systems above the "Vars" folder (except for MouseUtils, TimedHandles, SpellEffectEvent/Plugin) in my map, and they are fine for my use-cases. I'd look at the triggers below to see if they have any suspicious usage of "GetLocalPlayer".
 
Level 14
Joined
Jul 19, 2007
Messages
770
I have most of the systems above the "Vars" folder (except for MouseUtils, TimedHandles, SpellEffectEvent/Plugin) in my map, and they are fine for my use-cases. I'd look at the triggers below to see if they have any suspicious usage of "GetLocalPlayer".
Well Utilities, Alloc and TimerUtilsEx uses "GetLocalPlayer". What's wrong with that? Does it cause desync?
 
Well Utilities, Alloc and TimerUtilsEx uses "GetLocalPlayer". What's wrong with that? Does it cause desync?
If used wrongly, yes.
Within the local-player if-block, you can only do "local" changes.
If you create a unit for example within a local-player block, the unit will only be created on that player's computer, causing a desync.
If you change color of a special effect, that doesn't effect game-play and is considered fine.

Basically, you'd have to look at each instance and see if it is fine and/or find the resouce page/forum post for utilities and see if there is a comment (or hope that someone knows them all, including your mystery spell, and find answers for you).

My bet is that the mystery spell is to blame, but I don't have enough information to say for sure.
 
Level 14
Joined
Jul 19, 2007
Messages
770
If used wrongly, yes.
Within the local-player if-block, you can only do "local" changes.
If you create a unit for example within a local-player block, the unit will only be created on that player's computer, causing a desync.
If you change color of a special effect, that doesn't effect game-play and is considered fine.

Basically, you'd have to look at each instance and see if it is fine and/or find the resouce page/forum post for utilities and see if there is a comment (or hope that someone knows them all, including your mystery spell, and find answers for you).

My bet is that the mystery spell is to blame, but I don't have enough information to say for sure.
I don't think there is anything wrong with my imported spell because it doesn't use "GetLocalPlayer". I know nothing about JASS so I have no idéa how to solve this...
JASS:
library FelBeam requires Missiles, SpellEffectEvent, PluginSpellEffect, NewBonus, Utilities
    /* ----------------------- Fel Beam v1.4 by Chopinski ----------------------- */
    // Credits:
    //     BPower    - Missile Library
    //     Bribe     - SpellEffectEvent
    //     AZ        - Fel Beam model
    //     nGy       - Haunt model
    //     The Panda - ToxicBeam icon
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the Rain of Fel Fire ability
        public constant integer     ABILITY       = 'A09I'
        // The beam inicial z offset
        private constant real       START_HEIGHT  = 60
        // The beam final z offset
        private constant real       END_HEIGHT    = 60
        // The landing time of the falling misisle
        private constant real       LANDING_TIME  = 1.5
        // The impact radius of the missile that will damage units.
        private constant real       IMPACT_RADIUS = 120.
        // The missile model
        private constant string     MISSILE_MODEL = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdx"
        // The size of the fel beam
        private constant real       MISSILE_SCALE = 1.5
        // The beam missile speed
        private constant real       MISSILE_SPEED = 700
        // The Attack type of the damage dealt (Spell)
        private constant attacktype ATTACK_TYPE   = ATTACK_TYPE_NORMAL
        // The Damage type of the damage dealt 
        private constant damagetype DAMAGE_TYPE   = DAMAGE_TYPE_MAGIC
        // The curse model
        private constant string     CURSE_MODEL   = "Burning Rage Yellow.mdx"
        // The curse attachment point
        private constant string     CURSE_ATTACH  = "overhead"
    endglobals

    // The search range of units after a cursed unit dies
    private function GetSearchRange takes integer level returns real
        return 700. + 0.*level
    endfunction

    // The damage amount
    private function GetDamage takes integer level returns real
        return 50. *level
    endfunction

    // The amount of armor reduced
    public function GetArmorReduction takes integer level returns integer
        return level + 1
    endfunction

    // How long the curse lasts
    public function GetCurseDuration takes integer level returns real
        return 5. + 0.*level
    endfunction
    
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Curse
        static timer timer = CreateTimer()
        static integer array n
        static boolean array cursed
        //Dynamic Indexing
        static integer didx = -1
        static thistype array data

        unit    unit
        integer armor
        integer index
        real    ticks
        effect  effect

        method remove takes integer i returns integer
            call DestroyEffect(effect)
            call AddUnitBonus(unit, BONUS_ARMOR, armor)

            set data[i]       = data[didx]
            set didx          = didx - 1
            set cursed[index] = false
            set n[index]      = 0
            set unit          = null
            set effect        = null

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 or not UnitAlive(unit) then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 0.5
                set i = i + 1
            endloop
        endmethod

        static method create takes unit target, real duration, integer amount returns thistype
            local integer  idx = GetUnitUserData(target)
            local thistype this

            if n[idx] != 0 then
                set this       = n[idx]
            else
                set this       = thistype.allocate()
                set unit       = target
                set armor      = amount
                set effect     = AddSpecialEffectTarget(CURSE_MODEL, target, CURSE_ATTACH)
                set index      = idx
                set didx       = didx + 1
                set data[didx] = this
                set n[idx]     = this

                call AddUnitBonus(target, BONUS_ARMOR, -amount)
                if didx == 0 then
                    call TimerStart(timer, 0.5, true, function thistype.onPeriod)
                endif
            endif

            if duration >= 0.5 then
                set ticks = duration
            else
                set ticks = 0.
            endif

            return this
        endmethod
    endstruct

    struct Beam extends Missiles
        integer armor
        integer index
        real    curse_duration

        method onFinish takes nothing returns boolean
            if UnitAlive(target) then
                set Curse.cursed[index] = true
                if UnitDamageTarget(source, target, damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null) then
                    call Curse.create(target, curse_duration, armor)
                endif
            endif

            return true
        endmethod
    endstruct

    struct FelBeam
        static unit array source

        static method launch takes Beam beam, unit caster, unit target, integer level returns nothing
            set beam.source         = caster
            set beam.target         = target
            set beam.model          = MISSILE_MODEL
            set beam.scale          = MISSILE_SCALE
            set beam.speed          = MISSILE_SPEED
            set beam.damage         = GetDamage(level)
            set beam.owner          = GetOwningPlayer(caster)
            set beam.armor          = GetArmorReduction(level)
            set beam.curse_duration = GetCurseDuration(level)
            set beam.index          = GetUnitUserData(target)
            set source[beam.index]  = caster

            call beam.launch()
        endmethod

        private static method onDeath takes nothing returns nothing
            local unit    killed = GetTriggerUnit()
            local integer index  = GetUnitUserData(killed)
            local unit    caster = source[index]
            local integer level
            local real    x
            local real    y
            local real    z
            local group   g
            local unit    v
            local Beam    beam
        
            if Curse.cursed[index] then
                if source[index] == null then
                    set caster = GetKillingUnit()
                    set level  = 1
                else
                    set level = GetUnitAbilityLevel(caster, ABILITY)
                endif
        
                set x = GetUnitX(killed)
                set y = GetUnitY(killed)
                set z = GetUnitFlyHeight(killed) + START_HEIGHT
                set g = GetEnemyUnitsInRange(GetOwningPlayer(caster), x, y, GetSearchRange(level), false, false)
                if BlzGroupGetSize(g) > 0 then
                    set v    = GroupPickRandomUnit(g)
                    set beam = Beam.create(x, y, z, GetUnitX(v), GetUnitY(v), END_HEIGHT)
                    call launch(beam, caster, v, level)
                endif
                call DestroyGroup(g)
                set source[index]       = null
                set Curse.cursed[index] = false
            endif
        
            set g      = null
            set v      = null
            set killed = null
            set caster = null
        endmethod

        private static method onCast takes nothing returns nothing
            local Beam beam = Beam.create(Spell.source.x, Spell.source.y, START_HEIGHT, Spell.target.x, Spell.target.y, END_HEIGHT)
            
            call launch(beam, Spell.source.unit, Spell.target.unit, Spell.level)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
        endmethod
    endstruct
endlibrary
 
I don't see anything obvious. Sorry.
There are a few GUI things that can cause desync, such as camera panning, and possibly something more.

But the GetLocalPlayer-call is most often the cause of desyncs.

I don't use MouseUtils, but getting mouse location is something you do from a "local player", so that must handle all of that or else... It'll desync if used improperly.
 
Level 14
Joined
Jul 19, 2007
Messages
770
I don't see anything obvious. Sorry.
There are a few GUI things that can cause desync, such as camera panning, and possibly something more.

But the GetLocalPlayer-call is most often the cause of desyncs.

I don't use MouseUtils, but getting mouse location is something you do from a "local player", so that must handle all of that or else... It'll desync if used improperly.
Ok. I could try remove "MouseUtils" then because that system isn't needed...
 
Status
Not open for further replies.
Top