Skip to content

Commit

Permalink
Bots / sharedFunctions: Added fgcom.hooks.cleanupPluginData_entry(sid…
Browse files Browse the repository at this point in the history
…, iid)

  Its called when cleaning up an entry.
  return false to prevent the entry to be cleaned out.

Server/Statusbot: add more hooks, and try to prevent duplicate entries (#177)

Server/Statusbot: make sure, the data structure has a type field default

fix #174 (hopefully)
maybe also #177
  • Loading branch information
hbeni committed Mar 9, 2024
1 parent 80f70e6 commit a03e0b7
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 33 deletions.
30 changes: 28 additions & 2 deletions server/sharedFunctions.inc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,9 @@ fgcom = {
end

-- check if we already know this clients identity with given iid; if not, add template
if not fgcom_clients[sid][iid] then
if fgcom_clients[sid][iid] then
if fgcom.hooks.parsePluginData_updateKnownClient ~= nil then fgcom.hooks.parsePluginData_updateKnownClient(sid, iid) end
else
fgcom_clients[sid][iid] = {
callsign="",
lat="",
Expand All @@ -317,6 +319,7 @@ fgcom = {
radios={},
lastUpdate=0
}
if fgcom.hooks.parsePluginData_newClient ~= nil then fgcom.hooks.parsePluginData_newClient(sid, iid) end
end

-- record that we had an data update
Expand Down Expand Up @@ -387,6 +390,9 @@ fgcom = {
elseif packtype == "ICANHAZDATAPLZ" then
-- ignore for now
end

fgcom.dbg("Packet fully processed.")
if fgcom.hooks.parsePluginData_processedPacket ~= nil then fgcom.hooks.parsePluginData_processedPacket(sender, packtype, dataID_t) end
end

fgcom.dbg("Parsing done. New remote state:")
Expand All @@ -402,6 +408,9 @@ fgcom = {
fgcom.dbg("sid="..uid.."; idty="..iid.." radio #"..radio_id.." pwr='"..radio.power.."'")
fgcom.dbg("sid="..uid.."; idty="..iid.." radio #"..radio_id.." opr='"..radio.operable.."'")
end
elseif k == "lastUpdate" then
local last_updated_since = os.time() - v
fgcom.dbg("sid="..uid.."; idty="..iid.."\t"..k..":\t"..tostring(v).." ("..last_updated_since.."s ago)")
else
fgcom.dbg("sid="..uid.."; idty="..iid.."\t"..k..":\t"..tostring(v))
end
Expand All @@ -420,7 +429,10 @@ fgcom = {
local stale_since = os.time() - idty.lastUpdate
if stale_since > fgcom.data.cleanupTimeout then
fgcom.dbg("cleanup remote data: sid="..uid.."; idty="..iid.." stale_since="..stale_since)
fgcom_clients[uid][iid] = nil;
local process = true
if fgcom.hooks.cleanupPluginData_entry ~= nil then process=fgcom.hooks.cleanupPluginData_entry(uid, iid) end

if process then fgcom_clients[uid][iid] = nil end
end
end
end
Expand Down Expand Up @@ -487,6 +499,20 @@ fgcom = {
hooks = {
-- parsePluginData_afterParseIID(sid, iid)
-- called when parsePluginData() received data for a given iid

-- fgcom.hooks.parsePluginData_newClient(sid, iid)
-- called when parsePluginData() detected that the client was not seen before.
-- is called before any datas is parsed/added.

-- fgcom.hooks.parsePluginData_updateKnownClient(sid, iid)
-- called when parsePluginData() detected that the client was known.
-- is called before any datas is parsed/updated.

-- fgcom.hooks.parsePluginData_processedPacket(mumble_user, packtype, dataID_t)
-- called after processing the packet, passing raw data

-- fgcom.hooks.cleanupPluginData_entry(sid, iid)
-- called when cleaning up an entry. return false to prevent the entry to be cleaned out.
}
}

Expand Down
132 changes: 101 additions & 31 deletions server/statuspage/fgcom-status.bot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -129,45 +129,75 @@ local generateOutData = function()
for i, mc in ipairs(client:getUsers()) do allUsers[mc:getSession()] = mc end
local data = {clients={}, meta={}} -- final return array

-- Record already processed entries (Issue #177)
-- TODO: probably I''m just to dumb to properly use lua in the first place. So consider this a workaround.
-- If you ever see this comment and think otherwise, let me know so I can remove this embarrassing bit of comment and/or code. :)
local already_processed_clients = {}

-- generate list of current users
local users_alive = 0
for sid, remote_client in pairs(fgcom_clients) do
for iid,user in pairs(remote_client) do
fgcom.dbg("generateOutData(): processing user: "..sid.." with idty="..iid)
local userData = {} -- the return structure for generating the message
local mumbleUser = allUsers[sid]
if not mumbleUser then
fgcom.dbg("User sid="..sid.." not connected anymore!")
-- push out old data for a while
fgcom.dbg("generateOutData(): evaluating user: "..sid.." with idty="..iid)

-- Skip, if already processed (Issue #177).
-- I really have no Idea, why table entries are sometimes iterated more than once.
local already_processed_clients_key = string.format("%s:%s", sid, iid)
if already_processed_clients[already_processed_clients_key] ~= nil then
fgcom.dbg("skipping entry '"..already_processed_clients_key.."'); already processed")

else
fgcom.dbg("processing entry '"..already_processed_clients_key.."'")
already_processed_clients[already_processed_clients_key] = true


-- prepare the return structure for generating the message
local userData = {
callsign="",
type="invalid",
radios = {},
lat="",
lon="",
alt="",
updated=0
}

-- populate users metadata
local mumbleUser = allUsers[sid]
local last_updated_since = os.time() - fgcom_clients[sid][iid].lastUpdate
if not mumbleUser then
fgcom.dbg("User sid="..sid.." not connected anymore!")
-- push out old data for a while;
-- fgcom.data.cleanupPluginData() (called from dbUpdateTimer) will clean up for us.
-- once that happened, we will not see the entry here anymore.
else
if userData.type == "client" then users_alive = users_alive + 1 end
end
userData.updated = fgcom_clients[sid][iid].lastUpdate
userData.type = fgcom_clients[sid][iid].type
else
fgcom_clients[sid][iid].type = "client"
if mumbleUser:getName():find("FGCOM%-.*") then fgcom_clients[sid][iid].type = "playback-bot" end
if mumbleUser:getName():find("FGCOM%-BOTPILOT.*") then fgcom_clients[sid][iid].type = "client" end
userData.type = fgcom_clients[sid][iid].type
fgcom.dbg(" updated="..userData.updated.. " ("..last_updated_since.."s ago)")

if userData.type == "client" then users_alive = users_alive + 1 end
end

userData.callsign = user.callsign
fgcom.dbg(" callsign="..userData.callsign.." (type="..userData.type..")")

userData.radios = {}
for radio_id,radio in pairs(user.radios) do
fgcom.dbg(" radio #"..radio_id..", ptt='"..radio.ptt.."', frq='"..radio.frequency.."', dialedFRQ='"..radio.dialedFRQ.."', operable="..radio.operable)
if radio.frequency ~= "<del>" then
table.insert(userData.radios, radio_id, radio)
-- populate users callsign
userData.type = fgcom_clients[sid][iid].type
userData.callsign = user.callsign
fgcom.dbg(" callsign="..userData.callsign.." (type="..userData.type..")")
-- populate users radios
for radio_id,radio in pairs(user.radios) do
fgcom.dbg(" radio #"..radio_id..", ptt='"..radio.ptt.."', frq='"..radio.frequency.."', dialedFRQ='"..radio.dialedFRQ.."', operable="..radio.operable)
if radio.frequency ~= "<del>" then
table.insert(userData.radios, radio_id, radio)
end
end

-- populate users location
userData.lat = user.lat
userData.lon = user.lon
userData.alt = user.alt
fgcom.dbg(" lat="..userData.lat.."; lon="..userData.lon.."; alt="..userData.alt)

-- finally push the prepared data into the client result
table.insert(data.clients, userData)
end
userData.lat = user.lat
userData.lon = user.lon
userData.alt = user.alt
userData.updated = fgcom_clients[sid][iid].lastUpdate
fgcom.dbg(" updated="..userData.updated)
fgcom.dbg(" lat="..userData.lat.."; lon="..userData.lon.."; alt="..userData.alt)

table.insert(data.clients, userData)
end
end

Expand Down Expand Up @@ -394,4 +424,44 @@ client:hook("OnMessage", function(client, event)
end)


-- Implement fgcom hooks
fgcom.hooks.parsePluginData_updateKnownClient = function(sid, iid)
-- called when parsePluginData() detected that the client was known.
fgcom.dbg("fgcom.hooks.parsePluginData_updateKnownClient("..sid..","..iid..") called")
end

fgcom.hooks.parsePluginData_newClient = function(sid, iid)
-- called when parsePluginData() detected that the client was not seen before.
fgcom.dbg("fgcom.hooks.parsePluginData_newClient("..sid..","..iid..") called")
end

--fgcom.hooks.parsePluginData_afterParseIID = function(sid, iid)
-- -- called when parsePluginData() received data for a given iid
-- fgcom.dbg("fgcom.hooks.parsePluginData_afterParseIID("..sid..","..iid..") called")
--end

fgcom.hooks.parsePluginData_processedPacket = function(mumble_user, packtype, dataID_t)
-- called after processing the packet, passing raw data
fgcom.dbg("fgcom.hooks.parsePluginData_processedPacket("..packtype..") called")
if packtype == "UPD_USR" then
local iid = dataID_t[3] -- identity selector
local sid = mumble_user:getSession() -- mumble session id

-- Update type of client based on client name;
-- We know, that bot clients use FGCOM- as prefix for their mumble username.
local newType = "client" -- assume client as default
if mumble_user:getName():find("FGCOM%-.*") then newType = "playback-bot" end
if mumble_user:getName():find("FGCOM%-BOTPILOT.*") then newType = "client" end

if fgcom_clients[sid][iid].type ~= newType then
fgcom.dbg(" update type to '"..newType.."'")
fgcom_clients[sid][iid].type = newType
end
end
end




-- Finally start the bot
mumble.loop()

0 comments on commit a03e0b7

Please sign in to comment.