changeset 0:47fac96968e1

First commit
author John@Yosemite-PC
date Fri, 02 Mar 2012 00:15:09 -0500
parents
children 21c58930f74e
files .pkgmeta Core.lua Lists.lua Localization/Core.lua Localization/enUS.lua breuesk.toc
diffstat 5 files changed, 1002 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.pkgmeta	Fri Mar 02 00:15:09 2012 -0500
@@ -0,0 +1,15 @@
+package-as: BreueSK
+enable-nolib-creation: no
+
+externals:
+    libs/LibStub: svn://svn.wowace.com/wow/libstub/mainline/trunk
+    libs/AceAddon-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceAddon-3.0
+    libs/AceConsole-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConsole-3.0
+    libs/AceConfig-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceConfig-3.0
+    libs/AceComm-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceComm-3.0
+    libs/AceDB-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceDB-3.0
+    libs/AceDBOptions-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceDBOptions-3.0
+    libs/AceEvent-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceEvent-3.0
+    libs/AceHook-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceHook-3.0
+    libs/AceLocale-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceLocale-3.0
+    libs/AceSerializer-3.0: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceSerializer-3.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core.lua	Fri Mar 02 00:15:09 2012 -0500
@@ -0,0 +1,548 @@
+-- ideas: last attended data and/or remove people who haven't attended in X days
+-- and/or just a "remove from all lists" option
+
+
+-- order of implementation
+-- ( ) lists fully functional (/script interface)
+-- ( ) lists single-user functional via command line interface 
+-- ( ) single user + admin gui (manual suicides)
+-- ( ) single user + admin gui (master loot)
+-- ( ) communication and list trimming
+-- ( ) admins
+-- ( ) players gui
+-- ( ) crypto / protection against tampering
+-- ( ) alt tracking
+-- ( ) reserves
+
+-- important things to remember:
+-- 1) ipairs iterates from 1 until the first missing int index -> no gaps
+-- 2) a.x === a["x"]
+-- 3) a["1"] =/= a[1]
+
+bsk = LibStub("AceAddon-3.0"):NewAddon("bsk","AceConsole-3.0", "AceHook-3.0", "AceComm-3.0", "AceSerializer-3.0")
+local L = LibStub("AceLocale-3.0"):GetLocale("bsk", false)
+
+local AceGUI = LibStub("AceGUI-3.0")
+
+function bsk:OnInitialize()
+
+    self.db = LibStub("AceDB-3.0"):New("BskDB", self.defaults, "Default")
+
+    self.options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
+    LibStub("AceConfig-3.0"):RegisterOptionsTable("bsk", self.options)
+    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("bsk", "bsk")
+
+    self:RegisterChatCommand("bsk", "HandleCommand")
+    bsk:CreateWorkingStateFromChanges()
+end
+
+function bsk:OnEnable()
+    --self:HandleCommand()
+end
+
+function bsk:HandleCommand(paramIn)
+    local param = { strsplit(" ", paramIn) }
+    local FixPlayerName = function(p)
+        p = p:lower()
+        -- next two lines from sylvanaar
+        local MULTIBYTE_FIRST_CHAR = "^([\192-\255]?%a?[\128-\191]*)"
+        return string.gsub(p, MULTIBYTE_FIRST_CHAR, string.upper, 1)  
+    end
+
+    if param[1] == nil or param[1] == "" then
+        bsk:Print("need args")
+        return
+    end
+    if param[1] == "players" then
+        bsk:PrintPlayers()
+    elseif param[1] == "add" then
+        if param[2] == nil or param[2] == "" then
+            bsk:PrintTable(param)
+            return
+        end
+        if param[3] == nil or param[3] == "" then
+            bsk:PrintTable(param)
+            return
+        end
+        if param[2] == "player" then
+            --if param[3] == "all" then
+            --    bsk:Pop
+            --else
+                local player = FixPlayerName(param[3])
+                bsk:AddPlayer(player)
+            --end
+        elseif param[2] == "list" then
+            bsk:CreateList(param[3])
+        end
+    elseif param[1] == "suicide" then
+        if param[2] == nil or param[2] == "" or param[3] == nil or param[3] == "" then
+            bsk:PrintTable(param)
+            return
+        end
+        local player = FixPlayerName(param[2])
+        bsk:Print(string.format("Fixed player name %s to %s",param[2],player))
+        bsk:SuicidePlayer(player,param[3])
+    elseif param[1] == "show" then
+        if param[2] == nil or param[2] == "" then
+            bsk:PrintTable(param)
+            return
+        end
+        bsk:PrintLists(param[2])
+    end
+
+    -- TODO: add options
+    --
+    -- for now: launch GUI
+    --
+
+    --if self.frame == nil then
+        --self:CreateGUI()
+        --self:ShowGUI()
+    --else
+        --self:ShowGUI()
+    --end
+
+end
+--MULTIBYTE_FIRST_CHAR = "^([\192-\255]?%a?[\128-\191]*)"
+--function GetNamePattern(name)
+--    local u = name:match(MULTIBYTE_FIRST_CHAR):upper()
+ 
+--    if not u or u:len() == 0 then Prat.Print("GetNamePattern: name error", name) return end
+--    local l = u:lower()
+--    local namepat 
+--    if u == l then
+--        namepat = name:lower()
+--    elseif u:len() == 1 then
+--        namepat = "["..u..l.."]"..name:sub(2):lower()
+--    elseif u:len() > 1 then 
+--        namepat = ""
+--        for i=1,u:len() do
+--            namepat = namepat .. "[" .. u:sub(i,i)..l:sub(i,i).."]"
+--        end
+--        namepat = namepat .. name:sub(u:len()+1)
+--    end
+--    return "%f[%a\192-\255]"..namepat.."%f[^%a\128-\255]"
+--end
+--AnyNamePattern = "%f[%a\192-\255]([%a\192-\255]+)%f[^%a\128-\255]"  
+
+
+function bsk:CreateGUI()
+
+    -- Create a container frame
+    self.frame = AceGUI:Create("Frame")
+    self.frame:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
+    self.frame:SetTitle("BSK")
+    self.frame:SetLayout("Flow")
+    self.frame:SetHeight(700) 
+    self.frame:SetWidth(350)
+    local tab = AceGUI:Create("TabGroup")
+    tab:SetLayout("Flow")
+    tab:SetTabs(
+        {
+            {
+                text="Tab 1",
+                value="tab1"
+            },
+
+            {
+                text="Tab 2",
+                value="tab2"
+            },
+            {
+                text="Tab 3",
+                value="tab3"
+            },
+            {
+                text="Tab 4",
+                value="tab4"
+            }
+        }
+    )
+	tab.width = "fill"
+	tab.height = "fill"
+
+    tab:SetCallback("OnGroupSelected",ChangeTab)
+    tab:SelectTab("tab1")
+    self.frame:AddChild(tab)
+
+    -- Create a button
+    --local btn = AceGUI:Create("Button")
+    --btn:SetWidth(170)
+    --btn:SetText("Button !")
+    --btn:SetCallback("OnClick", function() print("Click!") end)
+    -- Add the button to the container
+    --self.frame:AddChild(btn)
+end
+
+bsk.defaults = {
+    profile = {
+        players = {},
+        changes = {},
+        listBase = {}
+    }
+}
+
+bsk.options = {
+    name= 'bsk',
+    type = 'group',
+    args = 
+    {
+        version =
+        {
+            type = "execute",
+            name = "Version query",
+            desc = "Check others' versions",
+            func = function(self) self:Print("TODO") end
+        }
+    }
+}
+function ChangeTab(container, event, group)
+    container:ReleaseChildren()
+    if group == "tab2" then
+        local desc = AceGUI:Create("Label")
+        desc:SetText("This is Tab 1")
+        desc:SetFullWidth(true)
+        container:AddChild(desc)
+
+        local button = AceGUI:Create("Button")
+        button:SetText("Tab 1 Button")
+        button:SetWidth(200)
+        container:AddChild(button)
+    elseif group == "tab1" then
+        local item2 = {string="item2!", color = {r=1,g=0,b=0.5} }
+        local itemList = {"Item1", item2, "Item3", "Item4"}
+
+        local myMultiSelect = AceGUI:Create("MultiSelect")
+        myMultiSelect:SetLabel("My Multi Select")
+        myMultiSelect:SetWidth(200)
+        myMultiSelect:SetHeight(400)
+        myMultiSelect:SetItemList(itemList)
+        myMultiSelect:SetMultiSelect(false)
+        container:AddChild(myMultiSelect)
+    end
+end
+
+
+
+
+
+
+
+
+
+
+
+--[[----------------------------------
+--  MultiSelect widget for AceGUI-3.0
+--  Written by Shirokuma
+--]]----------------------------------
+
+
+--[[-----------------
+-- AceGUI
+--]]-----------------
+--local AceGUI = LibStub("AceGUI-3.0")
+
+--[[-----------------
+-- Lua APIs
+--]]-----------------
+local format, pairs, tostring = string.format, pairs, tostring
+
+--[[-----------------
+-- WoW APIs
+--]]-----------------
+local CreateFrame, UIParent = CreateFrame, UIParent
+
+--[[-----------------
+-- Frame Elements
+--]]-----------------
+local FrameBackdrop = {
+	bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
+	edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+	tile = true, tileSize = 16, edgeSize = 16,
+	insets = { left = 3, right = 3, top = 3, bottom = 3 }
+}
+
+
+--[[-----------------
+-- Widget Info
+--]]-----------------
+local widgetType = "MultiSelect"
+local widgetVersion = 1
+
+
+--[[-----------------
+-- Event Code
+--]]-----------------
+local function Label_OnEnter(label)
+	local self = label.obj
+	local value = label
+	self:Fire("OnLabelEnter", value)
+end
+
+local function Label_OnLeave(label)
+	local self = label.obj
+	local value = label
+	self:Fire("OnLabelEnter", value)
+end
+
+local function Label_OnClick(label)
+	local self = label.obj
+	local value = label
+	self:Fire("OnLabelClick", value)
+	AceGUI:ClearFocus()
+end
+
+
+--[[-----------------
+-- MultiSelect Code
+--]]-----------------
+do
+	local function OnAcquire(self)  -- set up the default size
+		self:SetWidth(200)
+		self:SetHeight(400)
+	end
+	
+	local function SetWidth(self, w)  -- override the SetWidth function to include the labelframe
+		self.frame:SetWidth(w)
+		self.labelframe:SetWidth(w-33)
+	end	
+	
+	local function SetLabel(self, text)  -- sets the multiselect label text
+		self.label:SetText(text)
+	end
+	
+	local function SetMultiSelect(self, value)  -- set if multiple values can be selected simultaneously
+		self.multiselect = value
+	end
+	
+	local function AddItem(self, str, color)  -- add an item (create a new item label object)
+	    local color = color
+		local label = CreateFrame("Button", nil, self.labelframe)
+		label.selected = false
+		label.obj = self
+		label:SetHeight(18)
+		label:SetPoint("TOPLEFT", self.labelframe, "TOPLEFT", 0, -(getn(self.labels) * 18))
+		label:SetPoint("TOPRIGHT", self.labelframe, "TOPRIGHT", 0, -(getn(self.labels) * 18))
+		self.labels[getn(self.labels) + 1] = label
+		self.labelframe:SetHeight(getn(self.labels) * 18)
+		
+		local text = label:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+		text:SetJustifyH("LEFT")
+		text:SetPoint("TOPLEFT",label,"TOPLEFT",5,0)
+		text:SetPoint("BOTTOMRIGHT",label,"BOTTOMRIGHT",-5,0)
+		if color ~= nil then
+		    text:SetTextColor(color.r,color.g,color.b)
+		end
+		text:SetText(str)
+		label.text = text
+		
+		local highlight = label:CreateTexture(nil, "OVERLAY")
+		highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+		highlight:SetBlendMode("ADD")
+		highlight:SetHeight(14)
+		highlight:ClearAllPoints()
+		highlight:SetPoint("RIGHT",label,"RIGHT",0,0)
+		highlight:SetPoint("LEFT",label,"LEFT",0,0)
+		highlight:Hide()
+		label.highlight = highlight
+		
+		label:SetScript("OnEnter", function(this)
+			this.highlight:Show()
+			Label_OnEnter(this)
+		end)
+		label:SetScript("OnLeave", function(this)
+			if not this.selected then
+				this.highlight:Hide()
+			end
+		end)
+		label:SetScript("OnClick", function(this)
+			if not this.selected then
+				this.selected = true
+				if not self.multiselect then
+					for index, items in pairs(self.labels) do
+						if self.labels[index] ~= this and self.labels[index].selected then
+							self.labels[index].selected = false
+							self.labels[index].highlight:Hide()
+						end
+					end
+				end
+			else
+				this.selected = false
+			end
+			Label_OnClick(this)
+		end)
+	end
+	
+	local function GetItem(self, text)  -- find an object based on the text parameter
+		for _, value in pairs(self.labels) do
+			if value.text:GetText() == text then
+				return value
+			end
+		end
+		return nil
+	end
+	
+	local function GetText(self, value)  -- get the text of a label object
+		for _,item in pairs(self.labels) do
+			if value == item then
+				return item.text:GetText()
+			end
+		end
+		return nil
+	end
+	
+	local function SetText(self, value, text)  -- set the text of a label object
+		for _, item in pairs(self.labels) do
+			if value == item then
+				value.text:SetText(text)
+			end
+		end
+	end
+	
+	local function IsSelected(self, value)  -- return if the label object is currently selected
+		for _, item in pairs(self.labels) do
+			if value == item then
+				return item.selected
+			end
+		end
+		return nil
+	end
+	
+	local function GetSelected(self)  -- return a table of the currently selected label objects
+		local selectedList = {}
+		for _, item in pairs(self.labels) do
+			if item.selected then
+				table.insert(selectedList, item)
+			end
+		end
+		return selectedList
+	end
+		
+	local function SetItemList(self, list)  -- create new labels from a list of strings
+		for _,item in pairs(self.labels) do
+			item:Hide()
+			item:ClearAllPoints()
+		end
+		
+		self.labels = {}
+		
+		if list then
+			for _,item in pairs(list) do
+			    if type(item) == "string" then
+				    self:AddItem(item)
+                elseif type(item) == "table" then
+                    if item.string ~= nil and type(item.string) == "string" then
+                        if item.color ~= nil then
+                            if type(item.color) == "table" and item.color.r ~= nil and item.color.g ~= nil and item.color.b ~= nil then
+                                self:AddItem(item.string, item.color)
+                            else
+                                assert(false and "setitemlist: item.color is set, but nonsense")
+                            end
+                        else
+                            self:AddItem(item.string)
+                        end
+                    else
+                        assert( false and "setitemlist: item is table without .string member")
+                    end
+                else
+                    assert(false and "SetItemList: nonsense list entry")
+                end
+			end
+		end
+	end
+
+	local function RemoveItem(self, item)  -- delete an item
+		local function RedrawFrame()
+			for index,value in pairs(self.labels) do
+				value:SetPoint("TOPLEFT", self.labelframe, "TOPLEFT", 0, (-(index-1) * 18))
+				value:SetPoint("TOPRIGHT", self.labelframe, "TOPRIGHT", 0,(-(index-1) * 18))
+			end
+		end
+		
+		for index, value in pairs(self.labels) do
+			if value == item then
+				table.remove(self.labels, index)
+				item:Hide()
+				item:ClearAllPoints()
+				RedrawFrame()
+			end
+		end
+	end
+	
+	local function SetSelected(self, item, value)
+		if value then
+			if not self.multiselect then  -- test
+				for _, value in pairs(self.labels) do
+					value.selected = false
+					value.highlight:Hide()
+				end
+			end
+			item.selected = true
+			item.highlight:Show()
+		else
+			item.selected = false
+			item.highlight:Hide()
+		end
+	end
+	
+	local function Constructor()  -- widget constructor
+		local frame = CreateFrame("Frame", nil, UIParent)
+		local backdrop = CreateFrame("Frame", nil, frame)
+		local self = {}
+		local labels = {}
+		
+		self.type = widgetType
+		self.frame = frame
+		self.backdrop = backdrop
+		self.labels = {}
+		self.multiselect = true
+		frame.obj = self
+		
+		local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
+		label:SetJustifyH("LEFT")
+		label:SetPoint("TOPLEFT", 5, 0)
+		label:SetPoint("TOPRIGHT", -5, 0)
+		label:SetHeight(14)
+		label:SetText("MultiSelect")
+		self.label = label
+		
+		backdrop:SetBackdrop(FrameBackdrop)
+		backdrop:SetBackdropColor(0, 0, 0)
+		backdrop:SetBackdropBorderColor(0.4, 0.4, 0.4)
+		backdrop:SetPoint("TOPLEFT", frame, "TOPLEFT", 5, -14)
+		backdrop:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -5, 0)
+		
+		local scrollframe = CreateFrame("ScrollFrame", format("%s@%s@%s", widgetType, "ScrollFrame", tostring(self)), frame, "UIPanelScrollFrameTemplate")
+		scrollframe:SetPoint("TOPLEFT", backdrop, "TOPLEFT", 5, -6)
+		scrollframe:SetPoint("BOTTOMRIGHT", backdrop, "BOTTOMRIGHT", -28, 6)
+		scrollframe.obj = self
+		self.scrollframe = scrollframe
+		
+		local labelframe = CreateFrame("Frame", nil, scrollframe)
+		labelframe:SetAllPoints()
+		labelframe.obj = self
+		scrollframe:SetScrollChild(labelframe)
+		self.labelframe = labelframe
+
+		-- method listing
+		self.OnAcquire = OnAcquire
+		self.SetLabel = SetLabel
+		self.AddItem = AddItem
+		self.SetWidth  = SetWidth
+		self.SetMultiSelect = SetMultiSelect
+		self.SetItemList = SetItemList
+		self.GetItem = GetItem
+		self.RemoveItem = RemoveItem
+		self.GetText = GetText
+		self.SetText = SetText
+		self.IsSelected = IsSelected
+		self.GetSelected = GetSelected
+		self.SetSelected = SetSelected
+		
+		AceGUI:RegisterAsWidget(self)
+		return self
+	end
+	AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lists.lua	Fri Mar 02 00:15:09 2012 -0500
@@ -0,0 +1,408 @@
+-- lists consist of three things
+-- 1) a base state - agreed on by one or more list holders
+-- 2) change sets - incremental list changes (can be rolled forwards or
+-- backwards)
+-- 3) working state - not saved because it can be so easily calculated
+--
+-- A separate user list is held - lists index into this
+
+
+-- TODO: rename player
+
+
+
+bsk.lists = {}
+bsk.players = {}
+
+local RaidList = {}
+local ReserveList = {}
+local activeList = 0 -- temporary
+
+local tinsert = table.insert
+local sformat = string.format
+local getn = table.getn
+
+function bsk:tcopy(to, from)
+  for k,v in pairs(from) do
+    if(type(v)=="table") then
+      to[k] = {}
+      bsk:tcopy(to[k], v);
+    else
+      to[k] = v;
+    end
+  end
+end
+local shallowCopy = function(t)
+  local u = { }
+  for k, v in pairs(t) do u[k] = v end
+  return setmetatable(u, getmetatable(t))
+end
+
+function bsk:PrintTable(table, depth)
+    depth = depth or ""
+    if not table then return end
+    for i,v in pairs(table) do 
+        if( type(v) == "string" ) then
+            self:Print(depth .. i ..  " - " .. v) 
+        elseif( type(v) == "number" ) then
+            self:Print(depth .. i .. " - " .. tostring(v))
+        elseif( type(v) == "table" ) then
+            self:Print(depth .. i .." - ") 
+            self:PrintTable(v,depth.."   ")
+        elseif( type(v) == "boolean" ) then
+            self:Print(depth .. i .. " - " .. tostring(v))
+        else
+            self:Print(depth .. i .. " - not sure how to print type: " .. type(v) )
+        end
+    end
+end
+
+-- Debugging {{{
+function bsk:PrintLists()
+    bsk:PrintTable(bsk.lists)
+end
+function bsk:PrintChanges()
+    bsk:PrintTable(bsk.db.profile.changes)
+end
+function bsk:PrintPlayers()
+    bsk:PrintTable(bsk.players)
+end
+--}}}
+
+function bsk:CreateWorkingStateFromChanges()
+    local playerBase = self.db.profile.players
+    local listBase = self.db.profile.listBase
+    local changes = self.db.profile.changes
+
+    -- copy the base to the working state
+    wipe(bsk.lists)
+    wipe(bsk.players)
+    bsk:tcopy(bsk.lists,listBase)
+    bsk:tcopy(bsk.players,playerBase)
+
+    -- now just go through the changes list applying each
+    for i,v in pairs(changes) do
+        bsk:ProcessChange(v)
+    end
+end
+
+function bsk:CreateChange(change)
+    -- sanity
+    assert(change)
+    assert(change.action)
+    assert(change.arg)
+
+    bsk:StartChange(change)
+    bsk:CommitChange(change)
+end
+
+function bsk:StartChange(change)
+    local changes = self.db.profile.changes
+    change.time = time()
+    local n = getn(changes)
+    if n > 0 then
+        if changes[n].time >= change.time then
+            change.time = changes[n].time + 1
+        end
+    end
+end
+
+function bsk:CommitChange(change)
+    local changes = self.db.profile.changes
+    tinsert(changes,change)
+    -- TODO: broadcast change
+end
+
+
+-- timestamp logic:
+-- use time() for comparisons - local clients use date() to make it pretty. only
+-- dowisde - we can't have a server timestamp. Which kind of sucks, but it turns
+-- out you can change timezones when you enter an instance server, so you really
+-- never know what time it is.
+-- There's unfortunately no hard-and-proven method for determining the true time
+-- difference between local time and server time. You can't just query the two
+-- and compare them because your server timezone can change (!) if you go into
+-- an instance server with a different timezone. This is apparently a big
+-- problem on Oceanic realms.
+--
+--  Timestamp handling (brainstorming how to deal with drift):
+--  (not an issue) if someone sends you time in the future, update your offset so you won't
+--  send out events in the "past" to that person
+--  (not an issue - using local UTC now) on change-zone-event: check if you've changed timezones - might need update
+--  each time you add a change, check the tail of the change list; if this is
+--  less than that, you have a problem. Print a message. if this is equal, then
+--  that's ok, just bump it by 1 second. This could happen in the case of, say,
+--  spam-clicking the undo button or adding names to the list. The recipients
+--  should be ok with this since they'll follow the same algorithm. The only
+--  real chance for a problem is if two people click within the 1 second window?
+--  if someone sends you a past event,
+--          it's ok if it's newer than anything in the changes list
+--          otherwise ... causality has been violated.
+--  Whenever an admin signon event happens, have the admins each perform a
+--  timestamp check. Issue warnings for anyone with a clock that's more than
+--  X seconds out of sync with the others. Seriously, why isn't NTP a standard
+--  setting on all operating systems ...
+
+function bsk:ProcessChange(change)
+    if change.action == "AddPlayer" then
+        bsk:DoAddPlayer(change)
+    elseif change.action == "CreateList" then
+        bsk:DoCreateList(change)
+    elseif change.action == "AddPlayerToList" then
+        bsk:DoAddPlayerToList(change)
+    elseif change.action == "SuicidePlayer" then
+        bsk:DoSuicidePlayer(change)
+    else
+        bsk:Print("Unknown message encountered")
+        bsk:PrintTable(change)
+        assert(false)
+    end 
+end
+
+
+--
+-- The actual actions for changes start here
+--
+-- Each action occurs as a pair of functions. The bsk:Action() function is from
+-- a list admin's point of view. Each will check for admin status, then create a
+-- change bundle, call the handler for that change (ie the DoAction func), and
+-- then record/transmist the bundle. These are simple and repetitive functions.
+--
+-- The bsk:DoAction() function is tasked with executing the bundle and is what
+-- non-admins and admins alike will call to transform their working state via a
+-- change packet. Each Do() function will accept *only* a change packet, and
+-- it's assumed that the change has been vetted elsewhere. These are very blunt
+-- routines.
+--
+-- Note that "undo" has no special voodoo to it. It's basically a change that
+-- reverses the prior change on the stack.
+
+-- Players list
+function bsk:DoAddPlayer(change)
+    assert(change)
+    assert(change.arg.guid)
+    local arg = change.arg
+    -- require admin
+    local players = bsk.players
+    local name = arg.name
+    local guid = arg.guid
+    assert(players[guid]==nil)
+    players[guid] = name
+    players.time=change.time
+    return true
+end
+
+function bsk:AddPlayer(name)
+    local players = bsk.players
+    local guid = UnitGUID(name)
+    -- TODO: check guid to be sure it's a player
+    if not guid then
+        self:Print(sformat("Could not add player %s - they must be in range or group",name))
+        return
+    end
+    if players[guid] and players[guid] ~= name then
+        self:Print(sformat("Namechange detected for %s - new is %s, please rename the existing entry", players[guid], name))
+        return
+    end
+    if players[guid] ~= nil then
+        self:Print(sformat("%s is already in the players list; disregarding", name))
+        return
+    end
+    local change = {action="AddPlayer",arg={name=name,guid=guid}}
+    if bsk:DoAddPlayer(change) then
+        bsk:CreateChange(change)
+    end
+end
+
+function bsk:CreateFakeLists()
+    -- testing only
+end
+
+function bsk:DoCreateList(change)
+    -- TODO: this segment will probably be useful as bsk:SearchForListByName
+    local lists = bsk.lists
+    for i,v in pairs(lists) do
+        if v.name == change.arg.name then
+            self:Print(sformat("List %s already exists",v.name))
+            return false
+        end
+    end
+    tinsert(lists,{name=change.arg.name,time=change.time})
+    return true
+end
+
+function bsk:CreateList(name)
+    -- require admin
+    local change={action="CreateList",arg={name=name}}
+    bsk:StartChange(change)
+    self:Print("Creating ... " .. name)
+    if bsk:DoCreateList(change) then
+        bsk:CommitChange(change)
+    end
+end
+
+function bsk:DoAddPlayerToList(change)
+    local listIndex = change.arg.listIndex
+    local slist = change.arg.slist
+    local list = bsk.lists[listIndex]
+
+    if #slist == 1 then -- end of list insertion - just one person
+        tinsert(list,slist[1])
+        list.time = change.time
+    else
+        self:Print("Adding to middle of list is not yet supported")
+        return false
+    end
+    return true
+end
+
+function bsk:AddPlayerToList(name,list)
+    -- require admin
+    local listIndex = bsk:GetListIndex(list)
+    local slist = {name} -- TODO: support adding to elsewhere besides the end
+    local change = {action="AddPlayerToList",arg={name=name,listIndex=listIndex,slist=slist}}
+    bsk:StartChange(change)
+    if bsk:DoAddPlayerToList(change) then
+        bsk:CommitChange(change)
+    end
+end
+
+function bsk:DoRemovePlayer(change)
+
+    -- return true
+end
+
+function bsk:RemovePlayer(name)
+    -- from both players and lists
+end
+
+function bsk:GetSuicideList(name,list)
+    --self:Print("Calculating changeset for "..name.." from list -")
+    --self:PrintTable(list)
+    local t = {}
+    local ret = {}
+    local pushing = false
+    for i = 1, #list do
+        if list[i] == name then
+            pushing = true
+        end
+        if pushing and (RaidList[list[i]] or ReserveList[list[i]]) then
+            tinsert(ret,list[i])
+        end
+    end
+    return ret
+end
+
+function bsk:GetActiveList()
+    return bsk.lists[1] -- todo!
+end
+
+function bsk:DoSuicidePlayer(change)
+    local listIndex = change.arg.listIndex
+    local list = bsk.lists[listIndex]
+    local slist = shallowCopy(change.arg.list)
+    -- the goal here is to rotate the suicide list by 1
+    -- then we can just mash it on top of the intersection between the original
+    -- list and the working copy
+    local stemp = shallowCopy(change.arg.list)
+    local temp = table.remove(stemp,1) -- pop
+    tinsert(stemp,temp) -- push_back
+    --bsk:Print(sformat("Before suicide of %s on list %s",slist[1],list.name))
+    --bsk:PrintTable(list)
+    for i = 1, #list do
+        if list[i] == slist[1] then
+            table.remove(slist,1)
+            list[i] = stemp[1]
+            table.remove(stemp,1)
+        end
+    end
+    list.time=change.time
+    --bsk:Print("After")
+    --bsk:PrintTable(list)
+    return true
+end
+
+function bsk:SuicidePlayer(name,list)
+    -- require admin
+    local l=bsk:GetActiveList()
+    bsk:PopulateRaidList()
+    local slist=bsk:GetSuicideList(name,l)
+    local listIndex = bsk:GetListIndex(list)
+    local change = {action="SuicidePlayer",arg={names=names,list=slist,listIndex=listIndex}}
+    bsk:StartChange(change)
+    if bsk:DoSuicidePlayer(change) then
+       bsk:CommitChange(change)
+    end
+end
+
+-- The following code is from Xinhuan (wowace forum member)
+-- Pre-create the unitID strings we will use
+local pID = {}
+local rID = {}
+for i = 1, 4 do
+    pID[i] = format("party%d", i)
+end
+for i = 1, 40 do
+    rID[i] = format("raid%d", i)
+end
+function bsk:PopulateRaidList()
+    local inParty = GetNumPartyMembers()
+    local inRaid = GetNumRaidMembers()
+
+    wipe(RaidList)
+    if inRaid > 0 then
+        for i = 1, inRaid do
+            RaidList[UnitName(rID[i])]=true
+        end
+    elseif inParty > 0 then
+        for i = 1, inParty do
+            RaidList[UnitName(pID[i])]=true
+        end
+        -- Now add yourself as the last party member
+        RaidList[UnitName("player")]=true
+    else
+        -- You're alone
+        RaidList[UnitName("player")]=true
+    end
+end
+
+-- undo rules!
+-- only the most recent event can be undone
+-- ^^^ on a given list?
+-- algorithm is easy, given "Suicide A B C"
+-- just find A,B,C in the list and replace in order from the s message
+-- while undo is allowed *per-list*, certain events in the stream will
+-- prevent proper undo, such as add/delete player or add/delete list
+
+
+
+
+-- reserves
+function bsk:AddReserve(name)
+    ReserveList[name]=true
+    -- TODO: communicate to others. don't store this in any way.
+end
+
+function bsk:RemoveReserve(name)
+    ReserveList[name]=false
+    -- TODO: communicate to others. don't store this in any way.
+end
+
+
+
+
+
+
+
+
+
+-- Support functions
+
+function bsk:GetListIndex(name)
+    for i,v in pairs(bsk.lists) do
+        if v.name == name then
+            return i
+        end
+    end
+    assert(false)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Localization/enUS.lua	Fri Mar 02 00:15:09 2012 -0500
@@ -0,0 +1,6 @@
+local AceLocale = LibStub:GetLibrary("AceLocale-3.0")
+local L = AceLocale:NewLocale("bsk","enUS",true)
+if not L then return end
+
+-- English
+L["test"] = true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/breuesk.toc	Fri Mar 02 00:15:09 2012 -0500
@@ -0,0 +1,25 @@
+## Interface: 40300
+
+## Title: Breue SK
+## Name: Breue SK
+## Notes: Suicide Kings Addon
+## Version: 4.3
+
+## SavedVariables: BskDB
+
+Libs\LibStub\LibStub.lua
+Libs\AceAddon-3.0\AceAddon-3.0.xml
+Libs\AceConsole-3.0\AceConsole-3.0.xml
+Libs\AceConfig-3.0\AceConfig-3.0.xml
+Libs\AceComm-3.0\AceComm-3.0.xml
+Libs\AceDB-3.0\AceDB-3.0.xml
+Libs\AceDBOptions-3.0\AceDBOptions-3.0.xml
+Libs\AceEvent-3.0\AceEvent-3.0.xml
+Libs\AceHook-3.0\AceHook-3.0.xml
+Libs\AceLocale-3.0\AceLocale-3.0.xml
+Libs\AceSerializer-3.0\AceSerializer-3.0.xml
+
+Localization\enUS.lua
+Core.lua
+Lists.lua
+