view Comm.lua @ 95:5df2c9cdb8c8

A few notes
author John@Yosemite-PC
date Sun, 22 Apr 2012 23:18:35 -0400
parents 2058d86778b4
children 082ff877c443
line wrap: on
line source
local bsk=bsk
local _G=_G
local table=table 
local pairs=pairs
local setmetatable=setmetatable
local ipairs=ipairs
local unpack=unpack
local string=string
local sformat=string.format
local tostring=tostring
local type=type
local getn=getn

local commlib = LibStub("AceComm-3.0") or _G.error("Couldn't load up AceComm")
local s = LibStub("AceSerializer-3.0") or _G.error("Couldn't load up AceSerializer")

setfenv(1,bsk)

local function BuildPacket(handler,message)
    local p = {handler,message}
    local str = s:Serialize({handler,message})
    print("sending",str)
    return p,str
end

local function SendMessage(str)
    --commlib:SendCommMessage("BSKADDON",str,"GUILD")
    commlib:SendCommMessage("BSKADDON"..commversion,str,"RAID")
end

local function Send(handler,message)
    local p,str = BuildPacket(handler,message)
    SendMessage(str)
end

-- todo: ActivateList and AddReserve -> state
Comm =
{
    ["SS"] = function(self,packet,sender,isloop)
        print("isloop",isloop)
        if not isloop then DispatchState(packet) end
    end,
    ["SendStateChange"] = function(self,...)
        local p,str = BuildPacket("SS",{...})
        DispatchState(p[2])
        SendMessage(str)
    end,
    ["AR"] = function(self,packet,sender,isloop)
        if isloop then return end
        PersonList:AddReserve(packet)
        changeListener:DataEvent()
    end,
    ["AddReserve"] = function(self,packet)
        if changeListener then
            changeListener:DataEvent(change)
        end
        Send("AR",packet)
    end,
    ["SendChange"] = function(self,change)
        if changeListener then
            changeListener:DataEvent(change)
        end
        Send("CC",change)
    end,
    ["CC"] = function(self,change,sender,isloop)
        if isloop then return end
        table.insert(db.profile.changes,change)
        ProcessChange(change)
        changeListener:DataEvent()
    end,
    ["Push"] = function(self)
        Send("PU",{db.profile.lists,db.profile.persons,db.profile.changes,db.profile.time})
    end,
    ["PU"] = function(self,packet,sender,isloop)
        if isloop then return end
        db.profile.lists,db.profile.persons,db.profile.changes,db.profile.time = unpack(packet)
        CreateWorkingStateFromChanges(db.profile.changes)
        if changeListener then
            changeListener:DataEvent()
        end
    end,

    ["TS"] = function(self,packet,sender,isloop)
        if isloop then return end
        if masterLooterIsMe and admin then
            -- only non-admins will send this message, send them the present
            -- working state

            local t = packet
            local remoteBase = table.remove(t,1)

            -- if their base is older than ours, this is easy - send them our
            -- base and all the changes
            -- if their base is equal to ours, then diff their changes vs ours
            -- and send them the remainder - it's ok if they have changes we
            -- don't
            -- if their base is newer, then we have a problem - it means another
            -- admin rebased and we aren't aware of that. but the other admin
            -- wouldn't have done that if they didn't believe all the admins
            -- were synced up to the release point. So do we trust that and use
            -- this as a rebase opportunity? print an error? just reply that we
            -- don't have anything new to share with them? only send them 
            --

            if remoteBase == db.profile.time then
                local j = 1 -- index in foreign list
                local n = getn(t)
                local o = {}
                for i,v in pairs(db.profile.changes) do -- for each timestamp in our list
                    if t and t[j] < v.time then
                        table.insert(o,v)
                    end
                    while j<n and t[j] <= v.time do j = j+1 end -- advance the foreign pointer past our current entry
                end -- j>=n ? add because the remote hit end of road. lt? add because it's a missing stamp
                print("Received request at timebase",remoteBase,"and returning:")
                PrintTable(o)
                if getn(o) > 0 then
                    Send("CU",o) -- todo: send privately to the requster
                end
            else
                print("Received request at differing timebase",remoteBase,db.profile.time," ... pushing")
                self:Push() -- todo: send privately to requester
            end
        end
    end,

    ["CU"] = function(self,packet,sender,isloop) -- blindly trust an admin loot master
        if isloop then return end

        local c = packet
        local old = db.profile.changes

        local new = {}

        local op = 1
        local cp = 1

        local no = getn(old)
        local nc = getn(c)

        if no == 0 then
            db.profile.changes = c
        else
            while op <= no or cp <= nc do -- lists are pre-sorted. insertion merge them
                if cp > nc then -- inelegant
                    table.insert(new,old[op])
                    op = op + 1
                elseif op > no then
                    table.insert(new,c[cp])
                    cp = cp + 1
                elseif c[cp].time < old[op].time then
                    table.insert(new,c[cp])
                    cp = cp + 1
                elseif c[cp].time > old[op].time then
                    table.insert(new,old[op])
                    op = op + 1
                else
                    error("Bad update received from ",sender)
                end
            end
            print("Updating changes - ",getn(new), "entries")
            db.profile.changes = new
        end

        CreateWorkingStateFromChanges(db.profile.changes)
        if changeListener then
            changeListener:DataEvent()
        end
    end,

    ["RequestCatchup"] = function(self)
        if not admin then
            --local string = _g.tostring(timestamp)
            --for i,v in pairs(db.profile.changes) do -- append all change timestamps
            --    string = string .. "|" .. _g.tostring(v.time)
            --end
            local t = {db.profile.time}
            for i,v in pairs(db.profile.changes) do -- append all change timestamps
                table.insert(t,v.time)
            end
            Send("TS", t) -- todo: send privately to loot master
        else
            -- todo: admins talking to one another
        end

    end,
}

local function OnCommReceived(prefix, message, distribution, sender)
    print("Received on", distribution)
    local success,packet = s:Deserialize(message)
    local isloop = _G.UnitName("player") == sender

    print("received",message)

    Comm[packet[1]](Comm,packet[2],sender,isloop)
end

alertlist = {}
local function OnOlderCommReceived(prefix, message, distribution, sender)
    if not alertlist[sender] then
        printf("Received communication from %s, who is using an older version of the addon; ignoring",sender)
        alertlist[sender]=true
    end
end

local function OnNewerCommReceived(prefix, message, distribution, sender)
    if not alertlist[sender] then
        printf("Received communication from %s, who is using a newer version of the addon; ignoring",sender)
        alertlist[sender]=true
    end
end

function InitializeComm()
    for i = 0,commversion-1 do
        commlib:RegisterComm("BSKADDON"..i,OnOlderCommReceived)
    end
    commlib:RegisterComm("BSKADDON"..commversion,OnCommReceived)
    for i = commversion+1,commversion+5 do -- some sensible number
        commlib:RegisterComm("BSKADDON"..i,OnNewerCommReceived)
    end
end

function DeinitializeComm()

end