mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
1526 lines
49 KiB
Lua
1526 lines
49 KiB
Lua
-- theDebugger
|
|
debugger = {}
|
|
debugger.version = "2.0.0"
|
|
debugDemon = {}
|
|
debugDemon.version = "2.0.0"
|
|
|
|
debugger.verbose = false
|
|
debugger.ups = 4 -- every 0.25 second
|
|
debugger.name = "DML Debugger" -- for compliance with cfxZones
|
|
|
|
debugger.log = ""
|
|
|
|
--[[--
|
|
Version History
|
|
1.0.0 - Initial version
|
|
1.0.1 - made ups available to config zone
|
|
- changed 'on' to 'active' in config zone
|
|
- merged debugger and debugDemon
|
|
- QoL check for 'debug' attribute (no '?')
|
|
1.1.0 - logging
|
|
- trigger.action --> debugger for outText
|
|
- persistence of logs
|
|
- save <name>
|
|
1.1.1 - warning when trying to set a flag to a non-int
|
|
1.1.2 - remove command
|
|
2.0.0 - dmlZones OOP
|
|
- eventmon command
|
|
- all, off, event #
|
|
- standard events
|
|
- adding events
|
|
- events? attribute from any zone
|
|
|
|
--]]--
|
|
|
|
debugger.requiredLibs = {
|
|
"dcsCommon", -- always
|
|
"cfxZones", -- Zones, of course
|
|
}
|
|
-- note: saving logs requires persistence module
|
|
-- will auto-abort saving if not present
|
|
|
|
|
|
debugger.debugZones = {}
|
|
debugger.debugUnits = {}
|
|
debugger.debugGroups = {}
|
|
debugger.debugObjects = {}
|
|
debugger.showEvents = {}
|
|
|
|
debugDemon.eventList = {
|
|
["0"] = "S_EVENT_INVALID = 0",
|
|
["1"] = "S_EVENT_SHOT = 1",
|
|
["2"] = "S_EVENT_HIT = 2",
|
|
["3"] = "S_EVENT_TAKEOFF = 3",
|
|
["4"] = "S_EVENT_LAND = 4",
|
|
["5"] = "S_EVENT_CRASH = 5",
|
|
["6"] = "S_EVENT_EJECTION = 6",
|
|
["7"] = "S_EVENT_REFUELING = 7",
|
|
["8"] = "S_EVENT_DEAD = 8",
|
|
["9"] = "S_EVENT_PILOT_DEAD = 9",
|
|
["10"] = "S_EVENT_BASE_CAPTURED = 10",
|
|
["11"] = "S_EVENT_MISSION_START = 11",
|
|
["12"] = "S_EVENT_MISSION_END = 12",
|
|
["13"] = "S_EVENT_TOOK_CONTROL = 13",
|
|
["14"] = "S_EVENT_REFUELING_STOP = 14",
|
|
["15"] = "S_EVENT_BIRTH = 15",
|
|
["16"] = "S_EVENT_HUMAN_FAILURE = 16",
|
|
["17"] = "S_EVENT_DETAILED_FAILURE = 17",
|
|
["18"] = "S_EVENT_ENGINE_STARTUP = 18",
|
|
["19"] = "S_EVENT_ENGINE_SHUTDOWN = 19",
|
|
["20"] = "S_EVENT_PLAYER_ENTER_UNIT = 20",
|
|
["21"] = "S_EVENT_PLAYER_LEAVE_UNIT = 21",
|
|
["22"] = "S_EVENT_PLAYER_COMMENT = 22",
|
|
["23"] = "S_EVENT_SHOOTING_START = 23",
|
|
["24"] = "S_EVENT_SHOOTING_END = 24",
|
|
["25"] = "S_EVENT_MARK_ADDED = 25",
|
|
["26"] = "S_EVENT_MARK_CHANGE = 26",
|
|
["27"] = "S_EVENT_MARK_REMOVED = 27",
|
|
["28"] = "S_EVENT_KILL = 28",
|
|
["29"] = "S_EVENT_SCORE = 29",
|
|
["30"] = "S_EVENT_UNIT_LOST = 30",
|
|
["31"] = "S_EVENT_LANDING_AFTER_EJECTION = 31",
|
|
["32"] = "S_EVENT_PARATROOPER_LENDING = 32",
|
|
["33"] = "S_EVENT_DISCARD_CHAIR_AFTER_EJECTION = 33",
|
|
["34"] = "S_EVENT_WEAPON_ADD = 34",
|
|
["35"] = "S_EVENT_TRIGGER_ZONE = 35",
|
|
["36"] = "S_EVENT_LANDING_QUALITY_MARK = 36",
|
|
["37"] = "S_EVENT_BDA = 37",
|
|
["38"] = "S_EVENT_AI_ABORT_MISSION = 38",
|
|
["39"] = "S_EVENT_DAYNIGHT = 39",
|
|
["40"] = "S_EVENT_FLIGHT_TIME = 40",
|
|
["41"] = "S_EVENT_PLAYER_SELF_KILL_PILOT = 41",
|
|
["42"] = "S_EVENT_PLAYER_CAPTURE_AIRFIELD = 42",
|
|
["43"] = "S_EVENT_EMERGENCY_LANDING = 43",
|
|
["44"] = "S_EVENT_UNIT_CREATE_TASK = 44",
|
|
["45"] = "S_EVENT_UNIT_DELETE_TASK = 45",
|
|
["46"] = "S_EVENT_SIMULATION_START = 46",
|
|
["47"] = "S_EVENT_WEAPON_REARM = 47",
|
|
["48"] = "S_EVENT_WEAPON_DROP = 48",
|
|
["49"] = "S_EVENT_UNIT_TASK_TIMEOUT = 49",
|
|
["50"] = "S_EVENT_UNIT_TASK_STAGE = 50",
|
|
["51"] = "S_EVENT_MAC_SUBTASK_SCORE = 51",
|
|
["52"] = "S_EVENT_MAC_EXTRA_SCORE = 52",
|
|
["53"] = "S_EVENT_MISSION_RESTART = 53",
|
|
["54"] = "S_EVENT_MISSION_WINNER = 54",
|
|
["55"] = "S_EVENT_POSTPONED_TAKEOFF = 55",
|
|
["56"] = "S_EVENT_POSTPONED_LAND = 56",
|
|
["57"] = "S_EVENT_MAX = 57",
|
|
}
|
|
--
|
|
-- Logging & saving
|
|
--
|
|
|
|
function debugger.outText(message, seconds, cls)
|
|
if not message then message = "" end
|
|
if not seconds then seconds = 20 end
|
|
if not cls then cls = false end
|
|
|
|
-- append message to log, and add a lf
|
|
if not debugger.log then debugger.log = "" end
|
|
debugger.log = debugger.log .. message .. "\n"
|
|
|
|
-- now hand up to trigger
|
|
trigger.action.outText(message, seconds, cls)
|
|
end
|
|
|
|
function debugger.saveLog(name)
|
|
if not _G["persistence"] then
|
|
debugger.outText("+++debug: persistence module required to save log")
|
|
return
|
|
end
|
|
|
|
if not persistence.active then
|
|
debugger.outText("+++debug: persistence module can't write. ensur you desanitize lfs and io")
|
|
return
|
|
end
|
|
|
|
if persistence.saveText(debugger.log, name) then
|
|
debugger.outText("+++debug: log saved to <" .. persistence.missionDir .. name .. ">")
|
|
else
|
|
debugger.outText("+++debug: unable to save log to <" .. persistence.missionDir .. name .. ">")
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- tracking flags
|
|
--
|
|
|
|
function debugger.addDebugger(theZone)
|
|
table.insert(debugger.debugZones, theZone)
|
|
end
|
|
|
|
function debugger.getDebuggerByName(aName)
|
|
for idx, aZone in pairs(debugger.debugZones) do
|
|
if aName == aZone.name then return aZone end
|
|
end
|
|
if debugger.verbose then
|
|
debugger.outText("+++debug: no debug zone with name <" .. aName ..">", 30)
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function debugger.removeDebugger(theZone)
|
|
local filtered = {}
|
|
for idx, dZone in pairs(debugger.debugZones) do
|
|
if dZone == theZone then
|
|
else
|
|
table.insert(filtered, dZone)
|
|
end
|
|
end
|
|
debugger.debugZones = filtered
|
|
end
|
|
--
|
|
-- read zone
|
|
--
|
|
function debugger.createDebuggerWithZone(theZone)
|
|
-- watchflag input trigger
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
|
if theZone.hasProperty("debugTriggerMethod") then
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty("debugTriggerMethod", "change")
|
|
elseif theZone:hasProperty("inputMethod") then
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty(theZone, "inputMethod", "change")
|
|
elseif theZone:hasProperty("sayWhen") then
|
|
theZone.debugInputMethod = theZone:getStringFromZoneProperty("sayWhen", "change")
|
|
end
|
|
|
|
-- say who we are and what we are monitoring
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText("---debug: adding zone <".. theZone.name .."> to look for <value " .. theZone.debugInputMethod .. "> in flag(s):", 30)
|
|
end
|
|
|
|
-- read main debug array
|
|
local theFlags = theZone:getStringFromZoneProperty("debug?", "<none>")
|
|
-- now, create an array from that
|
|
local flagArray = cfxZones.flagArrayFromString(theFlags)
|
|
local valueArray = {}
|
|
-- now establish current values
|
|
for idx, aFlag in pairs(flagArray) do
|
|
local fVal = theZone:getFlagValue(aFlag)
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText(" monitoring flag <" .. aFlag .. ">, inital value is <" .. fVal .. ">", 30)
|
|
end
|
|
valueArray[aFlag] = fVal
|
|
end
|
|
theZone.flagArray = flagArray
|
|
theZone.valueArray = valueArray
|
|
|
|
-- DML output method
|
|
theZone.debugOutputMethod = theZone:getStringFromZoneProperty("method", "inc")
|
|
if theZone:hasProperty("outputMethod") then
|
|
theZone.debugOutputMethod = theZone:getStringFromZoneProperty("outputMethod", "inc")
|
|
end
|
|
if theZone:hasProperty("debugMethod") then
|
|
theZone.debugOutputMethod = theZone:getStringFromZoneProperty("debugMethod", "inc")
|
|
end
|
|
|
|
-- notify!
|
|
if theZone:hasProperty("notify!") then
|
|
theZone.debugNotify = theZone:getStringFromZoneProperty("notify!", "<none>")
|
|
end
|
|
|
|
-- debug message, can use all messenger vals plus <f> for flag name
|
|
-- we use out own default
|
|
-- with <f> meaning flag name, <p> previous value, <c> current value
|
|
theZone.debugMsg = theZone:getStringFromZoneProperty("debugMsg", "---debug: <t> -- Flag <f> changed from <p> to <c> [<z>]")
|
|
end
|
|
|
|
function debugger.createEventMonWithZone(theZone)
|
|
local theFlags = theZone:getStringFromZoneProperty("events?", "<none>")
|
|
local flagArray = cfxZones.flagArrayFromString(theFlags)
|
|
local valueArray = {}
|
|
-- now establish current values
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText("*** monitoring events defined in <" .. theZone.name .. ">:", 30)
|
|
end
|
|
for idx, aFlag in pairs(flagArray) do
|
|
|
|
local evt = tonumber(aFlag)
|
|
if evt and (debugger.verbose or theZone.verbose) then
|
|
if evt < 0 then evt = 0 end
|
|
if evt > 57 then evt = 57 end
|
|
debugger.showEvents[evt] = debugDemon.eventList[tostring(evt)]
|
|
debugger.outText(" monitoring event <" .. debugger.showEvents[evt] .. ">", 30)
|
|
end
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Misc
|
|
--
|
|
function debugger.addFlagToObserver(flagName, theZone)
|
|
table.insert(theZone.flagArray, flagName)
|
|
local fVal = cfxZones.getFlagValue(flagName, theZone)
|
|
theZone.valueArray[flagName] = fVal
|
|
end
|
|
|
|
function debugger.removeFlagFromObserver(flagName, theZone)
|
|
local filtered = {}
|
|
for idy, aName in pairs(theZone.flagArray) do
|
|
if aName == flagName then
|
|
else
|
|
table.insert(filtered, aName)
|
|
end
|
|
end
|
|
theZone.flagArray = filtered
|
|
-- no need to clean up values, they are name-indexed. do it anyway
|
|
theZone.valueArray[flagName] = nil
|
|
end
|
|
|
|
function debugger.isObservingWithObserver(flagName, theZone)
|
|
for idy, aName in pairs(theZone.flagArray) do
|
|
if aName == flagName then
|
|
local val = theZone.valueArray[flagName]
|
|
return true, val
|
|
end
|
|
end
|
|
end
|
|
|
|
function debugger.isObserving(flagName)
|
|
-- scan all zones for flag, and return
|
|
-- zone, and flag value if observing
|
|
local observers = {}
|
|
for idx, theZone in pairs(debugger.debugZones) do
|
|
for idy, aName in pairs(theZone.flagArray) do
|
|
if aName == flagName then
|
|
table.insert(observers, theZone)
|
|
end
|
|
end
|
|
end
|
|
|
|
return observers
|
|
end
|
|
|
|
--
|
|
-- Update
|
|
--
|
|
function debugger.processDebugMsg(inMsg, theZone, theFlag, oldVal, currVal)
|
|
if not inMsg then return "<nil inMsg>" end
|
|
if not oldVal then oldVal = "<no val!>" else oldVal = tostring(oldVal) end
|
|
if not currVal then currVal = "<no val!>" else currVal = tostring(currVal) end
|
|
if not theFlag then theFlag = "<no flag!>" end
|
|
|
|
local formerType = type(inMsg)
|
|
if formerType ~= "string" then inMsg = tostring(inMsg) end
|
|
if not inMsg then inMsg = "<inMsg is incompatible type " .. formerType .. ">" end
|
|
|
|
-- build message by relacing wildcards
|
|
local outMsg = ""
|
|
|
|
-- replace line feeds
|
|
outMsg = inMsg:gsub("<n>", "\n")
|
|
if theZone then
|
|
outMsg = outMsg:gsub("<z>", theZone.name)
|
|
end
|
|
|
|
-- replace <C>, <p>, <f> with currVal, oldVal, flag
|
|
outMsg = outMsg:gsub("<c>", currVal)
|
|
outMsg = outMsg:gsub("<p>", oldVal)
|
|
outMsg = outMsg:gsub("<o>", oldVal) -- just for QoL
|
|
outMsg = outMsg:gsub("<f>", theFlag)
|
|
|
|
-- replace <t> with current mission time HMS
|
|
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
|
while absSecs > 86400 do
|
|
absSecs = absSecs - 86400 -- subtract out all days
|
|
end
|
|
local timeString = dcsCommon.processHMS("<:h>:<:m>:<:s>", absSecs)
|
|
outMsg = outMsg:gsub("<t>", timeString)
|
|
|
|
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
|
-- and <mgrs> with mgrs coords of zone point
|
|
if theZone then
|
|
local currPoint = cfxZones.getPoint(theZone)
|
|
local lat, lon, alt = coord.LOtoLL(currPoint)
|
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
|
outMsg = outMsg:gsub("<lat>", lat)
|
|
outMsg = outMsg:gsub("<lon>", lon)
|
|
currPoint = cfxZones.getPoint(theZone)
|
|
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
|
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
|
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
|
end
|
|
|
|
return outMsg
|
|
end
|
|
|
|
function debugger.debugZone(theZone)
|
|
-- check every flag of this zone
|
|
for idx, aFlag in pairs(theZone.flagArray) do
|
|
local oldVal = theZone.valueArray[aFlag]
|
|
local oldVal = theZone.valueArray[aFlag]
|
|
theZone.debugLastVal = oldVal
|
|
local hasChanged, newValue = cfxZones.testZoneFlag(
|
|
theZone,
|
|
aFlag,
|
|
theZone.debugInputMethod,
|
|
"debugLastVal")
|
|
-- we ALWAYS transfer latch back
|
|
theZone.valueArray[aFlag] = newValue
|
|
|
|
if hasChanged then
|
|
-- we are triggered
|
|
-- generate the ouput message
|
|
local msg = theZone.debugMsg
|
|
msg = debugger.processDebugMsg(msg, theZone, aFlag, oldVal, newValue)
|
|
debugger.outText(msg, 30)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
--
|
|
-- reset debugger
|
|
--
|
|
function debugger.resetObserver(theZone)
|
|
for idf, aFlag in pairs(theZone.flagArray) do
|
|
local fVal = cfxZones.getFlagValue(aFlag, theZone)
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText("---debug: resetting flag <" .. aFlag .. ">, to <" .. fVal .. "> for zone <" .. theZone.name .. ">", 30)
|
|
end
|
|
theZone.valueArray[aFlag] = fVal
|
|
end
|
|
end
|
|
|
|
function debugger.reset()
|
|
for idx, theZone in pairs(debugger.debugZones) do
|
|
-- reset this zone
|
|
debugger.resetObserver(theZone)
|
|
end
|
|
end
|
|
|
|
function debugger.showObserverState(theZone)
|
|
for idf, aFlag in pairs(theZone.flagArray) do
|
|
local fVal = cfxZones.getFlagValue(aFlag, theZone)
|
|
if debugger.verbose or theZone.verbose then
|
|
debugger.outText(" state of flag <" .. aFlag .. ">: <" .. theZone.valueArray[aFlag] .. ">", 30)
|
|
end
|
|
theZone.valueArray[aFlag] = fVal
|
|
end
|
|
end
|
|
|
|
function debugger.showState()
|
|
debugger.outText("---debug: CURRENT STATE <" .. dcsCommon.nowString() .. "> --- ", 30)
|
|
for idx, theZone in pairs(debugger.debugZones) do
|
|
-- show this zone's state
|
|
if #theZone.flagArray > 0 then
|
|
debugger.outText(" state of observer <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">:", 30)
|
|
debugger.showObserverState(theZone)
|
|
else
|
|
if theZone.verbose or debugger.verbose then
|
|
debugger.outText(" (empty observer <" .. theZone.name .. ">)", 30)
|
|
end
|
|
end
|
|
end
|
|
debugger.outText("---debug: end of state --- ", 30)
|
|
end
|
|
|
|
function debugger.doActivate()
|
|
debugger.active = true
|
|
if debugger.verbose or true then
|
|
debugger.outText("+++ DM Debugger is now active", 30)
|
|
end
|
|
end
|
|
|
|
function debugger.doDeactivate()
|
|
debugger.active = false
|
|
if debugger.verbose or true then
|
|
debugger.outText("+++ debugger deactivated", 30)
|
|
end
|
|
end
|
|
|
|
function debugger.update()
|
|
-- call me in a second to poll triggers
|
|
timer.scheduleFunction(debugger.update, {}, timer.getTime() + 1/debugger.ups)
|
|
|
|
-- first check for switch on or off
|
|
if debugger.onFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.onFlag, "change","lastOn") then
|
|
debugger.doActivate()
|
|
end
|
|
end
|
|
|
|
if debugger.offFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.offFlag, "change","lastOff") then
|
|
debugger.doDeactivate()
|
|
end
|
|
end
|
|
|
|
-- ALWAYS check for reset & state.
|
|
if debugger.resetFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.resetFlag, "change","lastReset") then
|
|
debugger.reset()
|
|
end
|
|
end
|
|
|
|
if debugger.stateFlag then
|
|
if cfxZones.testZoneFlag(debugger, debugger.stateFlag, "change","lastState") then
|
|
debugger.showState()
|
|
end
|
|
end
|
|
|
|
-- only progress if we are on
|
|
if not debugger.active then return end
|
|
|
|
for idx, aZone in pairs(debugger.debugZones) do
|
|
-- see if we are triggered
|
|
debugger.debugZone(aZone)
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Config & Start
|
|
--
|
|
function debugger.readConfigZone()
|
|
local theZone = cfxZones.getZoneByName("debuggerConfig")
|
|
if not theZone then
|
|
if debugger.verbose then
|
|
debugger.outText("+++debug: NO config zone!", 30)
|
|
end
|
|
theZone = cfxZones.createSimpleZone("debuggerConfig")
|
|
end
|
|
debugger.configZone = theZone
|
|
|
|
debugger.active = cfxZones.getBoolFromZoneProperty(theZone, "active", true)
|
|
debugger.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
|
|
|
if cfxZones.hasProperty(theZone, "on?") then
|
|
debugger.onFlag = cfxZones.getStringFromZoneProperty(theZone, "on?", "<none>")
|
|
debugger.lastOn = cfxZones.getFlagValue(debugger.onFlag, theZone)
|
|
end
|
|
|
|
if cfxZones.hasProperty(theZone, "off?") then
|
|
debugger.offFlag = cfxZones.getStringFromZoneProperty(theZone, "off?", "<none>")
|
|
debugger.lastOff = cfxZones.getFlagValue(debugger.offFlag, theZone)
|
|
end
|
|
|
|
if cfxZones.hasProperty(theZone, "reset?") then
|
|
debugger.resetFlag = cfxZones.getStringFromZoneProperty(theZone, "reset?", "<none>")
|
|
debugger.lastReset = cfxZones.getFlagValue(debugger.resetFlag, theZone)
|
|
end
|
|
|
|
if cfxZones.hasProperty(theZone, "state?") then
|
|
debugger.stateFlag = cfxZones.getStringFromZoneProperty(theZone, "state?", "<none>")
|
|
debugger.lastState = cfxZones.getFlagValue(debugger.stateFlag, theZone)
|
|
end
|
|
|
|
debugger.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 4)
|
|
|
|
if debugger.verbose then
|
|
debugger.outText("+++debug: read config", 30)
|
|
end
|
|
end
|
|
|
|
function debugger.start()
|
|
-- lib check
|
|
if not dcsCommon.libCheck then
|
|
trigger.action.outText("cfx debugger requires dcsCommon", 30)
|
|
return false
|
|
end
|
|
if not dcsCommon.libCheck("cfx debugger", debugger.requiredLibs) then
|
|
return false
|
|
end
|
|
|
|
-- read config
|
|
debugger.readConfigZone()
|
|
|
|
-- process debugger Zones
|
|
-- old style
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("debug?")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.createDebuggerWithZone(aZone) -- process attributes
|
|
debugger.addDebugger(aZone) -- add to list
|
|
end
|
|
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("debug")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' flag. Are you perhaps missing a '?'", 30)
|
|
end
|
|
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("events?")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.createEventMonWithZone(aZone) -- process attributes
|
|
end
|
|
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("events")
|
|
for k, aZone in pairs(attrZones) do
|
|
debugger.outText("***Warning: Zone <" .. aZone.name .. "> has a 'debug' flag. Are you perhaps missing a '?'", 30)
|
|
end
|
|
-- events
|
|
|
|
-- say if we are active
|
|
if debugger.verbose then
|
|
if debugger.active then
|
|
debugger.outText("+++debugger loaded and active", 30)
|
|
else
|
|
debugger.outText("+++ debugger: standing by for activation", 30)
|
|
end
|
|
end
|
|
|
|
-- start update
|
|
debugger.update()
|
|
|
|
debugger.outText("cfx debugger v" .. debugger.version .. " started.", 30)
|
|
return true
|
|
end
|
|
|
|
-- let's go!
|
|
if not debugger.start() then
|
|
trigger.action.outText("cfx debugger aborted: missing libraries", 30)
|
|
debugger = nil
|
|
end
|
|
|
|
|
|
--
|
|
-- DEBUG DEMON
|
|
--
|
|
|
|
debugDemon.myObserverName = "+DML Debugger+"
|
|
-- interactive interface for DML debugger
|
|
|
|
debugDemon.verbose = false
|
|
-- based on cfx stage demon
|
|
--[[--
|
|
Version History
|
|
1.0.0 - initial version
|
|
1.1.0 - save command, requires persistence
|
|
2.0.0 - eventmon
|
|
- dml zones OOP
|
|
--]]--
|
|
|
|
debugDemon.requiredLibs = {
|
|
"dcsCommon", -- always
|
|
"cfxZones", -- Zones, of course
|
|
"debugger",
|
|
}
|
|
debugDemon.markOfDemon = "-" -- all commands must start with this sequence
|
|
debugDemon.splitDelimiter = " "
|
|
debugDemon.commandTable = {} -- key, value pair for command processing per keyword
|
|
debugDemon.keepOpen = false -- keep mark open after a successful command
|
|
debugDemon.snapshot = {}
|
|
|
|
function debugDemon.hasMark(theString)
|
|
-- check if the string begins with the sequece to identify commands
|
|
if not theString then return false end
|
|
return theString:find(debugDemon.markOfDemon) == 1
|
|
end
|
|
|
|
|
|
-- main hook into DCS. Called whenever a Mark-related event happens
|
|
-- very simple: look if text begins with special sequence, and if so,
|
|
-- call the command processor.
|
|
function debugDemon:onEvent(theEvent)
|
|
-- first order of business: call the event monitor
|
|
debugDemon.doEventMon(theEvent)
|
|
|
|
-- now process our own
|
|
-- while we can hook into any of the three events,
|
|
-- we curently only utilize CHANGE Mark
|
|
if not (theEvent.id == world.event.S_EVENT_MARK_ADDED) and
|
|
not (theEvent.id == world.event.S_EVENT_MARK_CHANGE) and
|
|
not (theEvent.id == world.event.S_EVENT_MARK_REMOVED) then
|
|
-- not of interest for us, bye bye
|
|
return
|
|
end
|
|
|
|
-- when we get here, we have a mark event
|
|
|
|
if theEvent.id == world.event.S_EVENT_MARK_ADDED then
|
|
-- add mark is quite useless
|
|
end
|
|
|
|
if theEvent.id == world.event.S_EVENT_MARK_CHANGE then
|
|
-- when changed, the mark's text is examined for a command
|
|
-- if it starts with the 'mark' string ("-" by default) it is processed
|
|
-- by the command processor
|
|
-- if it is processed succesfully, the mark is immediately removed
|
|
-- else an error is displayed and the mark remains.
|
|
if debugDemon.hasMark(theEvent.text) then
|
|
-- strip the mark
|
|
local commandString = theEvent.text:sub(1+debugDemon.markOfDemon:len())
|
|
-- break remainder apart into <command> <arg1> ... <argn>
|
|
local commands = dcsCommon.splitString(commandString, debugDemon.splitDelimiter)
|
|
|
|
-- this is a command. process it and then remove it if it was executed successfully
|
|
local success = debugDemon.executeCommand(commands, theEvent)
|
|
|
|
-- remove this mark after successful execution
|
|
if success then
|
|
trigger.action.removeMark(theEvent.idx)
|
|
else
|
|
-- we could play some error sound
|
|
end
|
|
end
|
|
end
|
|
|
|
if theEvent.id == world.event.S_EVENT_MARK_REMOVED then
|
|
end
|
|
end
|
|
|
|
--
|
|
-- add / remove commands to/from vocabulary
|
|
--
|
|
function debugDemon.addCommndProcessor(command, processor)
|
|
debugDemon.commandTable[command:upper()] = processor
|
|
end
|
|
|
|
function debugDemon.removeCommandProcessor(command)
|
|
debugDemon.commandTable[command:upper()] = nil
|
|
end
|
|
|
|
--
|
|
-- process input arguments. Here we simply move them
|
|
-- up by one.
|
|
--
|
|
function debugDemon.getArgs(theCommands)
|
|
local args = {}
|
|
for i=2, #theCommands do
|
|
table.insert(args, theCommands[i])
|
|
end
|
|
return args
|
|
end
|
|
|
|
--
|
|
-- stage demon's main command interpreter.
|
|
-- magic lies in using the keywords as keys into a
|
|
-- function table that holds all processing functions
|
|
-- I wish we had that back in the Oberon days.
|
|
--
|
|
function debugDemon.executeCommand(theCommands, event)
|
|
local cmd = theCommands[1]
|
|
local arguments = debugDemon.getArgs(theCommands)
|
|
if not cmd then return false end
|
|
|
|
-- since we have a command in cmd, we remove this from
|
|
-- the string, and pass the remainder back
|
|
local remainder = event.text:sub(1 + debugDemon.markOfDemon:len())
|
|
remainder = dcsCommon.stripLF(remainder)
|
|
remainder = dcsCommon.trim(remainder)
|
|
remainder = remainder:sub(1+cmd:len())
|
|
remainder = dcsCommon.trim(remainder)
|
|
|
|
event.remainder = remainder
|
|
-- use the command as index into the table of functions
|
|
-- that handle them.
|
|
if debugDemon.commandTable[cmd:upper()] then
|
|
local theInvoker = debugDemon.commandTable[cmd:upper()]
|
|
local success = theInvoker(arguments, event)
|
|
return success
|
|
else
|
|
debugger.outText("***error: unknown command <".. cmd .. ">", 30)
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--
|
|
-- Helpers
|
|
--
|
|
|
|
function debugDemon.createObserver(aName)
|
|
local observer = cfxZones.createSimpleZone(aName)
|
|
observer.verbose = debugDemon.verbose
|
|
observer.debugInputMethod = "change"
|
|
observer.flagArray = {}
|
|
observer.valueArray = {}
|
|
observer.debugMsg = "---debug: <t> -- Flag <f> changed from <p> to <c> [<z>]"
|
|
return observer
|
|
end
|
|
--
|
|
-- COMMANDS
|
|
--
|
|
function debugDemon.processHelpCommand(args, event)
|
|
debugger.outText("*** debugger: commands are:" ..
|
|
"\n " .. debugDemon.markOfDemon .. "show <flagname/observername> -- show current values for flag or observer" ..
|
|
"\n " .. debugDemon.markOfDemon .. "set <flagname> <number> -- set flag to value <number>" ..
|
|
"\n " .. debugDemon.markOfDemon .. "inc <flagname> -- increase flag by 1, changing it" ..
|
|
"\n " .. debugDemon.markOfDemon .. "flip <flagname> -- when flag's value is 0, set it to 1, else to 0" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "observe <flagname> [with <observername>] -- observe a flag for change" ..
|
|
"\n " .. debugDemon.markOfDemon .. "o <flagname> [with <observername>] -- observe a flag for change" ..
|
|
"\n " .. debugDemon.markOfDemon .. "forget <flagname> [with <observername>] -- stop observing a flag" ..
|
|
"\n " .. debugDemon.markOfDemon .. "new <observername> [[for] <condition>] -- create observer for flags" ..
|
|
"\n " .. debugDemon.markOfDemon .. "update <observername> [[to] <condition>] -- change observer's condition" ..
|
|
"\n " .. debugDemon.markOfDemon .. "drop <observername> -- remove observer from debugger" ..
|
|
"\n " .. debugDemon.markOfDemon .. "list [<match>] -- list observers [name contains <match>]" ..
|
|
"\n " .. debugDemon.markOfDemon .. "who <flagname> -- all who observe <flagname>" ..
|
|
"\n " .. debugDemon.markOfDemon .. "reset [<observername>] -- reset all or only the named observer" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
|
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
|
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
|
"\n\n " .. debugDemon.markOfDemon .. "remove <group/unit/object name> -- remove named item from mission" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "eventmon [all | off | <number> | ?] -- show events for all | none | event <number> | list" ..
|
|
"\n\n " .. debugDemon.markOfDemon .. "q <Lua Var> -- Query value of Lua variable <Lua Var>" ..
|
|
"\n " .. debugDemon.markOfDemon .. "w <Lua Var> [=] <Lua Value> -- Write <Lua Value> to variable <Lua Var>" ..
|
|
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
|
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "save [<filename>] -- saves debugger log to storage" ..
|
|
|
|
"\n\n " .. debugDemon.markOfDemon .. "? or -help -- this text", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processNewCommand(args, event)
|
|
-- syntax new <observername> [[for] <condition>]
|
|
local observerName = args[1]
|
|
if not observerName then
|
|
debugger.outText("*** new: missing observer name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- see if this observer already existst
|
|
local theObserver = debugger.getDebuggerByName(observerName)
|
|
if theObserver then
|
|
debugger.outText("*** new: observer <" .. observerName .. "> already exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- little pitfall: what if the name contains blanks?
|
|
-- also check against remainder!
|
|
local remainderName = event.remainder
|
|
local rObserver = debugger.getDebuggerByName(remainderName)
|
|
if rObserver then
|
|
debugger.outText("*** new: observer <" .. remainderName .. "> already exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local theZone = nil
|
|
local condition = args[2] -- may need remainder instead
|
|
if condition == "for" then
|
|
-- we use arg[2]
|
|
else
|
|
observerName = remainderName -- we use entire rest of line
|
|
end
|
|
|
|
theZone = debugDemon.createObserver(observerName)
|
|
|
|
if condition == "for" then condition = args[3] end
|
|
if condition then
|
|
if not cfxZones.verifyMethod(condition, theZone) then
|
|
debugger.outText("*** new: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
|
return false
|
|
end
|
|
theZone.debugInputMethod = condition
|
|
end
|
|
|
|
debugger.addDebugger(theZone)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: new observer <" .. observerName .. "> for <" .. theZone.debugInputMethod .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processUpdateCommand(args, event)
|
|
-- syntax update <observername> [[to] <condition>]
|
|
local observerName = args[1]
|
|
if not observerName then
|
|
debugger.outText("*** update: missing observer name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- see if this observer already existst
|
|
local theZone = debugger.getDebuggerByName(observerName)
|
|
if not theZone then
|
|
debugger.outText("*** update: observer <" .. observerName .. "> does not exist exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local condition = args[2] -- may need remainder instead
|
|
if condition == "to" then condition = args[3] end
|
|
if condition then
|
|
if not cfxZones.verifyMethod(condition, theZone) then
|
|
debugger.outText("*** update: illegal trigger condition <" .. condition .. "> for observer <" .. observerName .. ">", 30)
|
|
return false
|
|
end
|
|
theZone.debugInputMethod = condition
|
|
end
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: updated observer <" .. observerName .. "> to <" .. theZone.debugInputMethod .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processDropCommand(args, event)
|
|
-- syntax drop <observername>
|
|
local observerName = event.remainder -- remainder
|
|
if not observerName then
|
|
debugger.outText("*** drop: missing observer name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- see if this observer already existst
|
|
local theZone = debugger.getDebuggerByName(observerName)
|
|
if not theZone then
|
|
debugger.outText("*** drop: observer <" .. observerName .. "> does not exist exists.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- now simply and irrevocable remove the observer, unless it's home,
|
|
-- in which case it's simply reset
|
|
if theZone == debugDemon.observer then
|
|
debugger.outText("*** drop: <" .. observerName .. "> is MY PRECIOUS and WILL NOT be dropped.", 30)
|
|
-- can't really happen since it contains blanks, but
|
|
-- we've seen stranger things
|
|
return false -- allows correction
|
|
end
|
|
|
|
debugger.removeDebugger(theZone)
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: dropped observer <" .. observerName .. ">", 30)
|
|
return true
|
|
end
|
|
-- observe command: add a new flag to observe
|
|
function debugDemon.processObserveCommand(args, event)
|
|
-- syntax: observe <flagname> [with <observername>]
|
|
-- args[1] is the name of the flag
|
|
local flagName = args[1]
|
|
if not flagName then
|
|
debugger.outText("*** observe: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local withTracker = nil
|
|
if args[2] == "with" then
|
|
local aName = args[3]
|
|
if not aName then
|
|
debugger.outText("*** observe: missing <observer name> after 'with'.", 30)
|
|
return false -- allows correction
|
|
end
|
|
aName = dcsCommon.stringRemainsStartingWith(event.remainder, aName)
|
|
withTracker = debugger.getDebuggerByName(aName)
|
|
if not withTracker then
|
|
-- withTracker = debugDemon.createObserver(aName)
|
|
-- debugger.addDebugger(withTracker)
|
|
debugger.outText("*** observe: no observer <" .. aName .. "> exists", 30)
|
|
return false -- allows correction
|
|
end
|
|
else -- not with as arg 2
|
|
if #args > 1 then
|
|
debugger.outText("*** observe: unknown command after flag name '" .. flagName .. "'.", 30)
|
|
return false -- allows correction
|
|
end
|
|
-- use own observer
|
|
withTracker = debugDemon.observer
|
|
end
|
|
|
|
if debugger.isObservingWithObserver(flagName, withTracker) then
|
|
debugger.outText("*** observe: already observing " .. flagName .. " with <" .. withTracker.name .. ">" , 30)
|
|
return true
|
|
end
|
|
|
|
-- we add flag to tracker and init value
|
|
debugger.addFlagToObserver(flagName, withTracker)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: now observing <" .. flagName .. "> for value " .. withTracker.debugInputMethod .. " with <" .. withTracker.name .. ">.", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processShowCommand(args, event)
|
|
-- syntax -show <name> with name either flag or observer
|
|
-- observer has precendce over flag
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** show: missing observer/flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
-- now see if we have an observer
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
local theObserver = debugger.getDebuggerByName(theName)
|
|
|
|
if not theObserver then
|
|
-- we directly use trigger.misc
|
|
local fVal = trigger.misc.getUserFlag(theName)
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] flag <" .. theName .. "> : value <".. fVal .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
-- if we get here, we want to show an entire observer
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flags observed by <" .. theName .. "> looking for <value ".. theObserver.debugInputMethod .. ">:", 30)
|
|
local flags = theObserver.flagArray
|
|
local values = theObserver.valueArray
|
|
for idx, flagName in pairs(flags) do
|
|
local lastVal = values[flagName]
|
|
local fVal = cfxZones.getFlagValue(flagName, theObserver)
|
|
-- add code to detect if it would trigger here
|
|
local hit = cfxZones.testFlagByMethodForZone(fVal, lastVal, theObserver.debugInputMethod, theObserver)
|
|
local theMark = " "
|
|
local trailer = ""
|
|
if hit then
|
|
theMark = " ! "
|
|
trailer = ", HIT!"
|
|
end
|
|
debugger.outText(theMark .. "f:<" .. flagName .. "> = <".. fVal .. "> [current, state = <" .. values[flagName] .. ">" .. trailer .. "]", 30)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.createSnapshot(allObservers)
|
|
if not allObservers then allObservers = {debugDemon.observer} end
|
|
local snapshot = {}
|
|
for idx, theZone in pairs(allObservers) do
|
|
|
|
-- iterate each observer
|
|
for idy, flagName in pairs (theZone.flagArray) do
|
|
local fullName = cfxZones.expandFlagName(flagName, theZone)
|
|
local fVal = trigger.misc.getUserFlag(fullName)
|
|
snapshot[fullName] = fVal
|
|
end
|
|
end
|
|
return snapshot
|
|
end
|
|
|
|
function debugDemon.processSnapCommand(args, event)
|
|
-- syntax snap [<observername>]
|
|
local allObservers = debugger.debugZones -- default: all zones
|
|
local theObserver = nil
|
|
|
|
local theName = args[1]
|
|
if theName then
|
|
-- now see if we have an observer
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
theObserver = debugger.getDebuggerByName(theName)
|
|
if not theObserver then
|
|
debugger.outText("*** snap: unknown observer name <" .. theName .. ">.", 30)
|
|
return false -- allows correction
|
|
end
|
|
end
|
|
|
|
if theObserver then
|
|
allObservers = {}
|
|
table.insert(allObservers, theObserver)
|
|
end
|
|
|
|
-- set up snapshot
|
|
local snapshot = debugDemon.createSnapshot(allObservers)
|
|
|
|
local sz = dcsCommon.getSizeOfTable(snapshot)
|
|
debugDemon.snapshot = snapshot
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: new snapshot created, " .. sz .. " flags.", 30)
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processCompareCommand(args, event)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: comparing snapshot with current flag values", 30)
|
|
for flagName, val in pairs (debugDemon.snapshot) do
|
|
local cVal = trigger.misc.getUserFlag(flagName)
|
|
local mark = ' '
|
|
if cVal ~= val then mark = ' ! ' end
|
|
debugger.outText(mark .. "<" .. flagName .. "> snap = <" .. val .. ">, now = <" .. cVal .. "> " .. mark, 30)
|
|
end
|
|
debugger.outText("*** END", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processNoteCommand(args, event)
|
|
local n = event.remainder
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "]: " .. n, 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processSetCommand(args, event)
|
|
-- syntax set <flagname> <value>
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** set: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local theVal = args[2]
|
|
if theVal and type(theVal) == "string" then
|
|
theVal = theVal:upper()
|
|
if theVal == "YES" or theVal == "TRUE" then theVal = "1" end
|
|
if theVal == "NO" or theVal == "FALSE" then theVal = "0" end
|
|
end
|
|
|
|
if not theVal or not (tonumber(theVal)) then
|
|
debugger.outText("*** set: missing or illegal value for flag <" .. theName .. ">.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
theVal = tonumber(theVal)
|
|
trigger.action.setUserFlag(theName, theVal)
|
|
-- we set directly, no cfxZones proccing
|
|
local note =""
|
|
-- flags are ints only?
|
|
if theVal ~= math.floor(theVal) then
|
|
note = " [int! " .. math.floor(theVal) .. "]"
|
|
end
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: set flag <" .. theName .. "> to <" .. theVal .. ">" .. note, 30)
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processIncCommand(args, event)
|
|
-- syntax inc <flagname>
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** inc: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local cVal = trigger.misc.getUserFlag(theName)
|
|
local nVal = cVal + 1
|
|
|
|
-- we set directly, no cfxZones procing
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: inc flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
|
trigger.action.setUserFlag(theName, nVal)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processFlipCommand(args, event)
|
|
-- syntax flip <flagname>
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** flip: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local cVal = trigger.misc.getUserFlag(theName)
|
|
if cVal == 0 then nVal = 1 else nVal = 0 end
|
|
|
|
-- we set directly, no cfxZones procing
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: flipped flag <" .. theName .. "> from <" .. cVal .. "> to <" .. nVal .. ">", 30)
|
|
trigger.action.setUserFlag(theName, nVal)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processListCommand(args, event)
|
|
-- syntax list or list <prefix>
|
|
local prefix = nil
|
|
prefix = args[1]
|
|
if prefix then
|
|
prefix = event.remainder -- dcsCommon.stringRemainsStartingWith(event.text, prefix)
|
|
end
|
|
if prefix then
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] listing observers whose name contains <" .. prefix .. ">:", 30)
|
|
else
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] listing all observers:", 30)
|
|
end
|
|
|
|
local allObservers = debugger.debugZones
|
|
for idx, theZone in pairs(allObservers) do
|
|
local theName = theZone.name
|
|
local doList = true
|
|
if prefix then
|
|
doList = dcsCommon.containsString(theName, prefix, false)
|
|
end
|
|
|
|
if doList then
|
|
debugger.outText(" <" .. theName .. "> for <value " .. theZone.debugInputMethod .. "> (" .. #theZone.flagArray .. " flags)", 30)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processWhoCommand(args, event)
|
|
-- syntax: who <flagname>
|
|
local flagName = event.remainder -- args[1]
|
|
if not flagName or flagName:len()<1 then
|
|
debugger.outText("*** who: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local observers = debugger.isObserving(flagName)
|
|
|
|
if not observers or #observers < 1 then
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently not observed", 30)
|
|
return false
|
|
end
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] flag <" .. flagName .. "> is currently observed by", 30)
|
|
for idx, theZone in pairs(observers) do
|
|
debugger.outText(" <" .. theZone.name .. "> looking for <value " .. theZone.debugInputMethod .. ">", 30)
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
function debugDemon.processForgetCommand(args, event)
|
|
-- syntax: forget <flagname> [with <observername>]
|
|
|
|
local flagName = args[1]
|
|
if not flagName then
|
|
debugger.outText("*** forget: missing flag name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
local withTracker = nil
|
|
if args[2] == "with" or args[2] == "from" then -- we also allow 'from'
|
|
local aName = args[3]
|
|
if not aName then
|
|
debugger.outText("*** forget: missing <observer name> after 'with'.", 30)
|
|
return false -- allows correction
|
|
end
|
|
|
|
aName = dcsCommon.stringRemainsStartingWith(event.remainder, aName)
|
|
withTracker = debugger.getDebuggerByName(aName)
|
|
if not withTracker then
|
|
debugger.outText("*** forget: no observer named <" .. aName .. ">", 30)
|
|
return false
|
|
end
|
|
else -- not with as arg 2
|
|
if #args > 1 then
|
|
debugger.outText("*** forget: unknown command after flag name '" .. flagName .. "'.", 30)
|
|
return false -- allows correction
|
|
end
|
|
-- use own observer
|
|
withTracker = debugDemon.observer
|
|
end
|
|
|
|
if not debugger.isObservingWithObserver(flagName, withTracker) then
|
|
debugger.outText("*** forget: observer <" .. withTracker.name .. "> does not observe flag <" .. flagName .. ">", 30)
|
|
return false
|
|
end
|
|
|
|
-- we add flag to tracker and init value
|
|
debugger.removeFlagFromObserver(flagName, withTracker)
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger: no longer observing " .. flagName .. " with <" .. withTracker.name .. ">.", 30)
|
|
return true
|
|
end
|
|
|
|
|
|
function debugDemon.processStartCommand(args, event)
|
|
debugger.doActivate()
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processStopCommand(args, event)
|
|
debugger.doDeactivate()
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processResetCommand(args, event)
|
|
-- supports reset <observer>
|
|
-- syntax: forget <flagname> [with <observername>]
|
|
|
|
local obsName = args[1]
|
|
if not obsName then
|
|
debugger.reset() -- reset all
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: reset complete.", 30)
|
|
return true -- allows correction
|
|
end
|
|
|
|
local withTracker = nil
|
|
local aName = event.remainder
|
|
withTracker = debugger.getDebuggerByName(aName)
|
|
if not withTracker then
|
|
debugger.outText("*** reset: no observer <" .. aName .. ">", 30)
|
|
return false
|
|
end
|
|
|
|
debugger.resetObserver(withTracker)
|
|
|
|
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debugger:reset observer <" .. withTracker.name .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processSaveCommand(args, event)
|
|
-- save log to file, requires persistence module
|
|
-- syntax: -save [<fileName>]
|
|
local aName = event.remainder
|
|
if not aName or aName:len() < 1 then
|
|
aName = "DML Debugger Log"
|
|
end
|
|
if not dcsCommon.stringEndsWith(aName, ".txt") then
|
|
aName = aName .. ".txt"
|
|
end
|
|
debugger.saveLog(aName)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processRemoveCommand(args, event)
|
|
-- remove a group, unit or object
|
|
-- try group first
|
|
local aName = event.remainder
|
|
if not aName or aName:len() < 1 then
|
|
debugger.outText("*** remove: no remove target", 30)
|
|
return false
|
|
end
|
|
|
|
aName = dcsCommon.trim(aName)
|
|
local theGroup = Group.getByName(aName)
|
|
if theGroup and theGroup:isExist() then
|
|
theGroup:destroy()
|
|
debugger.outText("*** remove: removed group <" .. aName .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
local theUnit = Unit.getByName(aName)
|
|
if theUnit and theUnit:isExist() then
|
|
theUnit:destroy()
|
|
debugger.outText("*** remove: removed unit <" .. aName .. ">", 30)
|
|
return true
|
|
end
|
|
|
|
local theStatic = StaticObject.getByName(aName)
|
|
if theStatic and theStatic:isExist() then
|
|
theStatic:destroy()
|
|
debugger.outText("*** remove: removed static object <" .. aName .. ">", 30)
|
|
return true
|
|
end
|
|
debugger.outText("*** remove: did not find anything called <" .. aName .. "> to remove", 30)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.doEventMon(theEvent)
|
|
if not theEvent then return end
|
|
local ID = theEvent.id
|
|
if debugger.showEvents[ID] then
|
|
-- we show this event
|
|
m = "*** event <" .. debugger.showEvents[ID] .. ">"
|
|
-- see if we have initiator
|
|
if theEvent.initiator then
|
|
local theUnit = theEvent.initiator
|
|
if Unit.isExist(theUnit) then
|
|
m = m .. " for "
|
|
if theUnit.getPlayerName and theUnit:getPlayerName() then
|
|
m = m .. "player = " .. theUnit:getPlayerName() .. " in "
|
|
end
|
|
m = m .. "unit <" .. theUnit:getName() .. ">"
|
|
end
|
|
end
|
|
debugger.outText(m, 30)
|
|
end
|
|
end
|
|
|
|
function debugDemon.processEventMonCommand(args, event)
|
|
-- turn event monito on/off
|
|
-- syntax: -eventmon on|off
|
|
local aParam = dcsCommon.trim(event.remainder)
|
|
if not aParam or aParam:len() < 1 then
|
|
aParam = "all"
|
|
end
|
|
aParam = string.upper(aParam)
|
|
evtNum = tonumber(aParam)
|
|
if aParam == "ON" or aParam == "ALL" then
|
|
-- debugger.eventmon = true
|
|
debugger.outText("*** eventmon: turned ON, showing ALL events", 30)
|
|
local events = {}
|
|
for idx,evt in pairs(debugDemon.eventList) do
|
|
events[tonumber(idx)] = evt
|
|
end
|
|
debugger.showEvents = events
|
|
elseif evtNum then -- add the numbered to
|
|
debugger.eventmon = false
|
|
if evtNum <= 0 then evtNum = 0 end
|
|
if evtNum >= 57 then evtNum = 35 end
|
|
debugger.showEvents[evtNum] = debugDemon.eventList[tostring(evtNum)]
|
|
debugger.outText("*** eventmon: added event <" .. debugger.showEvents[evtNum] .. ">", 30)
|
|
elseif aParam == "OFF" then
|
|
debugger.showEvents = {}
|
|
debugger.outText("*** eventmon: removed all events from monitor list", 30)
|
|
elseif aParam == "?" then
|
|
local m = "*** eventmon: currently tracking these events:"
|
|
for idx, evt in pairs(debugger.showEvents) do
|
|
m = m .. "\n" .. evt
|
|
end
|
|
debugger.outText(m .. "\n*** end of list", 30)
|
|
else
|
|
debugger.outText("*** eventmon: unknown parameter <" .. event.remainder .. ">", 30)
|
|
end
|
|
return true
|
|
end
|
|
|
|
--
|
|
-- read and write directly to Lua tables
|
|
--
|
|
|
|
function debugDemon.processQueryCommand(args, event)
|
|
-- syntax -q <name> with name a (qualified) Lua table reference
|
|
local theName = args[1]
|
|
-- local p = args [2]
|
|
-- trigger.action.outText("args1 = " .. theName, 30)
|
|
-- if args[2] then trigger.action.outText("param = " .. args[2], 30) end
|
|
if not theName then
|
|
debugger.outText("*** q: missing Lua table/element name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
theName = dcsCommon.stringRemainsStartingWith(event.remainder, theName)
|
|
|
|
-- put this into a string, and execute it
|
|
local exec = "return " .. theName
|
|
local f = loadstring(exec)
|
|
local res
|
|
if pcall(f) then
|
|
res = f()
|
|
if type(res) == "boolean" then
|
|
res = "[BOOL FALSE]"
|
|
if res then res = "[BOOL TRUE]" end
|
|
elseif type(res) == "table" then res = "[Lua Table]"
|
|
elseif type(res) == "nil" then res = "[NIL]"
|
|
elseif type(res) == "function" then res = "[Lua Function]"
|
|
elseif type(res) == "number" or type(res) == "string" then
|
|
res = res .. " (a " .. type(res) .. ")"
|
|
else res = "[Lua " .. type(res) .. "]"
|
|
end
|
|
else
|
|
res = "[Lua error]"
|
|
end
|
|
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] <" .. theName .. "> = ".. res, 30)
|
|
|
|
return true
|
|
end
|
|
|
|
function debugDemon.processWriteCommand(args, event)
|
|
-- syntax -w <name> <value> with name a (qualified) Lua table reference and value a Lua value (including strings, with quotes of course). {} means an empty set etc. you CAN call into DCS MSE with this, and create a lot of havoc.
|
|
-- also, allow "=" semantic, -w p = {x=1, y=2}
|
|
|
|
local theName = args[1]
|
|
if not theName then
|
|
debugger.outText("*** w: missing Lua table/element name.", 30)
|
|
return false -- allows correction
|
|
end
|
|
local param = args [2]
|
|
if param == "=" then param = args[3] end
|
|
if not param then
|
|
debugger.outText("*** w: missing value to set to")
|
|
return false
|
|
end
|
|
|
|
param = dcsCommon.stringRemainsStartingWith(event.remainder, param)
|
|
|
|
-- put this into a string, and execute it
|
|
local exec = theName .. " = " .. param
|
|
local f = loadstring(exec)
|
|
local res
|
|
if pcall(f) then
|
|
res = "<" .. theName .. "> set to <" .. param .. ">"
|
|
else
|
|
res = "[Unable to set - Lua error]"
|
|
end
|
|
|
|
debugger.outText("[" .. dcsCommon.nowString() .. "] " .. res, 30)
|
|
|
|
return true
|
|
end
|
|
|
|
|
|
--
|
|
-- init and start
|
|
--
|
|
|
|
function debugDemon.readConfigZone()
|
|
local theZone = cfxZones.getZoneByName("debugDemonConfig")
|
|
if not theZone then
|
|
if debugDemon.verbose then
|
|
debugger.outText("+++debug (daemon): NO config zone!", 30)
|
|
end
|
|
theZone = cfxZones.createSimpleZone("debugDemonConfig")
|
|
end
|
|
debugDemon.configZone = theZone
|
|
|
|
debugDemon.keepOpen = cfxZones.getBoolFromZoneProperty(theZone, "keepOpen", false)
|
|
|
|
debugDemon.markOfDemon = cfxZones.getStringFromZoneProperty(theZone,"mark", "-") -- all commands must start with this sequence
|
|
|
|
|
|
debugDemon.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
|
|
|
|
|
if debugger.verbose then
|
|
debugger.outText("+++debug (deamon): read config", 30)
|
|
end
|
|
end
|
|
|
|
function debugDemon.init()
|
|
if not dcsCommon.libCheck then
|
|
trigger.action.outText("cfx interactive debugger requires dcsCommon", 30)
|
|
return false
|
|
end
|
|
if not dcsCommon.libCheck("cfx interactive debugger", debugDemon.requiredLibs) then
|
|
return false
|
|
end
|
|
|
|
-- config
|
|
debugDemon.readConfigZone()
|
|
|
|
-- now add known commands to interpreter.
|
|
debugDemon.addCommndProcessor("observe", debugDemon.processObserveCommand)
|
|
debugDemon.addCommndProcessor("o", debugDemon.processObserveCommand) -- synonym
|
|
|
|
debugDemon.addCommndProcessor("forget", debugDemon.processForgetCommand)
|
|
|
|
debugDemon.addCommndProcessor("show", debugDemon.processShowCommand)
|
|
debugDemon.addCommndProcessor("set", debugDemon.processSetCommand)
|
|
debugDemon.addCommndProcessor("inc", debugDemon.processIncCommand)
|
|
debugDemon.addCommndProcessor("flip", debugDemon.processFlipCommand)
|
|
debugDemon.addCommndProcessor("list", debugDemon.processListCommand)
|
|
debugDemon.addCommndProcessor("who", debugDemon.processWhoCommand)
|
|
|
|
debugDemon.addCommndProcessor("new", debugDemon.processNewCommand)
|
|
debugDemon.addCommndProcessor("update", debugDemon.processUpdateCommand)
|
|
debugDemon.addCommndProcessor("drop", debugDemon.processDropCommand)
|
|
|
|
debugDemon.addCommndProcessor("snap", debugDemon.processSnapCommand)
|
|
debugDemon.addCommndProcessor("compare", debugDemon.processCompareCommand)
|
|
debugDemon.addCommndProcessor("note", debugDemon.processNoteCommand)
|
|
|
|
debugDemon.addCommndProcessor("start", debugDemon.processStartCommand)
|
|
debugDemon.addCommndProcessor("stop", debugDemon.processStopCommand)
|
|
debugDemon.addCommndProcessor("reset", debugDemon.processResetCommand)
|
|
|
|
debugDemon.addCommndProcessor("save", debugDemon.processSaveCommand)
|
|
|
|
debugDemon.addCommndProcessor("?", debugDemon.processHelpCommand)
|
|
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
|
|
|
debugDemon.addCommndProcessor("remove", debugDemon.processRemoveCommand)
|
|
|
|
debugDemon.addCommndProcessor("eventmon", debugDemon.processEventMonCommand)
|
|
debugDemon.addCommndProcessor("q", debugDemon.processQueryCommand)
|
|
debugDemon.addCommndProcessor("w", debugDemon.processWriteCommand)
|
|
return true
|
|
end
|
|
|
|
function debugDemon.start()
|
|
-- add my own debug zones to debugger so it can
|
|
-- track any changes
|
|
|
|
local observer = debugDemon.createObserver(debugDemon.myObserverName)
|
|
debugDemon.observer = observer
|
|
debugger.addDebugger(observer)
|
|
|
|
-- create initial snapshot
|
|
debugDemon.snapshot = debugDemon.createSnapshot(debugger.debugZones)
|
|
debugDemon.demonID = world.addEventHandler(debugDemon)
|
|
|
|
debugger.outText("interactive debugDemon v" .. debugDemon.version .. " started" .. "\n enter " .. debugDemon.markOfDemon .. "? in a map mark for help", 30)
|
|
|
|
if not _G["persistence"] then
|
|
debugger.outText("\n note: '-save' disabled, no persistence module found", 30)
|
|
end
|
|
end
|
|
|
|
if debugDemon.init() then
|
|
debugDemon.start()
|
|
else
|
|
trigger.action.outText("*** interactive debugger failed to initialize.", 30)
|
|
debugDemon = {}
|
|
end
|
|
|
|
--[[--
|
|
- track units/groups/objects: health changes
|
|
- track players: unit change, enter, exit
|
|
- inspect objects, dumping category, life, if it's tasking, latLon, alt, speed, direction
|
|
|
|
- exec files. save all commands and then run them from script
|
|
|
|
- query objects: -q persistence.active returns boolean, true
|
|
-q x.y returns table, 12 elements
|
|
-q a.b.x returns number 12
|
|
-q d.e.f returns string "asdasda..."
|
|
-q sada returs <nil>
|
|
|
|
- xref: which zones/attributes reference a flag, g.g. '-xref go'
|
|
|
|
- dml version can config to start with events list, e.g. 1, 4, 7
|
|
--]]--
|