• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

[vJASS] Read/Write Configuration File

Hello guys, I am attempting to have a system in my map, which reads out a configuration file and adjusts some variables and camera depending on the content.
I got that part working, but I have trouble with syncing/desyncs. So some things have to be done in a local block but other stuff like global variables will desync. Could you please help me figure this out? I appreciate any help!
For the read and write I use TriggerHappy's FileIO library.
JASS:
library ConfigReader requires FileIO

globals
    constant real CONFIG_DEFAULT_ZOOM = 3000
    constant real CONFIG_DEFAULT_FIELD_ANGLE = 300
    constant boolean CONFIG_DEFAULT_RESPAWN_CAM = true
    constant boolean CONFIG_DEFAULT_WOOD = true
    constant string CONFIG_FILE = "Battleships Crossfire\\config.txt"
    
    private real array CONFIG_Zoom
    private real array CONFIG_FieldAngle
    private boolean array CONFIG_RespawnCam
    private boolean array CONFIG_Wood
endglobals

private function TrimWhitespace takes string s returns string
    local integer i = 0
    local integer len = StringLength(s)
    
    if s == null or len == 0 then
        return ""
    endif

    loop
        exitwhen i >= len or SubString(s, i, i + 1) != " "
        set i = i + 1
    endloop
    set s = SubString(s, i, len)
    set len = StringLength(s)
    
    loop
        exitwhen len == 0 or SubString(s, len - 1, len) != " "
        set len = len - 1
    endloop
    
    return SubString(s, 0, len)
endfunction

private function GetConfigValue takes string configFileContents, string key returns string
    local integer contentsLen = StringLength(configFileContents)
    local string searchKey = key + ":"
    local integer searchKeyLen = StringLength(searchKey)
    
    local integer start = -1
    local integer endLine = -1
    local integer i = 0
    local string value
    
    if configFileContents == null or contentsLen == 0 then
        return null
    endif

    loop
        exitwhen i + searchKeyLen > contentsLen
        
        if SubString(configFileContents, i, i + searchKeyLen) == searchKey then
            set start = i
            set i = i + searchKeyLen 
            exitwhen true
        endif
        
        set i = i + 1
    endloop
    
    if start == -1 then
        return null
    endif

    loop
        exitwhen i >= contentsLen
        
        if SubString(configFileContents, i, i + 1) == "\n" then
            set endLine = i
            exitwhen true
        endif
        
        set i = i + 1
    endloop
    
    if endLine == -1 then
        set endLine = contentsLen
    endif
    
    set value = SubString(configFileContents, start + searchKeyLen, endLine)
    
    return TrimWhitespace(value)
endfunction

private function ConfigCreateDefault takes nothing returns nothing
    local string contents = ""
    local string respawnCamValue 
    local string woodValue       

    if CONFIG_DEFAULT_RESPAWN_CAM == true then
        set respawnCamValue = "True"
    else
        set respawnCamValue = "False"
    endif

    if CONFIG_DEFAULT_WOOD == true then
        set woodValue = "True"
    else
        set woodValue = "False"
    endif
    
    set contents = contents + "\n\n### CONFIGURATION ###\n"
    set contents = contents + "Zoom: " + R2S(CONFIG_DEFAULT_ZOOM) + "\n"
    set contents = contents + "Angle: " + R2S(CONFIG_DEFAULT_FIELD_ANGLE) + "\n"
    set contents = contents + "Camera on Respawn: " + respawnCamValue + "\n"
    set contents = contents + "Use Wood Hotkey: " + woodValue + "\n"
    set contents = contents + "#####################\n"
    set contents = contents + "# Zoom: 0 - 8000 # Angle: 270 - 360\n\n"
   
    call FileIO_Write(CONFIG_FILE, contents)
endfunction

private function ConfigParse takes nothing returns nothing
    local string contents = FileIO_Read(CONFIG_FILE)
    local string valueString
    local player p = GetLocalPlayer()
    local integer pid = GetPlayerId(p)
    
    set valueString = GetConfigValue(contents, "Zoom")
    if valueString != null then
        set CONFIG_Zoom[pid] = S2R(valueString)
    else
        set CONFIG_Zoom[pid] = CONFIG_DEFAULT_ZOOM
    endif
    
    set valueString = GetConfigValue(contents, "Angle")
    if valueString != null then
        set CONFIG_FieldAngle[pid] = S2R(valueString)
    else
        set CONFIG_FieldAngle[pid] = CONFIG_DEFAULT_FIELD_ANGLE
    endif

    set valueString = GetConfigValue(contents, "Camera on Respawn")
    if valueString != null and StringCase(valueString, true) == "FALSE" then 
        set CONFIG_RespawnCam[pid] = false
    else
        set CONFIG_RespawnCam[pid] = CONFIG_DEFAULT_RESPAWN_CAM
    endif

    set valueString = GetConfigValue(contents, "Use Wood Hotkey")
    if valueString != null and StringCase(valueString, true) == "FALSE" then 
        set CONFIG_Wood[pid] = false
    else
        set CONFIG_Wood[pid] = CONFIG_DEFAULT_WOOD
    endif
endfunction

private function ConfigApplySettings takes nothing returns nothing
    local integer i = 2
    local player p = null
    local real zoom = 0
    local real angle = 0

    loop
        exitwhen i > 19
            set p = Player(i)
            if (GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING) == true and (GetPlayerController(p) == MAP_CONTROL_USER) == true then
            
                if CONFIG_Zoom[i] > 8000 then
                    set zoom = 8000
                elseif CONFIG_Zoom[i] < 0 then
                    set zoom = 0
                else
                    set zoom = CONFIG_Zoom[i]
                endif
                
                if CONFIG_FieldAngle[i] > 360 then
                    set angle = 360
                elseif CONFIG_FieldAngle[i] < 270 then
                    set angle = 270
                else
                    set angle = CONFIG_FieldAngle[i]
                endif
                call DisplayTextToForce( GetForceOfPlayer(p), "Zoom: |cff66BBBB" + R2S(zoom) + "|r")
                call DisplayTextToForce( GetForceOfPlayer(p), "Angle: |cff66BBBB" + R2S(angle)  + "|r")
                call SetCameraFieldForPlayer( p, CAMERA_FIELD_FARZ, 10000.00, 0 )
                call SetCameraFieldForPlayer( p, CAMERA_FIELD_TARGET_DISTANCE, zoom, 0.0 )
                call SetCameraFieldForPlayer( p, CAMERA_FIELD_ANGLE_OF_ATTACK, angle, 0.0 )
                set udg_Kamera_zoom[i+1] = zoom
                set udg_Kamera_perspektive[i+1] = angle
                
                if CONFIG_RespawnCam[i] == false then
                    call UnitRemoveAbility( udg_barrel[i+1], 'A0KN' )
                    call UnitAddAbility( udg_barrel[i+1], 'A0KO' )
                    set udg_respawn_camera_boolean[i+1] = false
                    call DisplayTextToForce( GetForceOfPlayer(p), "Respawn camera has been |cffff0000disabled|r" )
                endif
                if CONFIG_Wood[i] == false then
                    set udg_wood_esc_boolean[i+1] = false
                    call DisplayTextToForce( GetForceOfPlayer(p), "|cffffff00ESC|r has been |cffff0000disabled|r as a hotkey for repair woods" )
                endif                
            endif
        set i = i + 1
    endloop
endfunction

public function ConfigInit takes nothing returns nothing
    local string contents = FileIO_Read(CONFIG_FILE)
    if contents == null then
        call ConfigCreateDefault()
    endif
    call ConfigParse()
    call ConfigApplySettings()
endfunction

endlibrary
 
It makes sense to me that it desyncs. Let's imagine you have only two players: Player 1 Red and Player 2 Blue. i will mention only CONFIG_RespawnCam, but the issue is related to your other arrays as well.
Let's assume that both players have left CONFIG_DEFAULT_RESPAWN_CAM with the default 'true' value.
Therefore config files of each player will contain following string: Camera on Respawn: True

At map initialization, before your script is ran, the array CONFIG_RespawnCam is in this state for Red and Blue player:
CONFIG_RespawnCam[0] = false
CONFIG_RespawnCam[1] = false

When you run ConfigParse function, it will read the 'True' value from Camera on Respawn: True and do set CONFIG_RespawnCam[pid] = CONFIG_DEFAULT_RESPAWN_CAM.

Finally, in ConfigApplySettings function you read the value from the array in condition if CONFIG_RespawnCam[i] == false then.

The issue is that in ConfigParse function the pid is player number of "GetLocalPlayer()".
So after ConfigParse is ran, the CONFIG_RespawnCam has following state for player Red:
CONFIG_RespawnCam[0] = true
CONFIG_RespawnCam[1] = false

But following state for player Blue:
CONFIG_RespawnCam[0] = false
CONFIG_RespawnCam[1] = true

When your ConfigApplySettings function runs the condition, each player evaluates that condition differently for 'Player id 0':
  • In case of Blue player, it will be True and remove from unit udg_barrel[1] the 'A0KN' ability
  • In case of Red player, it will be False and udg_barrel[1] unit will retain the 'A0KN' ability.

So for one player unit has ability, for other player that same unit does not have an ability -> game detects desync and kicks everyone out.
 
Last edited:
Back
Top