Announcement

Collapse
No announcement yet.

Simple Saving Scripts

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    #41
    OK, I have racked my brain, asked Moose folk and generally given up on such a simple addition to make this script a little more useful (at least for me)

    Issue: I want to have the script ignore ALL ground vehicle groups with the word 'IGNORE' or 'ignore' or 'Ignore' or any variance of the word ignore in the group name, but save all the other ground units.

    The script is currently only writing the line below now.
    SaveUnits = {}

    Any help would be appreciated as I am completely stuck.

    Here is the code I have attempted to put in but had no success with. It was originally saving the ignore groups and on reload the ignore groups had no waypoints. Now after i modified line 181 it only saves the following line.

    Code:
    -- Simple Group Saving by Pikey May 2019
    -- Usage of this script should credit the following contributors:
     --Pikey, 
     --Speed & Grimes for their work on Serialising tables, included below,
     --FlightControl for MOOSE (Required)
     
     -- Modified by OzDeaDMeaT to add the IGNORE 
     
     --INTENDED USAGE
     --DCS Server Admins looking to do long term multi session play that will need a server reboot in between and they wish to keep the Ground 
     --Unit positions true from one reload to the next.
     
     --USAGE
     --Ensure LFS and IO are not santitised in missionScripting.lua. This enables writing of files. If you don't know what this does, don't attempt to use this script.
     --Requires versions of MOOSE.lua supporting "SET:ForEachGroupAlive()". Should be good for 6 months or more from date of writing. 
     --MIST not required, but should work OK with it regardless.
     --Edit 'SaveScheduleUnits' below, (line 34) to the number of seconds between saves. Low impact. 10 seconds is a fast schedule.
     --Place Ground Groups wherever you want on the map as normal.
     --Run this script at Mission start
     --The script will create a small file with the list of Groups and Units.
     --At Mission Start it will check for a save file, if not there, create it fresh
     --If the table is there, it loads it and Spawns everything that was saved.
     --The table is updated throughout mission play
     --The next time the mission is loaded it goes through all the Groups again and loads them from the save file.
     
     --LIMITATIONS
     --Only Ground Groups and Units are specified, play with the SET Filter at your own peril. Could be adjusted for just one Coalition or a FilterByName().
     --See line 107 and 168 for the SET.
     --See https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Core.Set.html##(SET_GROUP)
     --Naval Groups not Saved. If Included, there may be issues with spawned objects and Client slots where Ships have slots for aircraft/helo. Possible if not a factor
     --Statics are not included. See 'Simple Static Saving' for a solution
     --Routes are not saved. Uncomment lines 148-153 if you wish to keep them, but they won't activate them on restart. It is impossible to query a group for it's current
     --route, only for the original route it recieved from the Mission Editor. Therefore a DCS limitation.
     -----------------------------------
     --Configurable for user:
     SaveScheduleUnits=30 --how many seconds between each check of all the statics.
     -----------------------------------
     --Do not edit below here
     -----------------------------------
     local version = "1.0"
     
     function IntegratedbasicSerialize(s)
        if s == nil then
          return "\"\""
        else
          if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then
            return tostring(s)
          elseif type(s) == 'string' then
            return string.format('%q', s)
          end
        end
      end
      
    -- imported slmod.serializeWithCycles (Speed)
      function IntegratedserializeWithCycles(name, value, saved)
        local basicSerialize = function (o)
          if type(o) == "number" then
            return tostring(o)
          elseif type(o) == "boolean" then
            return tostring(o)
          else -- assume it is a string
            return IntegratedbasicSerialize(o)
          end
        end
    
        local t_str = {}
        saved = saved or {}       -- initial value
        if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then
          table.insert(t_str, name .. " = ")
          if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then
            table.insert(t_str, basicSerialize(value) ..  "\n")
          else
    
            if saved[value] then    -- value already saved?
              table.insert(t_str, saved[value] .. "\n")
            else
              saved[value] = name   -- save name for next time
              table.insert(t_str, "{}\n")
              for k,v in pairs(value) do      -- save its fields
                local fieldname = string.format("%s[%s]", name, basicSerialize(k))
                table.insert(t_str, IntegratedserializeWithCycles(fieldname, v, saved))
              end
            end
          end
          return table.concat(t_str)
        else
          return ""
        end
      end
    
    function file_exists(name) --check if the file already exists for writing
        if lfs.attributes(name) then
        return true
        else
        return false end 
    end
    
    function writemission(data, file)--Function for saving to file (commonly found)
      File = io.open(file, "w")
      File:write(data)
      File:close()
    end
    
    
    
    --SCRIPT START
    env.info("Loaded Simple Group Saving, by Pikey, 2018, version " .. version)
    
    if file_exists("TestUnits.lua") then --Script has been run before, so we need to load the save
      env.info("Existing database, loading from File.")
    AllGroups = SET_GROUP:New() --Create Moose Group Variable
    local TempSET = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterOnce() --Creates a Moose Group Variable with all ground units
    TempSET:ForEachGroup(function (MooseGroup)
      local GroupsName = MooseGroup:GetName()
      if string.find(GroupsName, "IGNORE*")  == nil then
        env.info("Group Loaded - " .. GroupsName)
    	AllGroups:Add(MooseGroup)
      end
     end)
    --AllGroups = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterStart()
        AllGroups:ForEachGroup(function (grp)
          grp:Destroy()
        end)
    
      dofile("TestUnits.lua")
      tempTable={}
      Spawn={}
    --RUN THROUGH THE KEYS IN THE TABLE (GROUPS)
      for k,v in pairs (SaveUnits) do
        units={}
    --RUN THROUGH THE UNITS IN EACH GROUP
          for i= 1, #(SaveUnits[k]["units"]) do 
      
    tempTable =
    
      { 
       ["type"]=SaveUnits[k]["units"][i]["type"],
       ["transportable"]= {["randomTransportable"] = false,}, 
       --["unitId"]=9000,used to generate ID's here but no longer doing that since DCS seems to handle it
       ["skill"]=SaveUnits[k]["units"][i]["skill"],
       ["y"]=SaveUnits[k]["units"][i]["y"] ,
       ["x"]=SaveUnits[k]["units"][i]["x"] ,
       ["name"]=SaveUnits[k]["units"][i]["name"],
       ["heading"]=SaveUnits[k]["units"][i]["heading"],
       ["playerCanDrive"]=true,  --hardcoded but easily changed.  
      }
    
          table.insert(units,tempTable)
        end --end unit for loop
    
    
    groupData = 
    
      {
        ["visible"] = true,
        --["lateActivation"] = false,
        ["tasks"] = {}, -- end of ["tasks"]
        ["uncontrollable"] = false,
        ["task"] = "Ground Nothing",
        --["taskSelected"] = true,
        --["route"] = 
        --{ 
        --["spans"] = {},
        --["points"]= {}
        -- },-- end of ["spans"] 
        --["groupId"] = 9000 + _count,
        ["hidden"] = false,
        ["units"] = units,
        ["y"] = SaveUnits[k]["y"],
        ["x"] = SaveUnits[k]["x"],
        ["name"] = SaveUnits[k]["name"],
        --["start_time"] = 0,
      } 
    
      coalition.addGroup(SaveUnits[k]["CountryID"], SaveUnits[k]["CategoryID"], groupData)
      groupData = {}
      end --end Group for loop
    
    else --Save File does not exist we start a fresh table, no spawns needed
    
    -- Added by OzDM
    SaveUnits={}
    AllGroups = SET_GROUP:New() --Create Moose Group Variable
    local TempSET = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterOnce() --Creates a Moose Group Variable with all ground units
    TempSET:ForEachGroup(function (SpecificGroup)
      local GrpName = SpecificGroup:GetName()
      if string.find(GrpName, "IGNORE*")  == nil then
        env.info("Group Saved - " .. GrpName)
    	AllGroups:Add(SpecificGroup)
      end
     end)
    
    -- ORIGINAL 
    --  SaveUnits={}
    --  AllGroups = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterStart()
    end
    
    --THE SAVING SCHEDULE
    SCHEDULER:New( nil, function()
      AllGroups:ForEachGroupAlive(function (grp)
      local DCSgroup = Group.getByName(grp:GetName() )
      local size = DCSgroup:getSize()
    
    _unittable={}
    
    for i = 1, size do
    
    local tmpTable =
    
      {   
        ["type"]=grp:GetUnit(i):GetTypeName(),
        ["transportable"]=true,
        ["unitID"]=grp:GetUnit(i):GetID(),
        ["skill"]="Average",
        ["y"]=grp:GetUnit(i):GetVec2().y,
        ["x"]=grp:GetUnit(i):GetVec2().x,
        ["name"]=grp:GetUnit(i):GetName(),
        ["playerCanDrive"]=true,
        ["heading"]=grp:GetUnit(i):GetHeading(),
      }
    
    table.insert(_unittable,tmpTable) --add units to a temporary table
    end
    
    SaveUnits[grp:GetName()] =
    {
       ["CountryID"]=grp:GetCountry(),
       ["SpawnCoalitionID"]=grp:GetCountry(),
       ["tasks"]={}, --grp:GetTaskMission(), --wrong gives the whole thing
       ["CategoryID"]=grp:GetCategory(),
       ["task"]="Ground Nothing",
       ["route"]={}, -- grp:GetTaskRoute(),
       ["groupId"]=grp:GetID(),
       --["SpawnCategoryID"]=grp:GetCategory(),
       ["units"]= _unittable,
       ["y"]=grp:GetVec2().y, 
       ["x"]=grp:GetVec2().x,
       ["name"]=grp:GetName(),
       ["start_time"]=0,
       ["CoalitionID"]=grp:GetCoalition(),
       ["SpawnCountryID"]=grp:GetCoalition(),
    }
    
    end)
    
    newMissionStr = IntegratedserializeWithCycles("SaveUnits",SaveUnits) --save the Table as a serialised type with key SaveUnits
    writemission(newMissionStr, "TestUnits.lua")--write the file from the above to SaveUnits.lua
    --writemission(newMissionStr, "SaveUnits.lua")--write the file from the above to SaveUnits.lua
    SaveUnits={}--clear the table for a new write.
    --env.info("Data saved.")
    end, {}, 1, SaveScheduleUnits)
    (FYI I am not a LUA person, just trying to get this most basic of basic feature additions added to this script)
    -System Specs are:

    CPU: i9-9900K @ 4.9Ghz
    M/B: Asus TUF z390 Gaming Plus
    RAM: 32GB
    Video Card: EVGA RTX 2080 Ti XC Hybrid Gaming (11G-P4-2384)
    VR: Oculus Rift S
    Controls: Warthog Stick & Throttle, TPR pedals, Streamdeck XL

    Comment


      #42
      Here's how I handled it - at restore time, not save time.

      You cannot fix the route/waypoints issue. Restored groups simply won't have them, or they won't work.

      Since the route issue in DCS affects this script, I chose to have it ignore any moving vehicles, which I name "convoy..," and are all scheduled spawns anyway (patrolling vehicles, etc).

      I also added in the functionality for it to recreate any CSAR pickups that are on the map, which are also easily identified by their group name.

      I also load my script at 25 seconds, not at mission start, so that CSAR exists already.

      Happy to contribute back if anyone else finds this useful.

      /[BSD]Fargo
      Attached Files

      sigpic

      Are you ready to take your chopper flying skills to the next level?
      Then check out http://www.blacksharkden.com/ Or visit us on Discord https://discord.gg/kaayJ5z and talk to some of our awesome pilots today.

      Comment


        #43
        Originally posted by fargo007 View Post
        Here's how I handled it - at restore time, not save time.

        You cannot fix the route/waypoints issue. Restored groups simply won't have them, or they won't work.

        Since the route issue in DCS affects this script, I chose to have it ignore any moving vehicles, which I name "convoy..," and are all scheduled spawns anyway (patrolling vehicles, etc).

        I also added in the functionality for it to recreate any CSAR pickups that are on the map, which are also easily identified by their group name.

        I also load my script at 25 seconds, not at mission start, so that CSAR exists already.

        Happy to contribute back if anyone else finds this useful.

        /[BSD]Fargo
        Thanks for the Script Fargo.

        With a little help from the from AEF Zyfr and Wingthor from Moose I managed to get it working. I also added a section some variables at the start of the script to help people tweak it for various missions (i.e. the save file name && IgnoreString for groups)

        Code:
        -- Simple Group Saving by Pikey May 2019
        -- Usage of this script should credit the following contributors:
         --Pikey, 
         --Speed & Grimes for their work on Serialising tables, included below,
         --FlightControl for MOOSE (Required)
         
         -- Modified by OzDeaDMeaT to add the ability to ignore specific groups defined by the mission maker.
         
         --INTENDED USAGE
         --DCS Server Admins looking to do long term multi session play that will need a server reboot in between and they wish to keep the Ground 
         --Unit positions true from one reload to the next.
         
         --USAGE
         --Ensure LFS and IO are not santitised in missionScripting.lua. This enables writing of files. If you don't know what this does, don't attempt to use this script.
         --Requires versions of MOOSE.lua supporting "SET:ForEachGroupAlive()". Should be good for 6 months or more from date of writing. 
         --MIST not required, but should work OK with it regardless.
         --Edit 'SaveScheduleUnits' below, (line 34) to the number of seconds between saves. Low impact. 10 seconds is a fast schedule.
         --Place Ground Groups wherever you want on the map as normal.
         --Run this script at Mission start
         --The script will create a small file with the list of Groups and Units.
         --At Mission Start it will check for a save file, if not there, create it fresh
         --If the table is there, it loads it and Spawns everything that was saved.
         --The table is updated throughout mission play
         --The next time the mission is loaded it goes through all the Groups again and loads them from the save file.
         
         --LIMITATIONS
         --Only Ground Groups and Units are specified, play with the SET Filter at your own peril. Could be adjusted for just one Coalition or a FilterByName().
         --See line 107 and 168 for the SET.
         --See https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Core.Set.html##(SET_GROUP)
         --Naval Groups not Saved. If Included, there may be issues with spawned objects and Client slots where Ships have slots for aircraft/helo. Possible if not a factor
         --Statics are not included. See 'Simple Static Saving' for a solution
         --Routes are not saved. Uncomment lines 148-153 if you wish to keep them, but they won't activate them on restart. It is impossible to query a group for it's current
         --route, only for the original route it recieved from the Mission Editor. Therefore a DCS limitation.
         -----------------------------------
         --Configurable for user:
         SaveScheduleUnits=30 --how many seconds between each check of all the statics.
         -----------------------------------
         --Do not edit below here
         -----------------------------------
         local version = "1.1a"
         SaveFileName = "TestUnits.lua" --The File you want this script to use when writing out.
         SrchString = "IGNORE" --This string will be forced lower case when searched as the GroupName searched is also forced to lowercase
         
         function IntegratedbasicSerialize(s)
            if s == nil then
              return "\"\""
            else
              if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then
                return tostring(s)
              elseif type(s) == 'string' then
                return string.format('%q', s)
              end
            end
          end
          
        function IntegratedserializeWithCycles(name, value, saved)
        -- imported slmod.serializeWithCycles (Speed)
        	local basicSerialize = function (o)
        	  if type(o) == "number" then
        		return tostring(o)
        	  elseif type(o) == "boolean" then
        		return tostring(o)
        	  else -- assume it is a string
        		return IntegratedbasicSerialize(o)
        	  end
        	end
        
        	local t_str = {}
        	saved = saved or {}       -- initial value
        	if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then
        	  table.insert(t_str, name .. " = ")
        	  if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then
        		table.insert(t_str, basicSerialize(value) ..  "\n")
        	  else
        
        		if saved[value] then    -- value already saved?
        		  table.insert(t_str, saved[value] .. "\n")
        		else
        		  saved[value] = name   -- save name for next time
        		  table.insert(t_str, "{}\n")
        		  for k,v in pairs(value) do      -- save its fields
        			local fieldname = string.format("%s[%s]", name, basicSerialize(k))
        			table.insert(t_str, IntegratedserializeWithCycles(fieldname, v, saved))
        		  end
        		end
        	  end
        	  return table.concat(t_str)
        	else
        	  return ""
        	end
        end
        
        function file_exists(name) --check if the file already exists for writing
            if lfs.attributes(name) then
            return true
            else
            return false end 
        end
        
        function writemission(data, file)--Function for saving to file (commonly found)
          File = io.open(file, "w")
          File:write(data)
          File:close()
        end
        
        
        
        --SCRIPT START
        env.info("Loaded Simple Group Saving, by Pikey, 2018, version " .. version)
        
        if file_exists(SaveFileName) then --Script has been run before, so we need to load the save
          env.info("Existing database, loading from File.")
        AllGroups = SET_GROUP:New() --Create Moose Group Variable
        local TempSET = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterOnce() --Creates a Moose Group Variable with all ground units
        TempSET:ForEachGroup(function (MooseGroup)
          local GroupsName = MooseGroup:GetName()
        	env.info("Checking Group - " .. GroupsName)
          if string.find(string.lower(GroupsName), string.lower(SrchString))  == nil then
            env.info("Group Loaded - " .. GroupsName)
        	AllGroups:AddGroup(MooseGroup)
          end
         end)
        --AllGroups = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterStart()
            AllGroups:ForEachGroup(function (grp)
              grp:Destroy()
            end)
        
          dofile(SaveFileName)
          tempTable={}
          Spawn={}
        --RUN THROUGH THE KEYS IN THE TABLE (GROUPS)
          for k,v in pairs (SaveUnits) do
            units={}
        --RUN THROUGH THE UNITS IN EACH GROUP
              for i= 1, #(SaveUnits[k]["units"]) do 
          
        tempTable =
        
          { 
           ["type"]=SaveUnits[k]["units"][i]["type"],
           ["transportable"]= {["randomTransportable"] = false,}, 
           --["unitId"]=9000,used to generate ID's here but no longer doing that since DCS seems to handle it
           ["skill"]=SaveUnits[k]["units"][i]["skill"],
           ["y"]=SaveUnits[k]["units"][i]["y"] ,
           ["x"]=SaveUnits[k]["units"][i]["x"] ,
           ["name"]=SaveUnits[k]["units"][i]["name"],
           ["heading"]=SaveUnits[k]["units"][i]["heading"],
           ["playerCanDrive"]=true,  --hardcoded but easily changed.  
          }
        
              table.insert(units,tempTable)
            end --end unit for loop
        
        
        groupData = 
        
          {
            ["visible"] = true,
            --["lateActivation"] = false,
            ["tasks"] = {}, -- end of ["tasks"]
            ["uncontrollable"] = false,
            ["task"] = "Ground Nothing",
            --["taskSelected"] = true,
            --["route"] = 
            --{ 
            --["spans"] = {},
            --["points"]= {}
            -- },-- end of ["spans"] 
            --["groupId"] = 9000 + _count,
            ["hidden"] = false,
            ["units"] = units,
            ["y"] = SaveUnits[k]["y"],
            ["x"] = SaveUnits[k]["x"],
            ["name"] = SaveUnits[k]["name"],
            --["start_time"] = 0,
          } 
        
          coalition.addGroup(SaveUnits[k]["CountryID"], SaveUnits[k]["CategoryID"], groupData)
          groupData = {}
          end --end Group for loop
        
        else --Save File does not exist we start a fresh table, no spawns needed
        
        -- Added by OzDM
        SaveUnits={}
        AllGroups = SET_GROUP:New() --Create Moose Group Variable
        local TempSET = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterOnce() --Creates a Moose Group Variable with all ground units
        TempSET:ForEachGroup(function (SpecificGroup)
          local GrpName = SpecificGroup:GetName()
          if string.find(string.lower(GrpName), string.lower(SrchString))  == nil then
            env.info("Group Saved - " .. GrpName)
        	AllGroups:AddGroup(SpecificGroup)
          end
         end)
        
        -- ORIGINAL 
        --  SaveUnits={}
        --  AllGroups = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterStart()
        end
        
        --THE SAVING SCHEDULE
        SCHEDULER:New( nil, function()
          AllGroups:ForEachGroupAlive(function (grp)
          local DCSgroup = Group.getByName(grp:GetName() )
          local size = DCSgroup:getSize()
        
        _unittable={}
        
        for i = 1, size do
        
        local tmpTable =
        
          {   
            ["type"]=grp:GetUnit(i):GetTypeName(),
            ["transportable"]=true,
            ["unitID"]=grp:GetUnit(i):GetID(),
            ["skill"]="Average",
            ["y"]=grp:GetUnit(i):GetVec2().y,
            ["x"]=grp:GetUnit(i):GetVec2().x,
            ["name"]=grp:GetUnit(i):GetName(),
            ["playerCanDrive"]=true,
            ["heading"]=grp:GetUnit(i):GetHeading(),
          }
        
        table.insert(_unittable,tmpTable) --add units to a temporary table
        end
        
        SaveUnits[grp:GetName()] =
        {
           ["CountryID"]=grp:GetCountry(),
           ["SpawnCoalitionID"]=grp:GetCountry(),
           ["tasks"]={}, --grp:GetTaskMission(), --wrong gives the whole thing
           ["CategoryID"]=grp:GetCategory(),
           ["task"]="Ground Nothing",
           ["route"]={}, -- grp:GetTaskRoute(),
           ["groupId"]=grp:GetID(),
           --["SpawnCategoryID"]=grp:GetCategory(),
           ["units"]= _unittable,
           ["y"]=grp:GetVec2().y, 
           ["x"]=grp:GetVec2().x,
           ["name"]=grp:GetName(),
           ["start_time"]=0,
           ["CoalitionID"]=grp:GetCoalition(),
           ["SpawnCountryID"]=grp:GetCoalition(),
        }
        
        end)
        
        newMissionStr = IntegratedserializeWithCycles("SaveUnits",SaveUnits) --save the Table as a serialised type with key SaveUnits
        writemission(newMissionStr, SaveFileName)--write the file from the above to SaveUnits.lua
        --writemission(newMissionStr, "SaveUnits.lua")--write the file from the above to SaveUnits.lua
        SaveUnits={}--clear the table for a new write.
        --env.info("Data saved.")
        end, {}, 1, SaveScheduleUnits)
        -System Specs are:

        CPU: i9-9900K @ 4.9Ghz
        M/B: Asus TUF z390 Gaming Plus
        RAM: 32GB
        Video Card: EVGA RTX 2080 Ti XC Hybrid Gaming (11G-P4-2384)
        VR: Oculus Rift S
        Controls: Warthog Stick & Throttle, TPR pedals, Streamdeck XL

        Comment


          #44
          Thnx to Shadowze and Wingthor from the Moose Discord for the help while I learn LUA.

          - Added an array of strings to blacklist against the save function
          - Added Debug to show / hide messages while testing (can be turned off and on)
          - Added Unit and Group count when saving file
          - Added Support for scripted units to be saved as well (can be turned off and on)
          - Bug - Cant delete file when no groups are being saved (thus resetting the persistence). Not sure if this is an issue with DCS or Moose at this point.


          Code:
          --[[Simple Group Saving by Pikey May 2019 (Updated by OzDeaDMeaT September 2020)
          Usage of this script should credit the following contributors:
           Pikey, 
           Speed & Grimes for their work on Serialising tables, included below,
           FlightControl for MOOSE (Required)
          --------------------------------------------------------------------------------------  
           INTENDED USAGE
           DCS Server Admins looking to do long term multi session play that will need a server reboot in between and they wish to keep the Ground 
           Unit positions true from one reload to the next.
          -------------------------------------------------------------------------------------- 
           USAGE
           Ensure LFS and IO are not santitised in missionScripting.lua. This enables writing of files. If you don't know what this does, don't attempt to use this script.
           Requires versions of MOOSE.lua supporting "SET:ForEachGroupAlive()". Should be good for 6 months or more from date of writing. 
           MIST not required, but should work OK with it regardless.
           Edit 'SaveScheduleUnits' below, (line 34) to the number of seconds between saves. Low impact. 10 seconds is a fast schedule.
           Place Ground Groups wherever you want on the map as normal.
           Run this script at Mission start
           The script will create a small file with the list of Groups and Units.
           At Mission Start it will check for a save file, if not there, create it fresh
           If the table is there, it loads it and Spawns everything that was saved.
           The table is updated throughout mission play
           The next time the mission is loaded it goes through all the Groups again and loads them from the save file.
           --------------------------------------------------------------------------------------
           LIMITATIONS
           Only Ground Groups and Units are specified, play with the SET Filter at your own peril. Could be adjusted for just one Coalition or a FilterByName().
           See line 107 and 168 for the SET.
           See https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Core.Set.html##(SET_GROUP)
           Naval Groups not Saved. If Included, there may be issues with spawned objects and Client slots where Ships have slots for aircraft/helo. Possible if not a factor
           Statics are not included. See 'Simple Static Saving' for a solution
           Routes are not saved. Uncomment lines 148-153 if you wish to keep them, but they won't activate them on restart. It is impossible to query a group for it's current
           route, only for the original route it recieved from the Mission Editor. Therefore a DCS limitation.
           ]]
           -----------------------------------
            --Configurable for user:
           SaveScheduleUnits=30 --how many seconds between each check of all the statics.
           local version = "1.2c"
           local SGS_DEBUG = false --Enable if you want verbose debug messages in your log (great for troubleshooting)
           local ALLOW_DROPIN_GROUPS = false --Enables drop in groups, i.e. groups that are loaded into the game from external scripts rather than Mission Editor added groups.
           local DELETE_FILE_ON_NO_GROUPS = false --This switch will delete the save file once there is no ground units to safe anymore allowing for automated persistent data reset.
           SaveFileName = "TestUnits.lua" --The File you want this script to use when writing out.
           t_SrchStringTable = {"IGNORE","sidemission", "AnotherString"} --This string will be forced lower case when searched as the GroupName searched is also forced to lowercase
           -----------------------------------
           --Do not edit below here
           -----------------------------------
           function IntegratedbasicSerialize(s)
              if s == nil then
                return "\"\""
              else
                if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then
                  return tostring(s)
                elseif type(s) == 'string' then
                  return string.format('%q', s)
                end
              end
            end
          if SGS_DEBUG == true then env.info("Function: IntegratedbasicSerialize        LOADED OK")end
          
          function IntegratedserializeWithCycles(name, value, saved)
          -- imported slmod.serializeWithCycles (Speed)
          	local basicSerialize = function (o)
          	  if type(o) == "number" then
          		return tostring(o)
          	  elseif type(o) == "boolean" then
          		return tostring(o)
          	  else -- assume it is a string
          		return IntegratedbasicSerialize(o)
          	  end
          	end
          
          	local t_str = {}
          	saved = saved or {}       -- initial value
          	if ((type(value) == 'string') or (type(value) == 'number') or (type(value) == 'table') or (type(value) == 'boolean')) then
          	  table.insert(t_str, name .. " = ")
          	  if type(value) == "number" or type(value) == "string" or type(value) == "boolean" then
          		table.insert(t_str, basicSerialize(value) ..  "\n")
          	  else
          
          		if saved[value] then    -- value already saved?
          		  table.insert(t_str, saved[value] .. "\n")
          		else
          		  saved[value] = name   -- save name for next time
          		  table.insert(t_str, "{}\n")
          		  for k,v in pairs(value) do      -- save its fields
          			local fieldname = string.format("%s[%s]", name, basicSerialize(k))
          			table.insert(t_str, IntegratedserializeWithCycles(fieldname, v, saved))
          		  end
          		end
          	  end
          	  return table.concat(t_str)
          	else
          	  return ""
          	end
          end
          if SGS_DEBUG == true then env.info("Function: IntegratedserializeWithCycles   LOADED OK")end
          
          function file_exists(name) --check if the file already exists for writing
              if lfs.attributes(name) then
              return true
              else
              return false end 
          end
          if SGS_DEBUG == true then env.info("Function: file_exists                     LOADED OK")end
          
          function writemission(data, file)--Function for saving to file (commonly found)
            File = io.open(file, "w")
            File:write(data)
            File:close()
          end
          if SGS_DEBUG == true then env.info("Function: writemission                    LOADED OK")end
          
          function GroupStringSearch(t_SrchTbl, GroupName) --returns true if found
            BASE:F({t_SrchTbl,GroupName})
            if type(t_SrchTbl) ~= "table" then
              env.info("Error: t_SrchTbl is not a table")
              return nil
            end
            if type(GroupName)~="string" then
              env.info("Error: GroupName is not a string")
              return nil
            end
              for index, str2check in ipairs(t_SrchTbl) do
                if type(str2check) == "string" then
                    if string.find(string.lower(GroupName), string.lower(str2check))  ~= nil then 
                      return true
                    end
                end
              end
              return false
          end
          if SGS_DEBUG == true then env.info("Function: GroupStringSearch               LOADED OK")end
          
          function Generate_GroupSet() --Generates a set of Groups that have been filtered for Blacklisted group names
            local TempSET = SET_GROUP:New():FilterCategories("ground"):FilterActive(true):FilterOnce() --Creates a Moose Group Variable with all ground units
            local rtnSET = SET_GROUP:New()
            TempSET:ForEachGroup(function (MooseGroup)
              local GroupsName = MooseGroup:GetName()
          	if SGS_DEBUG == true then env.info("Checking Group - " .. GroupsName)end
          	if GroupStringSearch(t_SrchStringTable, GroupsName) == false then 
              if SGS_DEBUG == true then env.info("Group Loaded - " .. GroupsName)end
          	rtnSET:AddGroup(MooseGroup)
            end
           end)
          return rtnSET
          end
          if SGS_DEBUG == true then env.info("Function: Generate_GroupSet               LOADED OK")end
          
          --SCRIPT START
          env.info("Loaded Simple Group Saving, by Pikey (2018), updated by OzDeaDMeaT (2020), version " .. version)
          
          if file_exists(SaveFileName) then --Script has been run before, so we need to load the save
           if SGS_DEBUG == true then env.info(SaveFileName .. " save file found. Loading now....")end
           AllGroups = Generate_GroupSet()
           AllGroups:ForEachGroup(function (grp)
            grp:Destroy()
            end)
          
            dofile(SaveFileName)
            tempTable={}
            Spawn={}
          --RUN THROUGH THE KEYS IN THE TABLE (GROUPS)
            for k,v in pairs (SaveUnits) do
              units={}
          --RUN THROUGH THE UNITS IN EACH GROUP
                for i= 1, #(SaveUnits[k]["units"]) do 
            
          tempTable =
          
            { 
             ["type"]=SaveUnits[k]["units"][i]["type"],
             ["transportable"]= {["randomTransportable"] = false,}, 
             --["unitId"]=9000,used to generate ID's here but no longer doing that since DCS seems to handle it
             ["skill"]=SaveUnits[k]["units"][i]["skill"],
             ["y"]=SaveUnits[k]["units"][i]["y"] ,
             ["x"]=SaveUnits[k]["units"][i]["x"] ,
             ["name"]=SaveUnits[k]["units"][i]["name"],
             ["heading"]=SaveUnits[k]["units"][i]["heading"],
             ["playerCanDrive"]=true,  --hardcoded but easily changed.  
            }
          
                table.insert(units,tempTable)
              end --end unit for loop
          
          
          groupData = 
          
            {
              ["visible"] = true,
              --["lateActivation"] = false,
              ["tasks"] = {}, -- end of ["tasks"]
              ["uncontrollable"] = false,
              ["task"] = "Ground Nothing",
              --["taskSelected"] = true,
              --["route"] = 
              --{ 
              --["spans"] = {},
              --["points"]= {}
              -- },-- end of ["spans"] 
              --["groupId"] = 9000 + _count,
              ["hidden"] = false,
              ["units"] = units,
              ["y"] = SaveUnits[k]["y"],
              ["x"] = SaveUnits[k]["x"],
              ["name"] = SaveUnits[k]["name"],
              --["start_time"] = 0,
            } 
          
            coalition.addGroup(SaveUnits[k]["CountryID"], SaveUnits[k]["CategoryID"], groupData)
            groupData = {}
            end --end Group for loop
          
          else --Save File does not exist we start a fresh table, no spawns needed
          env.info("No File Found, Generating New file with name " .. SaveFileName)
          -- Added by OzDM
          SaveUnits={}
          AllGroups = Generate_GroupSet()
          end
          
          --THE SAVING SCHEDULE
          SCHEDULER:New( nil, function()
          env.info("SAVING     - Ground Forces...")
          if ALLOW_DROPIN_GROUPS == true then 
           AllGroups = Generate_GroupSet()
          end
          SavedUnitCount  = 0
          SavedGroupCount = 0
            AllGroups:ForEachGroupAlive(function (grp)
            local DCSgroup = Group.getByName(grp:GetName() )
            SavedGroupCount = SavedGroupCount + 1
            local size = DCSgroup:getSize()
            SavedUnitCount = SavedUnitCount + size
          _unittable={}
          
          for i = 1, size do
          
          local tmpTable =
          
            {   
              ["type"]=grp:GetUnit(i):GetTypeName(),
              ["transportable"]=true,
              ["unitID"]=grp:GetUnit(i):GetID(),
              ["skill"]="Average",
              ["y"]=grp:GetUnit(i):GetVec2().y,
              ["x"]=grp:GetUnit(i):GetVec2().x,
              ["name"]=grp:GetUnit(i):GetName(),
              ["playerCanDrive"]=true,
              ["heading"]=grp:GetUnit(i):GetHeading(),
            }
          
          table.insert(_unittable,tmpTable) --add units to a temporary table
          end
          
          SaveUnits[grp:GetName()] =
          {
             ["CountryID"]=grp:GetCountry(),
             ["SpawnCoalitionID"]=grp:GetCountry(),
             ["tasks"]={}, --grp:GetTaskMission(), --wrong gives the whole thing
             ["CategoryID"]=grp:GetCategory(),
             ["task"]="Ground Nothing",
             ["route"]={}, -- grp:GetTaskRoute(),
             ["groupId"]=grp:GetID(),
             --["SpawnCategoryID"]=grp:GetCategory(),
             ["units"]= _unittable,
             ["y"]=grp:GetVec2().y, 
             ["x"]=grp:GetVec2().x,
             ["name"]=grp:GetName(),
             ["start_time"]=0,
             ["CoalitionID"]=grp:GetCoalition(),
             ["SpawnCountryID"]=grp:GetCoalition(),
          }
          
          end)
          
          if (SavedGroupCount > 0) then 
           newMissionStr = IntegratedserializeWithCycles("SaveUnits",SaveUnits) --save the Table as a serialised type with key SaveUnits
           writemission(newMissionStr, SaveFileName)--write the file from the above to SaveUnits.lua
           env.info("SAVED      - " .. SavedUnitCount .. " units in " .. SavedGroupCount .." groups have been successfully saved to ".. SaveFileName)
          else
          if (file_exists(SaveFileName) and DELETE_FILE_ON_NO_GROUPS == true) then
            env.info("DELETING   - " .. SaveFileName)
            --io.remove (SaveFileName) seems to not work, not sure why.
           else
            env.info("SAVE EMPTY - " .. SaveFileName)
           end
          end
          
          --writemission(newMissionStr, "SaveUnits.lua")--write the file from the above to SaveUnits.lua
          SaveUnits={}--clear the table for a new write.
          --env.info("Data saved.")
          end, {}, 1, SaveScheduleUnits)
          -System Specs are:

          CPU: i9-9900K @ 4.9Ghz
          M/B: Asus TUF z390 Gaming Plus
          RAM: 32GB
          Video Card: EVGA RTX 2080 Ti XC Hybrid Gaming (11G-P4-2384)
          VR: Oculus Rift S
          Controls: Warthog Stick & Throttle, TPR pedals, Streamdeck XL

          Comment


            #45
            CTLD

            Has anyone been able to write the units back into CTLD to make it compatible?
            Last edited 10-13-2020, 09:48 PM.
            | CyperPower PC | AMD Ryzen 7 3700X @ 4.4 GHz | 64Gb DDR4 3200 MHz | Radeon RX 5700 8Gb | 32" Samsung Curve| Oculus Rift S | Thrustmaster T160000 HOTAS + Rudder Pedals | Windows 10-64 |

            Comment


              #46
              Pikey Hello, thanks for this script. I have question:
              How it will work with two missions that using saving script? Different units, diff locations, diff qty of units etc... So, first mission have SavedUnits.lua but what will happens if we stop current and load second mission? Will second mission script load units from first mission or will create new SavedUnits.lua (old will be deleted?). How to deal with that? Can SavedUnits.lua be renamed and be loaded only for specific mission or what...? Thanks.


              If you are a dedicated pilot, a team player ... join us !!!

              14th VFS Samurais Discord: https://discord.gg/uc9c7Vz
              Teamspeak: 14vfs.teamspeak3.com


              Немој ништа силом, узми већи чекић!

              MSI Tomahawk MAX | Ryzen 7 3700x | 32GB DDR4 3200MHz | RX 5700 XT OC Red Dragon 8GB

              Comment


                #47
                Anyone?


                If you are a dedicated pilot, a team player ... join us !!!

                14th VFS Samurais Discord: https://discord.gg/uc9c7Vz
                Teamspeak: 14vfs.teamspeak3.com


                Немој ништа силом, узми већи чекић!

                MSI Tomahawk MAX | Ryzen 7 3700x | 32GB DDR4 3200MHz | RX 5700 XT OC Red Dragon 8GB

                Comment


                  #48
                  Originally posted by Falcon_S View Post
                  Pikey Hello, thanks for this script. I have question:
                  How it will work with two missions that using saving script? Different units, diff locations, diff qty of units etc... So, first mission have SavedUnits.lua but what will happens if we stop current and load second mission? Will second mission script load units from first mission or will create new SavedUnits.lua (old will be deleted?). How to deal with that? Can SavedUnits.lua be renamed and be loaded only for specific mission or what...? Thanks.
                  Open the SGS.lua and edit lines 106,113 and 220 where it says "SaveUnits.lua" change it to "SaveUnitsMission1.lua". Then rename this SGS.lua file to SGSMission1.lua, save it, reload it in the mission editor and that should work. Do that for every different mission you want to run.
                  ▀▄▀▄▀▄ 24/7 Air Assault Operations ▄▀▄▀▄▀

                  Join our Discord!

                  Comment


                    #49
                    Thanks Guile !


                    If you are a dedicated pilot, a team player ... join us !!!

                    14th VFS Samurais Discord: https://discord.gg/uc9c7Vz
                    Teamspeak: 14vfs.teamspeak3.com


                    Немој ништа силом, узми већи чекић!

                    MSI Tomahawk MAX | Ryzen 7 3700x | 32GB DDR4 3200MHz | RX 5700 XT OC Red Dragon 8GB

                    Comment

                    Working...
                    X