Quantcast Mass confusion, loading process - WoWInterface
Thread Tools Display Modes
02-09-12, 10:15 PM   #1
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
Mass confusion, loading process

OKay so I have always had some funny intermitent problems with my saved variables and it has always had to do with the way it clears default keys out... but i may have just noticed part of the problem... umm when is it that PLAYER_LOGIN fires? because the following code should not even work but yet its the only way i can get it to work properly... Notice there is a register for ADDON_LOADED in a PLAYER_LOGIN event so technically that addon loaded part should never run right? but it does!

Code:
addon.RegisterEvent("MoveFrames-Initialize", 'PLAYER_LOGIN', function(self, event)
	addon.UnregisterEvent(self, event)
	if addon.settings.movedFrames then
		movedFrames = addon.settings.movedFrames
	end
	if type(movedFrames) ~= 'table' then
		movedFrames = { }
		addon.settings.movedFrames = movedFrames
	end

	local function HookFrames(self, event)
		addon:DefaultMoveableFrames()
		VerCheckUpdate()
		
		moveableframes = CopyTable(GMoveableFrames)
		for name, parent in pairs(moveableframes) do
			if HookFrame(name, parent) then
				moveableframes[name] = nil
			end
		end
		if next(moveableframes) then return end
		addon.UnregisterEvent(self, event)
	end
	addon.RegisterEvent("MoveFrames-Hook", 'ADDON_LOADED', HookFrames)
	HookFrames("MoveFrames-Hook", 'ADDON_LOADED')
end)
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-09-12, 11:14 PM   #2
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
yea thinking about it and looking at whats going on and how the game is responding my tweaks to what vrul originally wrote has created some bizarre and what one would think is impossible behavior. Ive been struggling with this for years now heh. Always come back to the same problem or rather set of problems. My frame mover system needs to load up during the addon loaded event to avoid taint bugs and just because frame positioning should be done right after the variables are available. Or so they say... Im constantly having issues with it not being able to return a value out of the saved variables. Possibly because i still em confused by the saved variables and tables and their working in conjunction, the prime example of this issue is in the frame mover system all the way around... as you can see i had to do all kinds of funny business to get the LOD config panels listings to function with the frame movers moveable frame table and oh my... it just gets out of control or rather just becomes a lot to wrap your brain around or at least for me to wrap mine around... The following code chunks are my "settings" setup, the small chunk in the core that deals with the event registering just incase and then the whole of the frame mover system. I should be able to change the frame mover event to have everything in the hookframes function in an addon loaded event along with the stuff at the top of that, with some minor changes well i made all proper changes and it loads with no errors but its not hooking the frames somthing is happening. Or rather not... Any thoughts on any of this? any recomendations even if its not addressing what appears to be the issue? as far as how is it an addon loaded fires after a player login? whats going on here?

Core -
Code:
local addonName, addon = ...
_G[addonName] = addon

--[[-----------------------------------------------------------------------------
Event handling - Use one frame to process multiple individual OnEvent needs
-------------------------------------------------------------------------------]]
do
	local frame, select, next, events = CreateFrame('Frame'), select, pairs({ })
	local register, unregister = frame.RegisterEvent, frame.UnregisterEvent
	frame:Hide()

	frame:SetScript('OnEvent', function(self, event, ...)
		for reference, func in next, events[event], nil do
			func(reference, event, ...)
		end
	end)

	function addon.RegisterEvent(reference, event, func, ...)
		if not events[event] then
			events[event] = { }
			register(frame, event)
		end
		events[event][reference] = func
	end

	function addon.RegisterEvents(reference, func, ...)
		local event
		for index = 1, select('#', ...) do
			event = select(index, ...)
			if not events[event] then
				events[event] = { }
				register(frame, event)
			end
			events[event][reference] = func
		end
	end

	function addon.UnregisterEvent(reference, event)
		if events[event] then
			events[event][reference] = nil
			if not next(events[event]) then
				events[event] = nil
				unregister(frame, event)
			end
		end
	end

	function addon.UnregisterAllEvents(reference)
		for event, registry in next, events, nil do
			registry[reference] = nil
			if not next(registry) then
				events[event] = nil
				unregister(frame, event)
			end
		end
	end
end

--[[-----------------------------------------------------------------------------
SafeCall - Queue a method of addon to run once combat ends if needed
-------------------------------------------------------------------------------]]
do
	local combatLockdown, queue = InCombatLockdown(), { }

	addon.RegisterEvent("Core-SafeCall-EnterCombat", 'PLAYER_REGEN_DISABLED', function(self)
		combatLockdown = true
	end)

	addon.RegisterEvent("Core-SafeCall-LeaveCombat", 'PLAYER_REGEN_ENABLED', function(self)
		combatLockdown = nil
		for method, arg in pairs(queue) do
			addon[method](addon, arg)
			queue[method] = nil
		end
	end)

	function addon:SafeCall(method, arg)
		if combatLockdown then
			queue[method] = arg or false
		else
			addon[method](addon, arg)
		end
	end
end

--[[-----------------------------------------------------------------------------
Localized class name to file class name look up table
-------------------------------------------------------------------------------]]
local CLASS = { }
for fileName, localName in pairs(LOCALIZED_CLASS_NAMES_MALE) do
	CLASS[localName] = fileName
end
for fileName, localName in pairs(LOCALIZED_CLASS_NAMES_FEMALE) do
	CLASS[localName] = fileName
end
addon.CLASS = CLASS
Settings Setup - note i did not include all default registrations the bottom of this file goes on for ever
Code:
local addonName, addon = ...
local defaults = {
	class = true
}

--[[-----------------------------------------------------------------------------
RegisterDefaultSetting - Allow modules to set their defaults
-------------------------------------------------------------------------------]]
function addon:RegisterDefaultSetting(key, value)	-- Use caution if using this after ADDON_LOADED
	defaults[key] = value
end

--[[-----------------------------------------------------------------------------
Load settings, apply version conversions as needed, and link to defaults
-------------------------------------------------------------------------------]]
addon.RegisterEvent("Settings-Load", 'ADDON_LOADED', function(self, event, name)
	if name ~= addonName then return end
	addon.UnregisterEvent(self, event)
	
	local name, class = UnitClass('player')
	name = UnitName('player')
	local realm = GetRealmName()

	local settings = _G[addonName .. "Settings"]
	if type(settings) ~= 'table' then
		settings = { }
		_G[addonName .. "Settings"] = settings
	end
	if type(settings[realm]) ~= 'table' then
		settings[realm] = { }
	end
	if type(settings[realm][name]) ~= 'table' then
		settings[realm][name] = { class = class }
	end

	settings.version = nil		-- Reset value on logout (so can iterate over data without issue)
	addon.settings = setmetatable(settings[realm][name], { __index = defaults })
	return settings
end)



--[[-----------------------------------------------------------------------------
Character Specific WTF Reset.
-------------------------------------------------------------------------------]]
function addon:ResetWTF()
	local name, class = UnitClass('player')
	name = UnitName('player')
	local realm = GetRealmName()
	local settings = _G[addonName .. "Settings"]
	
	if type(settings[realm][name]) == 'table' then
		settings[realm][name] = { class = class }
	end
	settings.version = nil	-- Reset value on logout 
	return settings 
end

--[[-----------------------------------------------------------------------------
Remove unused settings or settings that are the same as defaults
-------------------------------------------------------------------------------]]
addon.RegisterEvent("Settings-Unload", 'PLAYER_LOGOUT', function()
	local settings = addon.settings
	for key, value in pairs(settings) do
		if value == defaults[key] or defaults[key] == nil then
			settings[key] = nil
		end
	end
	_G[addonName .. "Settings"].version = GetAddOnMetadata(addonName, 'Version')
	_G[addonName .. "Settings"].lastVersion = GetAddOnMetadata(addonName, 'Version')
end)

--[[-----------------------------------------------------------------------------
LOD Configuration
-------------------------------------------------------------------------------]]
-- options
local LOA = LibStub and LibStub('LibOptionsAssist-1.0', true)
if not (LOA and select(2, GetAddOnInfo(addonName .. "_Config"))) then return end	-- Make sure config support exists

addon.configPanel = LOA:AddEntry(addonName, nil, addonName .. "_Config")

_G['SLASH_' .. addonName .. 1] = '/' .. addonName
_G['SLASH_' .. addonName .. 2] = '/' .. addonName:lower()
_G['SLASH_' .. addonName .. 3] = '/' .. addonName:upper()
_G['SLASH_' .. addonName .. 4] = '/gui'
SlashCmdList[addonName] = addon.configPanel

InterfaceOptionsFrame:SetFrameStrata('HIGH')
InterfaceOptionsFrame.SetFrameStrata = addon.DoNothing

--[[-----------------------------------------------------------------------------
GrimUI Options Frame Logo
-------------------------------------------------------------------------------]]
local GUI_Logo = CreateFrame('Frame', "GUI_Logo", InterfaceOptionsFrame)
GUI_Logo:SetHeight(256)
GUI_Logo:SetWidth(256)
GUI_Logo:SetPoint('BOTTOM', InterfaceOptionsFrame, 'TOP', 0, -75)
GUI_Logo:SetBackdrop({
	bgFile = [[Interface\AddOns\GrimUI\Media\GrimUILogo]],
	edgeFile = nil,
	edgeSize = 0,
	tile = false,
	insets = { left = 0, right = 0, top = 0, bottom = 0 }
})
GUI_Logo:Hide()

--[[-----------------------------------------------------------------------------
In Game Help System Setup
-------------------------------------------------------------------------------]]
function addon:LoadGUIHelp()
	addon.configPanel()
	GUI_HelpInitiate()
end

local GUI_HelpButton = CreateFrame('Button', "GUI_HelpButton", InterfaceOptionsFrame)
GUI_HelpButton:SetHeight(40)
GUI_HelpButton:SetWidth(25)
GUI_HelpButton:SetPoint('TOP', InterfaceOptionsFrame, 'TOP', 190, -37)
GUI_HelpButton:RegisterForClicks("AnyUp")
GUI_HelpButton:SetFrameStrata("DIALOG")
GUI_HelpButton:SetNormalTexture([[Interface\BUTTONS\UI-MicroButton-Help-Up]])
GUI_HelpButton:SetHighlightTexture([[Interface\BUTTONS\UI-MicroButton-Hilight]])
GUI_HelpButton:SetPushedTexture([[Interface\BUTTONS\UI-MicroButton-Help-Down]])
local HelpText = GUI_HelpButton:CreateFontString(nil, 'DIALOG')
HelpText:SetFont([[Fonts\FRIZQT__.TTF]], 14)
HelpText:SetText("Help")
HelpText:SetPoint("RIGHT", GUI_HelpButton, "LEFT", 0, -5)
HelpText:SetTextColor(1, .10, .10, 1)

GUI_HelpButton:SetScript('OnClick', function(self)
	
	if button == 'LeftButton' then
		GUI_HelpInitiate()
	else
		GUI_HelpInitiate()
	end
	
end)
GUI_HelpButton:Hide()

-- Auto load help on first use.
addon.RegisterEvent("Settings-Help", 'PLAYER_ENTERING_WORLD', function(self, event, name)
	addon.UnregisterEvent(self, event)
	if not addon.settings.HelpViewed or addon.settings.HelpViewed == false then
		addon:LoadGUIHelp()
	end
	if IsAddOnLoadOnDemand("GrimUI_DevTools") then 
		if addon.settings.loadDevTools == true then
			LoadAddOn("GrimUI_DevTools")
		end
	end
end)

--[[-------------------------------------------------------------------------------
Config Panel Show/Hide Scripts - Put all config panel adjustment functions here!
---------------------------------------------------------------------------------]]
addon.configPanel:SetScript('OnHide', function()
	GUI_Logo:Hide()
	GUI_HelpButton:Hide()
end)

addon.configPanel:SetScript('OnShow', function()
	GUI_Logo:Show()
	GUI_HelpButton:Show()
end)

--[[-------------------------------------------------------------------------------
Default WTF Settings - Try them here to stop nextto error and other errors.
---------------------------------------------------------------------------------]]
--Settings.lua settings
addon:RegisterDefaultSetting("HelpViewed", false)

--DevTools.lua settings
addon:RegisterDefaultSetting("loadDevTools", false)
addon:RegisterDefaultSetting("showDevBar", true)

--HideFrames.lua

--Skins.lua settings
addon:RegisterDefaultSetting("useViewPort", false)
addon:RegisterDefaultSetting("VPLeftPos", 0)
addon:RegisterDefaultSetting("VPRightPos", 0)
addon:RegisterDefaultSetting("VPTopPos", 0)
addon:RegisterDefaultSetting("VPBottomPos", 90)
addon:RegisterDefaultSetting("artSetPoint", 0)
addon:RegisterDefaultSetting("artSetAlpha", 1)
addon:RegisterDefaultSetting("ShowAggroArt", true)
addon:RegisterDefaultSetting("resolution", false)
addon:RegisterDefaultSetting("uiSkin", [[Interface\AddOns\]] .. addonName .. [[\Skins\]] .. UnitFactionGroup('player'))

--Anchors.lua settings
addon:RegisterDefaultSetting("LootGrowDirection", true)

--FrameMover.lua
--addon:RegisterDefaultSetting("movedFrames", true)
addon:RegisterDefaultSetting("lockFrames", false)

--ChatFrames.lua settings
addon:RegisterDefaultSetting("chatInitialized", false)
addon:RegisterDefaultSetting("showCombatLog", true)
addon:RegisterDefaultSetting("showInfoLog", true)
addon:RegisterDefaultSetting("showChatTabs", false)
addon:RegisterDefaultSetting("useGrimChat", true)

--MiniMap.lua settings
addon:RegisterDefaultSetting("NotSoMinimap", false)
addon:RegisterDefaultSetting("ShowMiniMap", true)
addon:RegisterDefaultSetting("MapArt", true)
addon:RegisterDefaultSetting("MapButStyle", 1)
addon:RegisterDefaultSetting("GmapMask", "Interface\\BUTTONS\\WHITE8X8")
addon:RegisterDefaultSetting("NSmapMask", "Interface\\BUTTONS\\WHITE8X8")
addon:RegisterDefaultSetting("GmapAlpha", 1)
addon:RegisterDefaultSetting("NSmapAlpha", 1)
addon:RegisterDefaultSetting("NSmapScale", 1.5)
Frame Mover - I think this is the cause of why sometimes it cant find things
Code:
local addonName, addon = ...

--[[-----------------------------------------------------------------------------
Frames to allow moving and saving
-------------------------------------------------------------------------------]]
--["FrameName"] =	false  - move the frame
--true   - move the frame's parent instead
--string - move the named frame instead
DefGMoveableFrames = {
			['MiniMapLFGFrame'] = false,
			['ShardBarFrame'] = false,
			['SpellBookFrame'] = false,
			['QuestLogFrame'] = false,
			['FriendsFrame'] = false,
			['LFDParentFrame'] = false,
			['KnowledgeBaseFrame'] = true,
			['MerchantFrame'] = false,
			['MailFrame'] = false,
			['DressUpFrame'] = false,
			['TaxiFrame'] = false,
			['QuestLogFrame'] = false,
			['PaperDollFrame'] = true,
			['PVPFrame'] = false,
			['WatchFrameHeader'] = true,
			['InspectFrame'] = false,
			['PlayerTalentFrame'] = false,
			['AchievementFrame'] = false,
			['AchievementFrameHeader'] = true,
			['AchievementFrameCategoriesContainer'] = 'AchievementFrame',
			['GlyphFrame'] = 'PlayerTalentFrame',
			['CalendarFrame'] = false,
			['ContainerFrame1'] = false,
			['ContainerFrame2'] = false,
			['ContainerFrame3'] = false,
			['ContainerFrame4'] = false,
			['ContainerFrame5'] = false,
			['Minimap'] = false,
			
			--GrimUI Frames
			['GrimUIPlayerFrame'] = false,
			['GrimUIPartyFrame1'] = false,
			['GrimUIPartyFrame2'] = false,
			['GrimUIPartyFrame3'] = false,
			['GrimUIPartyFrame4'] = false,
			['GrimExpBar'] = false,
			['GrimRepBar'] = false,
			['GUI_DevBar'] = false,
			['Audio_UI_Control'] = true,
			['GUI_AnchorPoint1'] = false,
			['GUI_AnchorPoint2'] = false,
			['GUI_AnchorPoint3'] = false,
			['GUI_AnchorPoint4'] = false,
			['GUI_AnchorPoint5'] = false,
			['GUI_AnchorPoint6'] = false,
			['GUI_AnchorPoint7'] = false,
			['GUI_AnchorPoint8'] = false,
			['GUI_AnchorPoint9'] = false,
			['GUI_AnchorPoint10'] = false,
			['GUI_AnchorPoint11'] = false,

}

function addon:DefaultMoveableFrames()
	if not GMoveableFrames then
		GMoveableFrames = GMoveableFrames or DefGMoveableFrames
	end
end

--[[-----------------------------------------------------------------------------
Frame hooking
-------------------------------------------------------------------------------]]
local hookedFrame, parentFrame, movedFrames, oldPoint, oldX, oldY = { }, { }
local function GetRelativePosition(frame)
	local _, _, uiWidth, uiHeight = UIParent:GetRect()
	local left, bottom, width, height = frame:GetRect()
	if bottom + height / 2 >= uiHeight / 2 then
		if left + width / 2 >= uiWidth / 2 then
			return 'TOPRIGHT', left + width - uiWidth - 1, bottom + height - uiHeight - 1
		else
			return 'TOPLEFT', left, bottom + height - uiHeight - 1
		end
	elseif left + width / 2 >= uiWidth / 2 then
		return 'BOTTOMRIGHT', left + width - uiWidth - 1, bottom
	end
	return 'BOTTOMLEFT', left, bottom
end

local function OnShow(self)
	local frame = parentFrame[self] or self
	if self == Minimap then
		if addon.settings.NotSoMinimap == true then
			self = _G["NSMiniMapAnchor"] -- needed for extra minimap functions
			frame = _G["NSMiniMapAnchor"] 
		else 
			self = _G["GMiniMapAnchor"] 
			frame = _G["GMiniMapAnchor"] 
		end
	end
	local position = movedFrames[self:GetName()]
	if position then
		addon:UnlockFrame(frame)
		frame:ClearAllPoints()
		frame:SetPoint(unpack(position))
		addon:LockFrame(frame)
	end
end

local function OnMouseDown(self, button)
	if button ~= 'LeftButton' or addon.settings.lockFrames then return end
	if IsControlKeyDown() and button == 'LeftButton' then
	local frame = parentFrame[self] or self
	if self == Minimap then
		if addon.settings.NotSoMinimap == true then
			self = _G["NSMiniMapAnchor"] -- needed for extra minimap functions
			frame = _G["NSMiniMapAnchor"]
		else 
			self = _G["GMiniMapAnchor"]
			frame = _G["GMiniMapAnchor"]
		end
	end
	oldPoint, oldX, oldY = GetRelativePosition(frame)
	addon:UnlockFrame(frame)
	frame:StartMoving()
	end
end

local function OnMouseUp(self, button)
	if button ~= 'LeftButton' or not oldPoint then return end
	local frame = parentFrame[self] or self
	if self == Minimap then
		if addon.settings.NotSoMinimap == true then
			self = _G["NSMiniMapAnchor"] -- needed for extra minimap functions
			frame = _G["NSMiniMapAnchor"]
		else 
			self = _G["GMiniMapAnchor"]
			frame = _G["GMiniMapAnchor"]
		end
	end
	frame:StopMovingOrSizing()
	addon:LockFrame(frame)
	local point, x, y = GetRelativePosition(frame)
	if point ~= oldPoint or x ~= oldX or y ~= oldY then
		movedFrames[frame:GetName()] = { point, 'UIParent', x, y }
	end
	oldPoint, oldX, oldY = nil, nil, nil
end

function HookFrame(name, parent)
	if hookedFrame[name] then 
		return true
	end
	local frame = _G[name]
	if not frame then
		return 
	end
	if parent then
		if type(parent) == 'string' then
			parent = _G[parent]
		else
			if frame:GetParent() ~= UIParent then
				parent = frame:GetParent()
			else
				parent = frame
			end
		end
		if not parent then 
			return 
		end
		HookFrame(parent:GetName())
		while(parentFrame[parent]) do
			parent = parentFrame[parent]
		end
		parentFrame[frame] = parent
	elseif not name:match("^" .. addonName) then
		frame:HookScript('OnShow', OnShow)
	end
	frame:EnableMouse(true)
	frame:EnableKeyboard(true)
	frame:SetMovable(true)
	frame:SetClampedToScreen(false)
	frame:HookScript('OnMouseDown', OnMouseDown)
	frame:HookScript('OnMouseUp', OnMouseUp)
	hookedFrame[name] = true
	if movedFrames[name] and frame:IsShown() then
		OnShow(frame)
	end
	return true
end

--[[-----------------------------------------------------------------------------
Initialize
-------------------------------------------------------------------------------]]
local moveableframes = {}
local function VerCheckUpdate()
	local CurVer = GetAddOnMetadata(addonName, 'Version')
	local LastVer = _G[addonName .. "Settings"].lastVersion
	if LastVer then
		if CurVer > LastVer then 
			for gname, gparent in pairs(DefGMoveableFrames) do 
				local TabCont = tContains( GMoveableFrames, gname )
				if TabCont == nil then
					GMoveableFrames[gname] = gparent
				end
			end
		end
	_G[addonName .. "Settings"].lastVersion = nil
	end
end

addon.RegisterEvent("MoveFrames-Initialize", 'PLAYER_LOGIN', function(self, event)
	addon.UnregisterEvent(self, event)
	movedFrames = addon.settings.movedFrames
	if type(movedFrames) ~= 'table' then
		movedFrames = { }
		addon.settings.movedFrames = movedFrames
	end
	addon:RegisterDefaultSetting("movedFrames", true)	-- Prevent it from being removed on PLAYER_LOGOUT
	local function HookFrames(self, event)
		addon:DefaultMoveableFrames()
		VerCheckUpdate()
		
		moveableframes = CopyTable(GMoveableFrames)
		for name, parent in pairs(moveableframes) do
			if HookFrame(name, parent) then
				moveableframes[name] = nil
			end
		end
		if next(moveableframes) then return end
		addon.UnregisterEvent(self, event)
	end
	addon.RegisterEvent("MoveFrames-Hook", 'ADDON_LOADED', HookFrames)
	HookFrames("MoveFrames-Hook", 'ADDON_LOADED')
end)

--[[-----------------------------------------------------------------------------
in game add remove functions
-------------------------------------------------------------------------------]]
function addon:AddMoveableFrame(name, parvar)
	if parvar then
		if parvar == string then
			parvar = _G[parvar]
		end
		GMoveableFrames[name] = parvar
		HookFrame(name, parvar)
	elseif not parvar or parvar == "" or parvar == nil then 
		parvar = false
		GMoveableFrames[name] = parvar
		HookFrame(name, parvar)
	end
end

function addon:RemMoveableFrame(name)
	GMoveableFrames[name] = nil
end

SlashCmdList["GADDMOVE_SHORTHAND"] = function(cmd)
	local name, parvar = strsplit(" ", cmd) 
	if parvar then
		if parvar == "true" then
			parvar = true
		elseif parvar == "false" then
			parvar = false
		end
	end
	if not parvar then parvar = false end
	addon:AddMoveableFrame(name, parvar)
end
SLASH_GADDMOVE_SHORTHAND1 = "/gaddmove"

SlashCmdList["GREMMOVE_SHORTHAND"] = function(cmd)
	addon:RemMoveableFrame(cmd)
end
SLASH_GREMMOVE_SHORTHAND1 = "/gremmove"
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-10-12, 02:06 AM   #3
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
ADDON_LOADED fires every time any addon (including the Blizzard ones) finishes loading. The first argument passed with the event is the name of the addon that finished loading. When it fires for an addon, that means that:
  • all of the files in the addon's folder have been read,
  • all objects defined in any XML files have been created,
  • all code in the top-level scope of any Lua files has been executed,
  • all font and textures files have been loaded into memory, and
  • all previously stored saved variables for the addon have been loaded into memory.
Most addons listen for their own ADDON_LOADED event, and perform basic start-up tasks like initializing saved variables when it fires.

PLAYER_LOGIN fires at approximately the same time that the initial loading screen disappears and the game world becomes visible. Generally, this event means that all non-LoD addons have finished loading.

Most addons listen for this event, and perform additional start-up tasks like creating visible UI objects (eg. action buttons or unit frames) and registering for general events (eg. UNIT_HEALTH or PLAYER_REGEN_ENABLED). Since the user cannot see or interact with the UI prior to this event firing, there is no reason to perform these tasks earlier.

If you are writing an addon that modifies another addon's configuration, you will usually want to listen for the other addon's ADDON_LOADED event, and make your changes when it fires. However, depending on the other addon's design, and the nature of the changes you wish to make, you may need to listen for the PLAYER_LOGIN event instead (though I have never found this useful), or temporarily pre-hook one of the addon's functions when its ADDON_LOADED event fires and make your changes when that function is run:

Lua Code:
  1. local f = CreateFrame("Frame")
  2. f:RegisterEvent("ADDON_LOADED")
  3. f:SetScript("OnEvent", function(self, event, addonName)
  4.     if addonName ~= "TargetAddon" then
  5.         -- Not the right addon.
  6.         -- Don't do anything else just now, but keep listening
  7.         -- for future events.
  8.         return
  9.     end
  10.  
  11.     -- If we reach this point, it's the right addon.
  12.  
  13.     -- Get a reference to a function the addon runs to set itself up.
  14.     local original = TargetAddon.DoSomeSetupStuff
  15.  
  16.     -- Make the addon run a different function instead.
  17.     function TargetAddon:DoSomeSetupStuff(...)
  18.         -- Change some settings here, before the addon tries
  19.         -- to do anything with them.
  20.  
  21.         -- Let the original function run.
  22.         original(self, ...)
  23.  
  24.         -- Run any code here that needs to happen after the
  25.         -- addon sets itself up, like parenting frames to other
  26.         -- frames, etc.
  27.  
  28.         -- Put the original function back. (optional)
  29.         self.DoSomeSetupStuff = original
  30.     end
  31.  
  32.     -- We found the addon and modified it. We don't need to
  33.     -- listen for the addon's loading event anymore.
  34.     self:UnregisterEvent("ADDON_LOADED")
  35.  
  36.     -- No events are registered anymore, so we don't need an
  37.     -- event handler anymore either. Unset it, so this function
  38.     -- gets garbage-collected and removed from memory.
  39.     self:SetScript("OnEvent", nil)
  40. end)

For a more complex example that handles modifying multiple addons, see the attached file. It's what I use to apply custom borders to a number of addons. You'll notice that in some cases, I need to permanently hook one or more of the addon's functions, so that I can overwrite changes the addon makes during gameplay, long after the initial loading process.
Attached Files
File Type: lua Apply.lua (13.3 KB, 474 views)
  Reply With Quote
02-10-12, 02:17 AM   #4
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
So h ow is it that my above code functions? if i have addonloaded events fireing after a player login event registration?

and whats the proper means of going down the saved variables table? SavedvairableName[firstlvl][2ndlvl]?
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]

Last edited by Grimsin : 02-10-12 at 02:19 AM.
  Reply With Quote
02-10-12, 02:42 AM   #5
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,306
The wowpedia loading process description is pretty accurate in its description. me and scott changed our addon loading process after I figured that VAIABLES_LOADED and PLAYER_ENTERING_WORLD sometimes loaded in different order and wasn't consistent enough to guarantee using just one of them for the same code segment.

http://www.wowpedia.org/AddOn_loading_process

Saved variables loading

After the addon code has been loaded, the loading process can be followed by registering for various events, listed here in order of firing.
1. ADDON_LOADED This event fires whenever an AddOn has finished loading and the SavedVariables for that AddOn have been loaded from their file.

2. SPELLS_CHANGED This event fires shortly before the PLAYER_LOGIN event and signals that information on the user's spells has been loaded and is available to the UI.

3. PLAYER_LOGIN This event fires immediately before PLAYER_ENTERING_WORLD.
Most information about the game world should now be available to the UI.
All Sizing and Positioning of frames is supposed to be completed before this event fires.
AddOns that want to do one-time initialization procedures once the player has "entered the world" should use this event instead of PLAYER_ENTERING_WORLD.

4. PLAYER_ENTERING_WORLD This event fires immediately after PLAYER_LOGIN
Most information about the game world should now be available to the UI. If this is an interface reload rather than a fresh log in, talent information should also be available.
All Sizing and Positioning of frames is supposed to be completed before this event fires.
This event also fires whenever the player enters/leaves an instance and generally whenever the player sees a loading screen

5. PLAYER_ALIVE This event fires after PLAYER_ENTERING_WORLD
Quest and Talent information should now be available to the UI


Until 3.0, VARIABLES_LOADED used to fire upon completion of the addon loading process; since 3.0, it is fired in response to CVars, Keybindings and other associated "Blizzard" variables being loaded, and may therefore be delayed until after PLAYER_ENTERING_WORLD. The event may still be useful to override positioning data stored in layout-cache.txt
And as for the variables table, thats the way I usually use them.
__________________
  Reply With Quote
02-10-12, 02:44 AM   #6
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
Im not used variables loaded though im getting ADDON_LOADED after PLAYER_LOGIN....
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-10-12, 02:57 AM   #7
Xrystal
nUI Maintainer
 
Xrystal's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 5,306
If you have a lot of addons that could be possible. Have you recorded which events trigger and in what order and are they consistent or haphazard ? It might be that you want to avoid using PLAYER_LOGIN for anything other than utilise what has been said is available then.
__________________
  Reply With Quote
02-10-12, 04:37 AM   #8
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
ADDON_LOADED fires every time any addon loads. This includes LOD addons, which can (and usually do) load after the initial login sequence completes.

If you register for ADDON_LOADED and PLAYER_LOGIN, and print messages each time they fire, you might see something like this:
  1. *Player clicks Enter World from the character selection screen*
  2. ADDON_LOADED, AddonLoader
  3. ADDON_LOADED, BadBoy
  4. ADDON_LOADED, Blizzard_ClientSavedVariables
  5. ADDON_LOADED, Blizzard_CombatLog
  6. ADDON_LOADED, Clique
  7. ADDON_LOADED, Grid
  8. ADDON_LOADED, Macaroon
  9. ADDON_LOADED, Squire2
  10. ADDON_LOADED, TomTom
  11. PLAYER_LOGIN
  12. *Loading screen disappears and player can see the game world*
  13. ADDON_LOADED, Bazooka
  14. ADDON_LOADED, TourGuide
  15. *Player types /squire to open the options for the addon Squire2*
  16. ADDON_LOADED, Squire2_Config
  17. *Player opens the mailbox*
  18. ADDON_LOADED, Postal
  19. *Player opens the auction house*
  20. ADDON_LOADED, Blizzard_AuctionUI
  21. *Player opens the currency/token frame*
  22. ADDON_LOADED, Blizzard_TokenUI
  23. ADDON_LOADED, Exonumist
Everything before PLAYER_LOGIN was a "normal" addon, not LOD.

The next two addons (Bazooka and TourGuide) are not LOD, but they do specify a LoadManager and that LoadManager is present (in this case AddonLoader), so they are treated as LOD. In this case, other data tells AddonLoader to load them "Always", so they get loaded immediately after login. This technique is often used to help reduce the amount of time users spend waiting for the loading screen.

Squire2_Config is LOD, and is loaded "manually" by Squire2 when the user requests the options panel.

Postal is not LOD either, but is treated as such because its LoadManager exists (AddonLoader again), and its load condition tells AddonLoader to load it when the mailbox opens.

Blizzard_AuctionUI is LOD, and is loaded by the game when the user wants the auction house.

Blizzard_TokenUI is also LOD, and is loaded by the game when the user wants the token frame.

Exonumist is not LOD, but is treated as such because it specifies a LoadsWith condition. In this case, it specifies that it should load with the Blizzard_TokenUI, so when that addon loads, so does this one. If the addon listed under LoadsWith was not LOD, and was loaded during the initial login sequence, this addon would also load at that time. If the addon listed under LoadsWith did not exist, this addon would never load, although LoadsWith is not the same as Dependencies, so manually calling LoadAddOn("Exonumist") would force it to load, whereas calling LoadAddOn on an addon whose Dependencies were missing would fail.

As for your code, honestly I did not read through all of it. It's very long, and just sitting down and reading someone else's addon code like a novel from start to finish is not something most people would call fun.
  Reply With Quote
02-10-12, 08:23 AM   #9
Nimhfree
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Aug 2006
Posts: 252
For those that are making use of the wowpedia documentation, I just want to note that PLAYER_ALIVE is not sent when the UI is reloaded, but it is when you login. So if you are planning on ever using PLAYER_ALIVE to set some state you cannot have the state correct if the UI ever reloads. Therefore, I use QUEST_LOG_UPDATE instead for state setting (which happens after PLAYER_ALIVE in a login situation) because it happens for both login and reload UI.
  Reply With Quote
02-10-12, 12:11 PM   #10
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
Originally Posted by Nimhfree View Post
For those that are making use of the wowpedia documentation, I just want to note that PLAYER_ALIVE is not sent when the UI is reloaded, but it is when you login. So if you are planning on ever using PLAYER_ALIVE to set some state you cannot have the state correct if the UI ever reloads. Therefore, I use QUEST_LOG_UPDATE instead for state setting (which happens after PLAYER_ALIVE in a login situation) because it happens for both login and reload UI.
Also player alive fires im pretty sure when ever you resurrect.
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-10-12, 12:15 PM   #11
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
Phanx - that explains it i think. The bliz LOD's are loading up after player login, curious though do they fire the addon loaded events prior to player entering world? and maybe this is altered a bit even more by the fact that some places in my addon code load Bliz LOD stuff in order to start data collection the moment a player "views" the world. If that is the case i suppose i can leave this for now... I did figure out where my issue was and my problem is im trying to use addon.settings.blahblahblah before my addon code has created "addon" or "settings" let alone blahblahblah. Basiclly i was trying to access my variables via the meta table after the game loaded and the metatable is not available yet... of course the problem once i figured that out was that i have to write out the long way all the stuff that the addon.settings part is handling. which is lots of somthing = {}
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-10-12, 03:51 PM   #12
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
time during loading accuracy.

Okay so i had an idea... it may be a bad one maybe not. I need some details first. So how accurate can time delays be? could you delay something into 100ths and 1000's of a second? does wow understand a time delay of 0.001? i know it understands 0.01 as a 1/10th of a second. Why is this important you may be thinking. Well my next question is how accurate are these timers, how reliable? are they at all effected by lag? My thinking is since now i have an issue of even if a file is listed first in the toc the addon settings are still in an addon loaded event and even though its listed before other files if i use addon loaded in other files it has no guaranty it loads first in fact i cant get it to load first no mater how many times i reload. So my thinking is what if, where i need things to fire after that settings code but still in an addon loaded event from another file, I use a timer of 0.001 or the like to stall that code. Would that insure it fires prior to player_login? the big reason to load as much as possible prior to player login and player entering world is it helps alleviate tainting especially when you reload or relog while in combat, prime example is if your dc'd during a boss fight. Ive noticed that combat lockdown and the secure environment do not invoke until player_login or later.
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-10-12, 10:48 PM   #13
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Grim, your posts would be 100x more readable if you used paragraphs instead of typing everything in one giant block.

Originally Posted by Grimsin
The bliz LOD's are loading up after player login, curious though do they fire the addon loaded events prior to player entering world?
I have no idea, and it's probably not even consistent between UI loads, let alone between different computers. It's trivially easy to find out for yourself what it's doing on your computer, though:

Code:
local t = {}
local f = CreateFrame("Frame")
f:RegisterEvent("ADDON_LOADED")
f:RegisterEvent("PLAYER_ALIVE")
f:RegisterEvent("PLAYER_ENTERING_WORLD")
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function(f, e, ...)
     local m = string.join(", ", e, ...)
     table.insert(t, m)
     print(m)
end)
InterfaceLoadingLog = t
This will print a message to your chat frame every time one of the loading events fires, as well as add them all to a table in the order they fire, so you can check the log later:

Code:
/run for i,v in ipairs(InterfaceLoadingLog) do print(i..". "..v) end
Originally Posted by Grimsin
... my addon code load Bliz LOD stuff in order to start data collection ...
What kind of "data collection" are you doing that requires Blizzard LOD addons to be loaded before they are relevant to the actual user experience?

Originally Posted by Grimsin
... my problem is im trying to use addon.settings.blahblahblah before my addon code has created "addon" or "settings" let alone blahblahblah ...
Any code that needs to reference your addon's saved variables cannot be run before the ADDON_LOADED event has fired for your addon.

Originally Posted by Grimsin
... i have to write out the long way all the stuff that the addon.settings part is handling. which is lots of somthing = {}
No. Instead of inserting default values that probably do not match the actual saved values, just delay running that code until after the ADDON_LOADED event has fired for your addon.

If you are having trouble initializing default settings for your addon, and are not using a library like AceDB-3.0 to handle this, here is a simple method for handling defaults:

Code:
local db

local MyAddon = CreateFrame("Frame")
MyAddon:RegisterEvent("ADDON_LOADED")
MyAddon:SetScript("OnEvent", function(self, event, addon)
	if addon ~= "MyAddon" then return end

	-- Define a table containing a complete configuration
	-- for your addon, with the default values:
	local defaultsTable = {
		blah = "blah",
		etc = "something",
		num = 5,
		height = 200,
		width = 600,
		color = {
			r = 1,
			g = 1,
			b = 0,
			a = 0.5,
			magic = true,
		}
	}

	-- This function goes through your defaults table
	-- and copies to the real settings table any values
	-- that are the wrong type (eg. nil vs string, or
	-- number vs boolean).
	-- It is recursive, so it will go through sub-tables
	-- as well, up to any depth:
	local function copyDefaults(a, b)
		if type(a) ~= "table" then return {} end
		if type(b) ~= "table" then b = {} end
		for k, v in pairs(a) do
			if type(v) == "table" then
				b[k] = copyDefaults(v, b[k])
			elseif type(v) ~= type(b[k]) then
				b[k] = v
			end
		end
		return b
	end

	-- Now call the function, supplying your defaults table
	-- as the source, and your addon's saved variable table
	-- as the destination:
	db = copyDefaults(defaultsTable, MyAddonDB)
	-- Finally, make sure your addon's saved variable points
	-- to the table, in case it did not already exist:
	MyAddonDB = db

	-- Now you can use "db.blah" or "db.color.magic" to get
	-- the current settings. There is no need for any metatables.
end)
  Reply With Quote
02-10-12, 10:59 PM   #14
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Grimsin View Post
... even if a file is listed first in the toc, the addon settings are still in an ADDON_LOADED event, and even though its listed before other files, if i use ADDON_LOADED in other files, it has no guaranty it loads first ...
The order in which frames receive an event is undefined. You cannot depend on Frame A receiving an event before Frame B receives the same event.

It sounds like you are simply doing things wrong. If you need multiple files to be aware of when your addon finishes loading, and run setup functions, here is a simple method of handling that that does not require multiple frames handling ADDON_LOADED, or convoluted timer solutions.

In your main file (the one that's loaded first), handle ADDON_LOADED and initializing your saved variables.

Near the top of that file, add:

Code:
MyAddon.loadFunctions = {}
Inside the ADDON_LOADED event handler, after you have initialized saved variables, add:

Code:
for _, methodName in ipairs(MyAddon.loadFunctions) do
	MyAddon[methodName](MyAddon)
end
Then, in each other file, add any functions that need to be run after saved variables are initialized to that table:

Code:
function MyAddon:DoSomething()
	-- Do stuff.
end

table.insert(MyAddon.loadFunctions, "DoSomething")
Originally Posted by Grimsin View Post
the big reason to load as much as possible prior to player login and player entering world is it helps alleviate tainting especially when you reload or relog while in combat
The load order of addons is irrelevant for taint. If something is tainted, it does not matter if it was tainted before or after some event fired; it's still tainted, and it will still cause the same problems (if any).

I think you're thinking of secure frames, and the actions that can only be performed on them outside of combat. In this case, you can still delay performing these actions until PLAYER_LOGIN fires; the UI is not locked down until some time after the PLAYER_REGEN_DISABLED event fires to indicate that the player has been flagged for combat, and you can always check to see if the UI is locked down by calling InCombatLockdown().

It might be more helpful if you are more specific about what code you are trying to run that you think needs to be called before PLAYER_LOGIN.
  Reply With Quote
02-11-12, 03:02 PM   #15
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
your right secure frames is what im thinking. So your saying the regen disabled happens after player login? because i know it happens before player entering world or at least last time i tried it it did... a few places i have this issue is in party frames, the focus frame, the vehicle action bar and the boss target frame.

As for data collection, two places i can think of right off the top of my head is the guild information and the calendar information Actually I think this may be true for any of the bliz LOD stuff, anything thats in the addons directory created by bliz. My UI allows the user to set the xp bar to show guild xp, in order for this to be available when they log in and display correct i have to load the guild stuff, same goes for the calendar in order for the highlighting on the clock that lets players know they have pending events to display proper it has to be able to check for events and the events dont load until the calendar loads...

I may go about alleviating the calendar issue the same way i did the mail issue by hooking blizzards own notification highlight to my clock. I did this for the mail since the mail had the same issue and i could not get the information i needed at the times needed to make the mail notifications function right so i made it so blizzards stuff just highlighted my frames. Did the same for hooking blizzards mail tooltip to my frame since i could not get the same function any other way. There is no way around the guild xp bar requiring the guild information be loaded.

And yes many people have informed me that i write exactly as i would speak. i tried to break it up some... and use more ","

also im using Vrul's LibOptionsAssist to handle options and the config panel. along with aceGUI and other ace config panel libs.
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-11-12, 03:19 PM   #16
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Grimsin View Post
regen disabled happens after player login?
It should, yes. Use the code in my last post to check. You can use the same code to check the order of any events you want.

Originally Posted by Grimsin View Post
... party frames, the focus frame, the vehicle action bar and the boss target frame.
These should all be able to be created in response to PLAYER_LOGIN. All addons using AceAddon-3.0 (such as PitBull, STUF, and Grid) create frames in their addon's :OnEnable method, which AceAddon-3.0 executes in response to PLAYER_LOGIN firing. oUF also uses PLAYER_LOGIN to trigger the creation of frames queued using its :Factory method.

If you are trying to create/modify secure frames when PLAYER_LOGIN fires, and are having problems, post the code. There's only so much anyone can tell you without seeing the actual code you are working with.

Originally Posted by Grimsin View Post
My UI allows the user to set the xp bar to show guild xp, in order for this to be available when they log in and display correct i have to load the guild stuff, ...
API functions do not depend on an addon being loaded. You should be able to call guild-related API functions like GetGuildRoster, GetGuildRosterContribution, and GetGuildRosterLargestContribution regardless of which addons are loaded.

Again, though, there's only so much I can tell you without seeing your code.

Originally Posted by Grimsin View Post
... same goes for the calendar in order for the highlighting on the clock that lets players know they have pending events to display proper it has to be able to check for events and the events dont load until the calendar loads.
Again, this information should be available through the appropriate API regardless of whether any addon is loaded. The default UI's clock can show pending calendar invites before the Blizzard_CalendarUI is loaded, so your addon's clock can do the same. If you aren't sure how to do it, look at the Blizzard UI code.
  Reply With Quote
02-11-12, 04:19 PM   #17
Grimsin
A Molten Giant
 
Grimsin's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2006
Posts: 990
Yea i will look at the code and then just alter and hook the bliz highlight but what im saying is the bliz highlight as able to access the information without the calendar LOD being loaded... we how ever can not. at least thats how it looks when i messed with it before ill be back to poking at it here in a minute. you have to open the calendar to get numpendinginvite information.
__________________
"Are we there yet?"

GrimUI
[SIGPIC][/SIGPIC]
  Reply With Quote
02-11-12, 07:32 PM   #18
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Grimsin View Post
... the bliz highlight as able to access the information without the calendar LOD being loaded... we how ever can not.
The Blizzard UI is just Lua/XML code like addons. If information is available to the Blizzard UI, it is also available to addons, though the exact same API.

See:
http://wowprogramming.com/utils/xmlb...L/GameTime.lua

The following events are relevant:
- PLAYER_ENTERING_WORLD
- CALENDAR_UPDATE_PENDING_INVITES
- CALENDAR_EVENT_ALARM

See the "GameTimeFrame_OnEvent" function to see which API functions are called to determine if there is a calendar invite pending. There's nothing magical going on there. You can even just copy/paste the code directly into your addon if you want.
  Reply With Quote
04-25-12, 04:50 PM   #19
Billtopia
A Warpwood Thunder Caller
AddOn Author - Click to view addons
Join Date: Apr 2009
Posts: 94
older thread but for other people who may have the same issue... put the addon names of the addons you need to hook as dependencies in the toc file, and ones you want to hook only if loaded as optional dependencies so that way they will load before your addon does. you can then hook all the functions in your addon loaded event.
  Reply With Quote
04-25-12, 05:45 PM   #20
Dridzt
A Pyroguard Emberseer
 
Dridzt's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2005
Posts: 1,253
Originally Posted by Billtopia View Post
<snip>... put the addon names of the addons you need to hook as dependencies in the toc file, <snip>
That is not always a good idea.
Especially where it concerns Blizzard_* addons.
ReqDep -ing an addon forces it to load when the client gets to parsing your addon .toc file during the loading process.

The problem is that the Blizzard_* addons and the FrameXML code have some hidden dependencies on each other that are not enforced through a similar mechanism.
Parts of Blizzard code just "expect" other Blizzard addons or parts to be present when they get to loading.

When you force-load a Blizzard_* addon by making it a required dependency of your addon you are liable to break alot of things in very subtle and hard to debug ways (examples: missing events in blizz calendar, missing buttons in archaeology_ui, and more examples exist which are hard to group together)

It is generally alot better to avoid messing with "natural" load order and instead either delay loading other addons until you can be relatively sure the client has completely finished initializing, or monitor ADDON_LOADED and react to addons loading off their own accord.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Mass confusion, loading process

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off