Thread Tools Display Modes
10-07-18, 10:42 AM   #1
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
Temporary Chat Frames/Community Chat Frame/New Chat Window Issue

Good afternoon all,

I'm in the process of writing a chat replacement mod that largely reskins the blizzard frames and does a few streamlining appearance changes. As it is written, the addon is fully functional for most tasks. However, I've noticed when entering pet battles, which creates a temporary chat window, that the tab appears unaligned to the other tabs. Additionally, I'm getting a lot of taint issues from this addon when I make new chat windows, join/create/leave communities.

The code can be found here: https://pastebin.com/S0nvycqG

If anyone sees any glaring mistakes, I'd be more than appreciative to hear them.

Thanks for your time!
  Reply With Quote
10-07-18, 11:52 AM   #2
Kanegasi
A Molten Giant
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 666
Just a quick look at your code, I noticed you're iterating CHAT_FRAMES. Temporary chat windows from whispers and the pet battle log aren't added to that table. Their ID is forced as 11 and above and do not persist between sessions.

You can save pointers to all created chat windows, 1-10 or temporary, by hooking FCF_SetWindowName(frame,name). Here's what I do in my TrueChatFrameHistory addon:

Lua Code:
  1. local CF,cfid={},{}
  2. hooksecurefunc("FCF_SetWindowName",function(frame,name)
  3.     local id=frame:GetID()
  4.     CF[frame]=id -- main ChatFrame pointers
  5.     cfid[id]=frame -- access by id
  6. end)

As for the taints, I'm pretty sure it's this:

Lua Code:
  1. local DefaultSetItemRef = SetItemRef
  2. function SetItemRef(link, ...)

Replacing any of Blizzard's objects is not a good idea.
  Reply With Quote
10-07-18, 12:46 PM   #3
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
Thank you for this response. I'll give it a look for both ideas!
  Reply With Quote
10-07-18, 08:28 PM   #4
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
I discovered the source of the taint I was experiencing with the community creation/destruction/joining. In the following function:
Lua Code:
  1. local function AddMessage(self, text, ...)
  2.     --Remove player brackets
  3.     text = text:gsub('|Hplayer:([^%|]+)|h%[([^%]]+)%]|h', '|Hplayer:%1|h%2|h')
  4.     --Remove Away and Busy Tags
  5.     text = text:gsub('<Away>', '')
  6.     text = text:gsub('<Busy>', '')
  7.  
  8.     --Strip yells: says: from chat
  9.     text = text:gsub('|Hplayer:([^%|]+)|h(.+)|h says:', '|Hplayer:%1|h%2|h:')
  10.     text = text:gsub('|Hplayer:([^%|]+)|h(.+)|h yells:', '|Hplayer:%1|h%2|h:')
  11.  
  12.     --Shorten GMotD and change online/offline warnings
  13.     text = text:gsub('Guild Message of the Day:', 'GMotD -')
  14.     text = text:gsub('has come online.', '+')
  15.     text = text:gsub('has gone offline.', '-')
  16.  
  17.     --channel replace (Trade and custom)
  18.     text = text:gsub('|h%[(%d+)%. .-%]|h', '|h%1.|h')
  19.  
  20.     --url search
  21.     text = text:gsub('([wWhH][wWtT][wWtT][%.pP]%S+[^%p%s])', '|cffffffff|Hurl:%1|h[%1]|h|r')
  22.  
  23.     return self.DefaultAddMessage(self, text, ...)
  24. end

the last line
Lua Code:
  1. return self.DefaultAddMessage(self, text, ...)
is causing the taint. I don't doubt that the DefaultSetItemRef line you mentioned could also be causing taint, but none that I've come across yet. The above section I was able to reproduce 10/10 times and 0/10 times when I commented that one line of code out.

You wouldn't by chance know how I could better implement the AddMessage function to change things without causing taint, would you?
  Reply With Quote
10-07-18, 09:46 PM   #5
Kanegasi
A Molten Giant
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 666
Ah, I missed that. The taint source for that is at the bottom.

Lua Code:
  1. frame.DefaultAddMessage = frame.AddMessage --idk what all of this is
  2. frame.AddMessage = AddMessage --idk what all of this is

As for the things you do in your replaced AddMessage, you already use ChatFrame_AddMessageEventFilter for some events. For the stuff you can't change through the filter return, just build your own message and feed it directly into AddMessage, then return true in the filter so that the original line is filtered. The event filters get all the returns from the chat event, so you can do anything Blizzard's chat output does without replacing AddMessage.

Here's an example of your channel header change added to colorName along with duplicate protection. Keep in mind I did not test this, but this should give you an idea to go on whether it works or not.

Lua Code:
  1. local messages={}
  2. local function colorName(self, event, msg, ...)
  3.  
  4.     local test = msg:gsub('[^a-zA-Z%s]', '')
  5.     local words = {strsplit(' ', test)}
  6.     for i = 1, #words do
  7.         local w = words[i]
  8.         if (w and not (w == 'player' or w == 'target') and UnitName(w) and UnitIsPlayer(w)) then
  9.             local class = select(2, UnitClass(w))
  10.             local colors = RAID_CLASS_COLORS[class]
  11.             if (colors) then
  12.                 msg = gsub(msg, w, '|cff'..RGBPercToHex(colors.r, colors.g, colors.b)..'%1|r')
  13.             end
  14.         end
  15.     end
  16.  
  17.     -- get some of the args from the chat event
  18.     local namerealm,lang,fullchan,name,afkdnd,zoneID,chanID,channame,unusedzero,lineID,playerguid=...
  19.  
  20.     -- get text color and id of event (FrameXML\ChatFrame.lua:3186)
  21.     local type = strsub(event, 10)
  22.     local info = ChatTypeInfo[type]
  23.  
  24.     if event=="CHAT_MSG_CHANNEL" then
  25.  
  26.         -- prevent duplicate messages
  27.         if messages[lineID] then
  28.             return true -- hide, do nothing
  29.         else
  30.             messages[lineID]=true
  31.         end
  32.  
  33.          -- since we have the raw message, we have to add the channel number ourselves
  34.         msg = "|Hchannel:channel:"..chanID.."|h"..chanID..".|h "..msg
  35.  
  36.         -- we also need to add the timestamp exactly how Blizzard does it
  37.         -- FrameXML\ChatFrame.lua:3540 (BetterDate @ FrameXML\UIParent.lua:4736)
  38.         if CHAT_TIMESTAMP_FORMAT then
  39.             msg= BetterDate(CHAT_TIMESTAMP_FORMAT, time())..msg
  40.         end
  41.  
  42.          -- add message as if it was the original
  43.         self:AddMessage(msg,info.r,info.g,info.b,info.id)
  44.  
  45.          -- hide original message
  46.         return true
  47.  
  48.     end
  49.  
  50.     return false, msg, ...
  51. end

Last edited by Kanegasi : 10-07-18 at 09:50 PM.
  Reply With Quote
10-08-18, 07:45 AM   #6
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
Hey man, thanks so much. Your code kinda sorta works. It gives me some ideas on how to springboard, but I have a feeling I'm going to have to seriously revamp that section of the code to get it to work the way I want. I really appreciate your time. The code is now completely functional, I just now know that if I mess with communities I'll get a taint error until I can figure out how to better filter the messages and not lose functionality.

Cheers!
  Reply With Quote
10-08-18, 10:49 AM   #7
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
I was able to perform a hooksecurefunc on the two functions, however it essentially just duplicates chat. Is there a way to remove the previous line of chat?

I know there's some sort of mechanism for this; when you report someone for language their chat lines are removed from your chat log. If I knew what function that was, perhaps I could call it during my AddMessage function and copy the blizzard version, remove the blizzard version, alter the blizzard version, and then put the altered version in the chat window. This should prevent tainting but also allow for the "skinning" of the chat strings.
  Reply With Quote
10-08-18, 11:29 AM   #8
Kanegasi
A Molten Giant
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 666
The method I presented, using filters as one piece printing and filtering, does seem like it's complicated, but using the filters is the best (or only) way to do what you want without replacing Blizzard objects. I also included duplicate prevention because I have had problems with some addons doing weird stuff with chat filters. Prat and WIM do weird things together when you try to add a filter that does more than filter.

An easier way to use the filters is to have a simple filter that blocks all default events for the event you want to manipulate, while using the event that fired directly. You'll have to specify the ChatFrame to go to since that's not self anymore. Also, keep in mind this blanket method clashes with all other chat addons, since they all use the combo filter method I posted originally and a straight "return true" filter will just kill all changes they do. If you don't have any other chat addons and your addon is meant to be used alone, this is fine.

Here's a piece of code that does what I posted earlier, but standalone without your code:

Lua Code:
  1. -- block ALL channel events from default UI
  2. ChatFrame_AddMessageEventFilter( "CHAT_MSG_CHANNEL", function( ) return true end )
  3.  
  4. local f = CreateFrame( "frame" )
  5.  
  6. f:RegisterEvent( "CHAT_MSG_CHANNEL" )
  7.  
  8. f:SetScript( "OnEvent", function( self, event, ... )
  9.  
  10.     local type = strsub( event, 10 )
  11.     local info = ChatTypeInfo[ type ]
  12.  
  13.     arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, _, arg11, arg12 = ...
  14.  
  15.     if event == "CHAT_MSG_CHANNEL" then
  16.  
  17.         arg1 = "|Hchannel:channel:" .. arg8 .. "|h" .. arg8 .. ".|h " .. arg1
  18.  
  19.         if CHAT_TIMESTAMP_FORMAT then
  20.             arg1 = BetterDate( CHAT_TIMESTAMP_FORMAT, time( ) ) .. arg1
  21.         end
  22.  
  23.         ChatFrame1:AddMessage( arg1, info.r, info.g, info.b, info.id )
  24.  
  25.     end
  26.  
  27. end )

I also have a way to automatically choose which ChatFrame to post to based on Blizzard's chat settings using IsEventRegistered, but I wanted to keep this simple.

Last edited by Kanegasi : 10-08-18 at 11:32 AM.
  Reply With Quote
10-08-18, 11:49 AM   #9
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
I really appreciate your efforts. The filtering method makes sense, but unless I messed something up, I noticed it removed the ability to left or right click on player's names who added to the chat. Being able to do that is pretty crucial for me, as I frequently invite or ignore or what have you. For example the [2 - Trade][someone's name]: message, the [someone's name] is a clickable entity with drop down menus.

If you can maintain that ability to click on someone's name who has altered the chat like the default UI allows, great, and I can mess around with it more, but if not, it's not a viable strategy for me, irrespective of no tainting.
  Reply With Quote
10-08-18, 01:50 PM   #10
Kanegasi
A Molten Giant
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 666
Oh, I missed that part, sorry. What I did to "build" the channel copy was just follow Blizzard's ChatFrame_MessageEventHandler function, which processes every chat event shown in the chat frames. You can see it all here: https://www.townlong-yak.com/framexm...Frame.lua#3188

The channel header and timestamp part I pulled starts on line 3544 but the event adds the playerlink starting at line 3477.

You'll probably end up just rebuilding the entire ChatFrame_MessageEventHandler function at this point to handle everything while relying on the filters to hide all the original messages. ChatFrame_MessageEventHandler builds the message (or body as it labels it halfway) and uses AddMessage as well.
  Reply With Quote
10-08-18, 02:30 PM   #11
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
God amongst men. I really appreciate it. What started off as repainting the car is going to be taking the engine block out and replacing 90% of it. Thank you again!
  Reply With Quote
02-01-19, 12:38 PM   #12
Terenna
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 105
Update to this project. I have written the entire addon utilizing the filter method for every chat event. Everything works well, and I've optimized it, commented, cleaned it up as best as possible. I am running into a small issue. Addons and some blizzard system events utilize the AddMessage function. They add their messsages into the chatframe without passing a CHAT_MSG_xyz event, thereby rendering my addon unable to add a timeStamp in front of the text.

As far as I have read, the only way to add a timeStamp in front of ALL messages is to override the AddMessage function and replace it with an AddMessage function that adds the timeStamp. This, however, causes taint, the entire reason I spent all this time writing a new chat addon.

Is there another way one can add timeStamps to lines added to chat by AddMessage calls that addons and blizzard sometimes uses (think GMOTD, Loot Specialization: xyz messages when you log in)? I have tried hooking the AddMessage function of ChatMessage1, but that just results in a copy. If there was a way to remove the previous line of Chat if it did not start with a timeStamp, that would be ideal, but I haven't found a way the API could support that.

Thank you.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Temporary Chat Frames/Community Chat Frame/New Chat Window Issue

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