view Admin.lua @ 105:c6c748a5823b tip

Collaborative list trimming 80% functional Updates for the zone-in problem
author John@Yosemite-PC
date Sun, 06 May 2012 08:33:34 -0400
parents d3ea0ab1428d
children
line wrap: on
line source
local bsk=bsk
local _G=_G
local table=table 
local pairs=pairs
local setmetatable=setmetatable
local ipairs=ipairs
local string=string
local sformat=string.format
local strsplit=strsplit
local tostring=tostring
local type=type
local unpack=unpack
local getn=getn
setfenv(1,bsk)

adminList = {}

local lastZoneTime = 0
local function ZoneChanged()
    lastZoneTime = _G.time()
end
local function GuildRosterUpdate()
    local guildInfoText = _G.GetGuildInfoText()
    if lastZoneTime+5 > _G.time() then return end -- silly workaround - GGIT() returns "" when you zone in
    local newAdminList = {}
    for line in guildInfoText:gmatch("[^\r\n]+") do
        local l,r = line:match("(.*):(.*)") -- could use wow strsplit had I known about it before writing this
        l = string.trim(l or "")
        r = string.trim(r or "")
        if l == "BSKADMIN" then -- found a juicy line. may contain multiple, comma or space delimited
            local admins = {strsplit(", ",r)}
            for _,a in pairs(admins) do
                a = string.trim(a or "")
                if a ~= "" then
                    newAdminList[a] = true
                end
            end
        end
    end

    if me == "Breuemama" then -- todo: strictly debugging ...
        newAdminList[me] = true
        newAdminList["Breue"] = true
    end

    if _G.next(adminList) ~= nil then -- had old admins. don't want to spam on initial load
        -- diff new vs old
        for a in pairs(adminList) do
            if not newAdminList[a] then
                print("Admin removed:", a)
            end
        end
        for a in pairs(newAdminList) do
            if not adminList[a] then
                print("Admin added:",a)
            end
        end
    end
    adminList = newAdminList

    if adminList[me] then -- I'm an admin!
        admin = true
        bsk.db.profile.admin = true
        RegisterAdminChannels()
    else
        admin = false
        bsk.db.profile.admin = false
        UnregisterAdminChannels()
    end
end

function RemoteAdminUpdateReceived(sender,remoteAdminStatusTable)
    if not admin then return end

    local rs,base,changes = unpack(remoteAdminStatusTable)
    for i,_ in pairs(adminList) do -- update each admin's entry in your own DB

        if i ~= me then
            -- grab the db copy and the incoming copy for that admin
            if not db.profile.adminStatus then db.profile.adminStatus = {} end
            local dbs = db.profile.adminStatus[i] or {base=0, changes={}}
            local ics = rs[i] or {base=0, changes={}}

            -- figure out which is better and keep that one
            -- winning criteria:
            --  * broadcast was actually from that person (ie best
            --  verification possible)
            --  * newer base
            --  * same base, more entries
            --  * todo: see if date last observed is a better option

            if i==sender then
                db.profile.adminStatus[i] = ics
            elseif ics.base > dbs.base or (ics.base==dbs.base and getn(ics.changes) > getn(dbs.changes)) then
                db.profile.adminStatus[i] = ics
            end
        end
    end

    if changes and getn(changes)>0 then
        IntegrateChangeDiff(base,changes)
    end

    -- trim if the replies said it's safe

    local trimPoint = db.profile.time
    -- come up with a tentative starting point (max base)
    for i,v in pairs(db.profile.adminStatus) do
        if v.base > trimPoint then trimPoint = v.base end
    end

    local pointer = {}
    -- fast forward pointers *past* the trimPoint in each list
    for p,l in pairs(db.profile.adminStatus) do
        pointer[p] = 1
        for i,v in ipairs(l.changes) do
            if v <= trimPoint then
                pointer[p] = i+1 -- "past" the trim point - might point to nil
            else
                break
            end
        end
    end
    print("pointers")
    PrintTable(pointer)

    for i,v in ipairs(db.profile.changes) do
        if v.time > trimPoint then -- advance to the trim point estimate before doing anything
            -- into uncharted territory, let's see how far we can push it
            local continue = true
            for p,t in pairs(pointer) do
                local dbpapct = db.profile.adminStatus[p].changes[t] 
                if dbpapct and dbpapct==v.time then
                    pointer[p] = pointer[p]+1
                else
                    continue=false
                    break
                end
                trimPoint = v.time
            end
            if not continue then break end
        end
    end
    print("pointers")
    PrintTable(pointer)
    if trimPoint > db.profile.time then

        -- protect trimming back to beginning of previous month so we're not
        -- trimming like madmen
        local ct = _G.date("*t",_G.time())
        ct.month = ct.month-1
        if ct.month < 1 then ct.month = 12 end
        ct.day = 1
        ct.hour = 1
        ct.minute = 0
        ct.sec = 0
        local ts = _G.time(ct)
        print("I think I can trim to",trimPoint,ts)
        TrimLists(ts)
    else
        print("no bother trimming",trimPoint)
    end
end

function InitializeAdmin()
    if not event then
        _G.error("BSK: Improper order of initialization")
    end
    me = _G.UnitName("player")

    if admin then -- if at last login I was an admin ...

        -- note that we're not transmitting anything here. we'll do that once we
        -- know for certain we're an admin

        -- cache the onload status in case it changes in memory later
        onloadAdminStatus = {}
        tcopy(onloadAdminStatus,db.profile.adminStatus)

        -- update our own entry - safe because comms shouldn't have happened yet
        if not onloadAdminStatus then onloadAdminStatus = {} end
        if not onloadAdminStatus[me] then onloadAdminStatus[me] = {} end
        onloadAdminStatus[me].base = db.profile.time or 0
        onloadAdminStatus[me].changes= {}
        for _,v in ipairs(db.profile.changes) do
            table.insert(onloadAdminStatus[me].changes,v.time) -- only timestamps
        end

    else -- otherwise store a blank slate
        onloadAdminStatus = {}
        db.profile.adminStatus = nil
    end

    event:RegisterEvent("GUILD_ROSTER_UPDATE",GuildRosterUpdate)
    event:RegisterEvent("ZONE_CHANGED_NEW_AREA",ZoneChanged)
    event:RegisterEvent("ZONE_CHANGED_INDOORS",ZoneChanged)
    event:RegisterEvent("ZONE_CHANGED",ZoneChanged)
    _G.GuildRoster() -- will eventually force the event to fire

    if me == "Breuemama" then -- debugging only
        GuildRosterUpdate()
    end
end