Jump to content

Simple Saving Scripts


Go to solution Solved by Pikey,

Recommended Posts

I dont understand.

Press LeftShift-R to restart a mission.?

 

i use it in my server to capture airports red units die and blue units come over when all airports are captured how could i reset this to start again (sorry my weak English)

Link to comment
Share on other sites

Ah! Makes more sense. Delete the Save file in your DCS directory, the mission is back to the start.

i use it in my server to capture airports red units die and blue units come over when all airports are captured how could i reset this to start again (sorry my weak English)

___________________________________________________________________________

SIMPLE SCENERY SAVING * SIMPLE GROUP SAVING * SIMPLE STATIC SAVING *

Link to comment
Share on other sites

Ah! Makes more sense. Delete the Save file in your DCS directory, the mission is back to the start.

 

thanks Pikey

although it would be better if there is automatic way to delete it or reset

 

for example when all airports cuptured trigger do script

newMissionStr = IntegratedserializeWithCycles("SaveUnits",SaveUnits) --save the Table as a serialised type with key SaveUnits
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)


Edited by AYcoo
Link to comment
Share on other sites

thanks Pikey

although it would be better if there is automatic way to delete it or reset

 

for example when all airports cuptured trigger do script

newMissionStr = IntegratedserializeWithCycles("SaveUnits",SaveUnits) --save the Table as a serialised type with key SaveUnits
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)

 

Just do that then. People dont all use it the same.

For Moose

Have a SET_AIRBASE:New():FilterCoalitions("red"):FilterStart()

Count it's members. in a SCHEDULER.

If it reaches 0 then

os.remove() the SaveUnits.lua file

 

restart the mission.

 

You still need a way to restart the mission though, that's only avialable in the triggers of ME itself. Some people wouldnt like an immediate restart, just saying.

 

MOOSE is easy, come learn Lua on Discord. :)

___________________________________________________________________________

SIMPLE SCENERY SAVING * SIMPLE GROUP SAVING * SIMPLE STATIC SAVING *

Link to comment
Share on other sites

[quote=newMissionStr = IntegratedserializeWithCycles("SaveUnits",SaveUnits) --save the Table as a serialised type with key SaveUnits

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)

 

tried to put trigger do script but didn't work

 

i have moose in my mission but i don't know how :(

Link to comment
Share on other sites

Sorry teaching you Lua is beyond the scope of forum work. You have to delete the save file as I said. its in your DCS folder. I've provided you with what you need to know, but I'm not customising the script for you! :)

___________________________________________________________________________

SIMPLE SCENERY SAVING * SIMPLE GROUP SAVING * SIMPLE STATIC SAVING *

Link to comment
Share on other sites

  • 1 month later...

SimpleStatic Saving has been reworked as per 2nd post. I've fixed unit headings in SGS also.

 

 

I wanted to scribble my mad findings about statics as it's taken me the best part of the entire day to workaround the seemingly endless issues DCS has. But I learned something worth sharing.

 

 

1. Farps. Jeez-Ohh.

Farps (and rigs), as statics refuse to obey normal DCS static conventions in some of the methods you can use on them. In this way, I found it better to treat them as Airbases, since they reliably work in that manner. I've now excluded them fairly easily from attempting to be manipulated by checking if the name of the static is also an airbase. Mad scientist workaround, but its perfect. Farps and Rigs will not be touched. I've never destroyed one either, but just know that the script will not persist farps, or try to do anything with them. This means they stay in all of your missions through restarts. Which is what you want.

 

 

2. Other statics like "Effects".

Effects like Bigsmoke are fairly new to DCS and do not observe all of the characteristics of other statics. They are always dead, getDesc() and other requests for type, heading do not work, but they end up included as a Static by moose. I've filtered these with nil checks also.

 

 

3. Ships

For some reason ships always return that they are alive when asked. This absolutely puts the kaibosh on this script and there is nothing you can do about it either. I'd recommend only using ships as static units when you are decorating and couldnt give a rat's arse about their persistence. Statics will always work as cheap alternatives and visual fillers very nicely so are important because scaling multiplayer missions is vital. However ships are one to avoid for statics.

 

4. Cargo was fine and I tested the "Barrage Balloon" in the assets pack too. You have to remember that not all statics have a destroyed model. So you can bomb the DCS airshow crowd all you want and they will carry on cheering.

 

 

The other issue to touch on is the error that occurs when a static object is spawned. I use the core scripting method rather than SPAWNSTATIC because it's the only way I can spawn a dead object. I have a feeling FlightControl hated statics with a pasison and they litter moose with SET issues, filtering issues and bugs, just by the nature of them having had bugs and quirks.

The error is caused when the event handler for the birth of a dead static object is attempted to be added to the MOOSE DATABASE. It's a lot of error lines in the log, but it's entirely of no effect to either the game, Moose or the progressing of the script. Moose just can't deal with it because there is a missing index on the birth event, probably caused by the static itself being missing something from the event data. I'm choosing to ignore it for now as fixing it has no benefit to anyone once they know what it is. However, I've written code comemnts so you can choose to comment out the part of the script that replaces live statics with dead models of itself after a restart. If someone knows what they are doing, then they might have a care to do that and can. Someone that doesn't understand the error message, has no reason to ask how to remove it, since they should have read this or the known issues part and would also have desanitised their mission environment modules and are likely a danger to themselves and others with the copy/paste shortcut.;)

___________________________________________________________________________

SIMPLE SCENERY SAVING * SIMPLE GROUP SAVING * SIMPLE STATIC SAVING *

Link to comment
Share on other sites

  • 2 weeks later...
  • 3 weeks later...

Hey Pikey,

 

Awesome scripts. I'm trying to use it on my multiplayer dynamic campaign called OP Iranian Freedom on PG, Open Beta. My mission uses MOOSE CAP/GCI with borders. The saving process itself works well (for both statics & groups) but for some reasons, after a restart, my CAP & GCI groups handled by MOOSE are not getting spawned.

 

Have you got any idea ? That's the only issue I got so far.

 

Here are the scripts I'm using.

GCICAP_Iranian_Freedom.lua

SGS_Iranian_Freedom.lua

SSS_Iranian_Freedom.lua

[sIGPIC][/sIGPIC]

Link to comment
Share on other sites

  • 4 months later...

Hey Pikey,

 

Firstly great script. I am attempting to load the externally from the .miz file. It all loads and creates the file ok. I am doing the same for Moose first also. Rather than starting the Moose script on Mission Start (it crashes my server for some reason) I am running the external include.lua that is running Moose etc.

 

I got an external include script that is injected into the mission itself.

 

local missionScriptsDir = [[C:\DCS_SCRIPTS\OzDM_Syria_Persistence_Test\]]
dofile(missionScriptsDir ..[[include.lua]])

 

The include looks like this:

 

local mooseDir = [[C:\DCS_SCRIPTS\MOOSE\]]
dofile(mooseDir ..[[Moose.lua]])
---------------------------------------------------------------------------MOOSE
local missionScriptsDir = [[C:\DCS_SCRIPTS\OzDM_Syria_Persistence_Test\]]
dofile(missionScriptsDir ..[[sGS.lua]])

 

The file is generated correctly, its on reload that I am getting an error saying the file exists.

 

2020-09-02 16:24:44.424 INFO    SCRIPTING: *** MOOSE INCLUDE END ***
2020-09-02 16:24:44.424 INFO    SCRIPTING: Loaded Simple Group Saving, by Pikey, 2018, version 1.0
2020-09-02 16:24:44.424 ERROR   SCRIPTING: Mission script error: [string "C:\DCS_SCRIPTS\OzDM_Syria_Persistence_Test\SGS.lua"]:90: attempt to index global 'lfs' (a nil value)
stack traceback:
       [C]: ?
       [string "C:\DCS_SCRIPTS\OzDM_Syria_Persistence_Test\SGS.lua"]:90: in function 'file_exists'
       [string "C:\DCS_SCRIPTS\OzDM_Syria_Persistence_Test\SGS.lua"]:105: in main chunk
       [C]: in function 'dofile'
       [string "C:\DCS_SCRIPTS\OzDM_Syria_Persistence_Test\include.lua"]:5: in main chunk
       [C]: in function 'dofile'
       [string "C:\Users\C0RNER~1\AppData\Local\Temp\DCS.openbeta_server\/~mis00006B1D.lua"]:2: in main chunk

 

I have changed two lines in your script. Firstly I have it so the units list is saved every 10 minutes.

 

SaveScheduleUnits=600 --how many seconds between each check of all the statics.

 

Secondly I changed it so the File it saves out to is of a similar name to the mission file itself.

 

writemission(newMissionStr, "OzDM_Syria_Persistence_Test.lua")--write the file from the above to SaveUnits.lua

 

The server is running SLMOD and i keep commenting out the likes but each time the server starts it uncomments these two lines.

 

sanitizeModule('io')
sanitizeModule('lfs')

 

I believe this is the problem but ****ed if i know how to stop DCS from modifying the lines at server start.

CPU: i9-12900K @ 4.9Ghz

M/B: MSI MEG z690 Ace

RAM: 128GB

Video Card: MSI RTX 4090 Suprim Liquid X

VR: Varjo Aero

Link to comment
Share on other sites

OK, it turns out SLMOD was ****ing me up. I have since removed SLMOD while i try and work out how this thing is supposed to work.

 

I can't seem to get the vehicles to load correctly on startup. it seems to generate a new file and overwrite then old one with a full list of units again.

CPU: i9-12900K @ 4.9Ghz

M/B: MSI MEG z690 Ace

RAM: 128GB

Video Card: MSI RTX 4090 Suprim Liquid X

VR: Varjo Aero

Link to comment
Share on other sites

  • 3 weeks later...

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.

 

-- 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)

CPU: i9-12900K @ 4.9Ghz

M/B: MSI MEG z690 Ace

RAM: 128GB

Video Card: MSI RTX 4090 Suprim Liquid X

VR: Varjo Aero

Link to comment
Share on other sites

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

SGS-Fargo.lua

 

Banner EDForum2020.jpg

Have fun. Don't suck. Kill bad guys. 👍

https://discord.gg/blacksharkden/

Link to comment
Share on other sites

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)

 

-- 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)

CPU: i9-12900K @ 4.9Ghz

M/B: MSI MEG z690 Ace

RAM: 128GB

Video Card: MSI RTX 4090 Suprim Liquid X

VR: Varjo Aero

Link to comment
Share on other sites

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.

 

 

--[[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)

CPU: i9-12900K @ 4.9Ghz

M/B: MSI MEG z690 Ace

RAM: 128GB

Video Card: MSI RTX 4090 Suprim Liquid X

VR: Varjo Aero

Link to comment
Share on other sites

  • 3 weeks later...

CTLD

 

Has anyone been able to write the units back into CTLD to make it compatible?


Edited by Chandawg
  • Like 1

| 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 |

Link to comment
Share on other sites

  • 2 weeks later...

@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.

Quote

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

MSI Tomahawk MAX | Ryzen 7 3700x | 32GB DDR4 3200MHz | RX 5700 XT OC Red Dragon 8GB | VPC Throttle CM3 + VPC Constellation ALPHA on VPC WarBRD Base | HP Reverb G2

 Youtube Follow Me on TWITCH! 

Link to comment
Share on other sites

  • 2 weeks later...
@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.

  • Like 1

▀▄▀▄▀▄ DCS: Air Assault Operations ▄▀▄▀▄▀

 

Join the fight!

YouTube Channel

Link to comment
Share on other sites

  • 1 year later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...