Version 1.1.2

More persistence
This commit is contained in:
Christian Franz
2022-08-11 12:33:28 +02:00
parent 7ad32b89c4
commit 78ff9a10e0
22 changed files with 1445 additions and 239 deletions

View File

@@ -1,5 +1,5 @@
unitPersistence = {}
unitPersistence.version = '1.0.1'
unitPersistence.version = '1.1.1'
unitPersistence.verbose = false
unitPersistence.updateTime = 60 -- seconds. Once every minute check statics
unitPersistence.requiredLibs = {
@@ -15,6 +15,11 @@ unitPersistence.requiredLibs = {
- handles linked static objects
- does no longer mess with heliports
- update statics once a minute, not second
1.0.2 - fixes coalition bug for static objects
1.1.0 - added air and sea units - for filtering destroyed units
1.1.1 - fixed static link (again)
- fixed air spawn (fixed wing)
REQUIRES PERSISTENCE AND MX
@@ -23,6 +28,10 @@ unitPersistence.requiredLibs = {
--]]--
unitPersistence.groundTroops = {} -- local buffered copy that we
-- maintain from save to save
unitPersistence.fixedWing = {}
unitPersistence.rotorWing = {}
unitPersistence.ships = {}
unitPersistence.statics = {} -- locally unpacked and buffered static objects
--
@@ -49,18 +58,24 @@ function unitPersistence.saveData()
local uName = theUnitData.name
local gUnit = Unit.getByName(uName)
if gUnit and gUnit:isExist() then
-- got a live one!
gotALiveOne = true
-- update x and y and heading
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
pos = gUnit:getPoint()
theUnitData.x = pos.x
theUnitData.y = pos.z -- (!!)
-- ground units do not use alt
if gUnit:isActive() then
theUnitData.isDead = gUnit:getLife() < 1
if not theUnitData.isDead then
-- got a live one!
gotALiveOne = true
-- update x, y and heading
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
pos = gUnit:getPoint()
theUnitData.x = pos.x
theUnitData.y = pos.z -- (!!)
end
else
gotALiveOne = true -- not yet activated
end
else
theUnitData.isDead = true
end -- is alive and exists?
end -- unit not dead
end -- unit maybe not dead
end -- iterate units in group
groupData.isDead = not gotALiveOne
end -- if group is not dead
@@ -69,6 +84,136 @@ function unitPersistence.saveData()
end
end
-- aircraft
for groupName, groupData in pairs(unitPersistence.fixedWing) do
-- we update this record live and save it to file
if not groupData.isDead then
local gotALiveOne = false
local allUnits = groupData.units
for idx, theUnitData in pairs(allUnits) do
if not theUnitData.isDead then
local uName = theUnitData.name
local gUnit = Unit.getByName(uName)
if gUnit and gUnit:isExist() then
-- update x and y and heading if active and alive
if gUnit:isActive() then -- only overwrite if active
theUnitData.isDead = gUnit:getLife() < 1
if not theUnitData.isDead then
gotALiveOne = true -- this group is still alive
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
pos = gUnit:getPoint()
theUnitData.x = pos.x
theUnitData.y = pos.z -- (!!)
theUnitData.alt = pos.y
theUnitData.alt_type = "BARO"
theUnitData.speed = dcsCommon.vMag(gUnit:getVelocity())
-- we now could get fancy and do some proccing of the
-- waypoints and make the one it's nearest to its
-- current waypoint, curtailing all others, but that
-- may easily mess with waypoint actions, so we don't
end
else
gotALiveOne = true -- has not yet been activated, live
end
else
theUnitData.isDead = true
-- trigger.action.outText("+++unitPersistence - unit <" .. uName .. "> of group <" .. groupName .. "> is dead or non-existant", 30)
end -- is alive and exists?
end -- unit maybe not dead
end -- iterate units in group
groupData.isDead = not gotALiveOne
end -- if group is not dead
if unitPersistence.verbose then
trigger.action.outText("unitPersistence: save - processed air group <" .. groupName .. ">.", 30)
end
end
-- helos
for groupName, groupData in pairs(unitPersistence.rotorWing) do
-- we update this record live and save it to file
if not groupData.isDead then
local gotALiveOne = false
local allUnits = groupData.units
for idx, theUnitData in pairs(allUnits) do
if not theUnitData.isDead then
local uName = theUnitData.name
local gUnit = Unit.getByName(uName)
if gUnit and gUnit:isExist() then
-- update x and y and heading if active and alive
if gUnit:isActive() then -- only overwrite if active
theUnitData.isDead = gUnit:getLife() < 1
if not theUnitData.isDead then
gotALiveOne = true -- this group is still alive
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
pos = gUnit:getPoint()
theUnitData.x = pos.x
theUnitData.y = pos.z -- (!!)
theUnitData.alt = pos.y
theUnitData.alt_type = "BARO"
theUnitData.speed = dcsCommon.vMag(gUnit:getVelocity())
-- we now could get fancy and do some proccing of the
-- waypoints and make the one it's nearest to its
-- current waypoint, curtailing all others, but that
-- may easily mess with waypoint actions, so we don't
end
else
gotALiveOne = true -- has not yet been activated, live
end
else
theUnitData.isDead = true
trigger.action.outText("+++unitPersistence - unit <" .. uName .. "> of group <" .. groupName .. "> is dead or non-existant", 30)
end -- is alive and exists?
end -- unit maybe not dead
end -- iterate units in group
groupData.isDead = not gotALiveOne
end -- if group is not dead
if unitPersistence.verbose then
trigger.action.outText("unitPersistence: save - processed helo group <" .. groupName .. ">.", 30)
end
end
-- ships
for groupName, groupData in pairs(unitPersistence.ships) do
-- we update this record live and save it to file
if not groupData.isDead then
local gotALiveOne = false
local allUnits = groupData.units
for idx, theUnitData in pairs(allUnits) do
if not theUnitData.isDead then
local uName = theUnitData.name
local gUnit = Unit.getByName(uName)
if gUnit and gUnit:isExist() then
-- update x and y and heading if active and alive
if gUnit:isActive() then -- only overwrite if active
theUnitData.isDead = gUnit:getLife() < 1
if not theUnitData.isDead then
gotALiveOne = true -- this group is still alive
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
pos = gUnit:getPoint()
theUnitData.x = pos.x
theUnitData.y = pos.z -- (!!)
-- we only filter dead ships and don't mess with others
-- during load, so we are doing this solely for possible
-- later expansions
end
else
gotALiveOne = true -- has not yet been activated, live
end
else
theUnitData.isDead = true
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence - unit <" .. uName .. "> of group <" .. groupName .. "> is dead or non-existant", 30)
end
end -- is alive and exists?
end -- unit maybe not dead
end -- iterate units in group
groupData.isDead = not gotALiveOne
end -- if group is not dead
if unitPersistence.verbose then
trigger.action.outText("unitPersistence: save - processed ship group <" .. groupName .. ">.", 30)
end
end
-- process all static objects placed with ME
for oName, oData in pairs(unitPersistence.statics) do
if not oData.isDead or oData.lateActivation then
@@ -96,6 +241,10 @@ function unitPersistence.saveData()
theData.version = unitPersistence.version
theData.ground = unitPersistence.groundTroops
theData.fixedWing = unitPersistence.fixedWing
theData.rotorWing = unitPersistence.rotorWing
theData.ships = unitPersistence.ships
theData.statics = unitPersistence.statics
return theData
end
@@ -103,6 +252,13 @@ end
--
-- Load Mission Data
--
function unitPersistence.delayedSpawn(args)
local cat = args.cat
local cty = args.cty
local newGroup = args.newGroup
local theGroup = coalition.addGroup(cty, cat, newGroup)
end
function unitPersistence.loadMission()
local theData = persistence.getSavedDataForModule("unitPersistence")
if not theData then
@@ -137,7 +293,6 @@ function unitPersistence.loadMission()
-- filter all dead groups
if theUnitData.isDead then
-- skip it
else
-- add it to new group
table.insert(newUnits, theUnitData)
@@ -145,29 +300,129 @@ function unitPersistence.loadMission()
end
-- replace old unit setup with new
newGroup.units = newUnits
local cty = groupData.cty
local cat = groupData.cat
-- destroy the old group
--theGroup:destroy() -- will be replaced
-- spawn new one
-- spawn new one, replaces old one
theGroup = coalition.addGroup(cty, cat, newGroup)
if not theGroup then
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">")
end
if unitPersistence.verbose then
-- trigger.action.outText("+++unitPersistence: updated group <" .. groupName .. "> of cat <" .. cat .. "> for cty <" .. cty .. ">", 30)
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">", 30)
end
end
end
unitPersistence.groundTroops = theData.ground
else
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: no ground unit data.", 30)
end
end
if theData.fixedWing then
for groupName, groupData in pairs(theData.fixedWing) do
--trigger.action.outText("+++ start loading group <" .. groupName .. ">", 30)
local theGroup = Group.getByName(groupName)
if not theGroup then
mismatchWarning = true
elseif groupData.isDead then
theGroup:destroy()
elseif groupData.isPlayer then
-- skip it
else
local newGroup = dcsCommon.clone(groupData)
local newUnits = {}
for idx, theUnitData in pairs(groupData.units) do
-- filter all dead groups
if theUnitData.isDead then
-- skip it
else
-- add it to new group
table.insert(newUnits, theUnitData)
end
end
-- replace old unit setup with (delayed) new
newGroup.units = newUnits
local cty = groupData.cty
local cat = groupData.cat
-- spawn new one, replaces old one
theGroup:destroy()
local args = {}
args.cty = cty
args.cat = cat
args.newGroup = newGroup
-- since DCS can't replace a group directly (none will appear), we introduce a brief interval for things to settle
timer.scheduleFunction(unitPersistence.delayedSpawn, args, timer.getTime()+0.5)
end
end
unitPersistence.fixedWing = theData.fixedWing
else
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: no aircraft (fixed wing) unit data.", 30)
end
end
if theData.rotorWing then
for groupName, groupData in pairs(theData.rotorWing) do
local theGroup = Group.getByName(groupName)
if not theGroup then
mismatchWarning = true
elseif groupData.isDead then
theGroup:destroy()
elseif groupData.isPlayer then
-- skip it
else
local newGroup = dcsCommon.clone(groupData)
local newUnits = {}
for idx, theUnitData in pairs(groupData.units) do
-- filter all dead groups
if theUnitData.isDead then
-- skip it
else
-- add it to new group
table.insert(newUnits, theUnitData)
end
end
-- replace old unit setup with new
newGroup.units = newUnits
local cty = groupData.cty
local cat = groupData.cat
-- spawn new one, replaces old one
theGroup = coalition.addGroup(cty, cat, newGroup)
if not theGroup then
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">", 30)
end
end
end
unitPersistence.rotorWing = theData.rotorWing
else
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: no rotor wing unit data.", 30)
end
end
if theData.ships then
for groupName, groupData in pairs(theData.ships) do
local theGroup = Group.getByName(groupName)
if not theGroup then
mismatchWarning = true
elseif groupData.isDead then
-- when entire group is destroyed, we will also
-- destroy group. Else all survive
-- we currently don't dick around with carrieres unless they are dead
theGroup:destroy()
else
-- do nothing
end
end
unitPersistence.ships = theData.ships
else
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: no rotor wing unit data.", 30)
end
end
-- and now the same for static objects
if theData.statics then
for name, staticData in pairs(theData.statics) do
@@ -177,8 +432,6 @@ function unitPersistence.loadMission()
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: static <" .. name .. "> is late activate, no update", 30)
end
--elseif not theStatic then
-- mismatchWarning = true
elseif staticData.category == "Heliports" then
-- FARPS are static objects that HATE to be
-- messed with, so we don't
@@ -188,7 +441,7 @@ function unitPersistence.loadMission()
else
local newStatic = dcsCommon.clone(staticData)
-- add link info if it exists
newStatic.linkUnit = cfxMX.linkByName[name]
newStatic.linkUnit = cfxMX.linkByName[staticData.groupName]
if newStatic.linkUnit and unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: linked static <" .. name .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
end
@@ -197,15 +450,16 @@ function unitPersistence.loadMission()
-- spawn new one, replacing same.named old, dead if required
gStatic = coalition.addStaticObject(cty, newStatic)
if not gStatic then
trigger.action.outText("+++ failed to add modified static <" .. name .. ">")
trigger.action.outText("+++ failed to add modified static <" .. name .. ">", 30)
end
if unitPersistence.verbose then
local note = ""
if newStatic.dead then note = " (dead)" end
-- trigger.action.outText("+++unitPersistence: updated static <" .. name .. "> for cty <" .. cty .. ">" .. note, 30)
trigger.action.outText("+++unitPersistence: updated static <" .. name .. "> for cty <" .. cty .. ">" .. note, 30)
end
end
end
unitPersistence.statics = theData.statics
end
if mismatchWarning then
@@ -268,6 +522,7 @@ function unitPersistence.start()
-- create a local copy of the entire groundForces data that
-- we maintain internally. It's fixed, and we work on our
-- own copy for speed
unitPersistence.groundTroops = {}
for gname, data in pairs(cfxMX.allGroundByName) do
local gd = dcsCommon.clone(data) -- copy the record
gd.isDead = false -- init new field to alive
@@ -275,14 +530,71 @@ function unitPersistence.start()
gd.cat = cfxMX.catText2ID("vehicle")
local gGroup = Group.getByName(gname)
if not gGroup then
trigger.action.outText("+++warning: group <" .. gname .. "> does not exist in-game!?", 30)
trigger.action.outText("+++warning: ground group <" .. gname .. "> does not exist in-game!?", 30)
else
local firstUnit = gGroup:getUnit(1)
gd.cty = firstUnit:getCountry()
gd.cty = cfxMX.countryByName[gname]
unitPersistence.groundTroops[gname] = gd
end
end
-- now add all aircraft
unitPersistence.fixedWing = {}
for gname, data in pairs(cfxMX.allFixedByName) do
local gd = dcsCommon.clone(data) -- copy the record
gd.isDead = false -- init new field to alive
gd.isPlayer = (cfxMX.playerGroupByName[gname] ~= nil)
-- coalition and country
gd.cat = cfxMX.catText2ID("plane") -- 0
gd.cty = cfxMX.countryByName[gname]
local gGroup = Group.getByName(gname)
if gd.isPlayer then
-- skip
elseif not gGroup then
trigger.action.outText("+++warning: fixed-wing group <" .. gname .. "> does not exist in-game!?", 30)
else
unitPersistence.fixedWing[gname] = gd
end
end
-- and helicopters
unitPersistence.rotorWing = {}
for gname, data in pairs(cfxMX.allHeloByName) do
local gd = dcsCommon.clone(data) -- copy the record
gd.isDead = false -- init new field to alive
gd.isPlayer = (cfxMX.playerGroupByName[gname] ~= nil)
-- coalition and country
gd.cat = cfxMX.catText2ID("helicopter") -- 1
gd.cty = cfxMX.countryByName[gname]
local gGroup = Group.getByName(gname)
if gd.isPlayer then
-- skip
elseif not gGroup then
trigger.action.outText("+++warning: helo group <" .. gname .. "> does not exist in-game!?", 30)
else
unitPersistence.rotorWing[gname] = gd
end
end
-- finally ships
-- we only do ships to remove them when they are dead because
-- messing with ships can give problems: aircraft carriers.
unitPersistence.ships = {}
for gname, data in pairs(cfxMX.allSeaByName) do
local gd = dcsCommon.clone(data) -- copy the record
gd.isDead = false -- init new field to alive
-- coalition and country
gd.cat = cfxMX.catText2ID("ship") -- 3
gd.cty = cfxMX.countryByName[gname]
local gGroup = Group.getByName(gname)
if gd.isPlayer then
-- skip
elseif not gGroup then
trigger.action.outText("+++warning: ship group <" .. gname .. "> does not exist in-game!?", 30)
else
unitPersistence.ships[gname] = gd
end
end
-- make local copies of all static MX objects
-- that we also maintain internally, and convert them to game
-- spawnable objects
@@ -294,19 +606,18 @@ function unitPersistence.start()
local theStatic = dcsCommon.clone(staticData)
theStatic.isDead = false
theStatic.groupId = mxData.groupId
theStatic.groupName = name -- save top-level name
theStatic.cat = cfxMX.catText2ID("static")
theStatic.cty = cfxMX.countryByName[name]
--trigger.action.outText("Processed MX static group <" .. name .. ">, object <" .. name .. "> with cty <" .. theStatic.cty .. ">",30)
local gameOb = StaticObject.getByName(theStatic.name)
if not gameOb then
if unitPersistence.verbose then
trigger.action.outText("+++unitPersistence: static object <" .. theStatic.name .. "> has late activation", 30)
end
theStatic.lateActivation = true
else
--theStatic.cty = gameOb:getCountry()
--unitPersistence.statics[theStatic.name] = theStatic
end
unitPersistence.statics[theStatic.name] = theStatic
unitPersistence.statics[theStatic.name] = theStatic -- HERE WE CHANGE FROM GROUP NAME TO STATIC NAME!!!
end
end
@@ -328,6 +639,11 @@ if not unitPersistence.start() then
unitPersistence = nil
end
--[[--
ToDo: linked statics and linked units on restore
- waypoint analysis for aircraft so
- whan they have take off as Inital WP and they are moving
then we change the first WP to 'turning point'
- waypoint analysis to match waypoint to position. very difficult if waypoints describe a circle.
- group analysis for carriers to be able to process groups that do
not contain carriers
--]]--