Page 1 of 3
1
2
3
LastLast
  1. #1

    Create channel for addon and display messages on table

    I am starting in the addon world and feeling already quite lost, so I apologize in advance if my questions sound super noob

    I'd like to have your opinion on how to approach the developing of my addon, since maybe I'm making things too complicated. This is it:

    I want to have a table displaying different cells containing text. This table (the different text fields) would be editable by the player but also, and at the same time, by other players in a particular group. That is, each of these players would see a table and could edit it and see the changes the others are doing in real time.

    To accomplish this, I want to create a frame with several edit boxes and send the text changes through a private chat channel, so it can be updated in each player's interface. What do you think of this approach? I'm having some problems with the development, so I thought it was better to ask about the general approach first, before asking for help with these problems.

    Thank you!

  2. #2
    You may have a hard time logistically trying to figure out how you want to handle multiple users editing the same text box at the same time, although that's up to you to figure out I suppose.

    You will probably want to use API SendAddonMessage and API RegisterAddonMessagePrefix rather than using an actual "chat channel". Everything else sounds like it should be doable, good luck!

    Feral Druid Maintainer of SimC // Moderator at Fluid Druid forums
    Pawkets' Armory
    // Pawkets' Pro Raiders // <Forgotten Aspects>

  3. #3
    "Particular group" as in "your raid group" or as in "an arbitrary group of players". If the first, you can SendAddonMessage to specific channels, too. If the last, a invisible channel seems like the best approach, yes.

    Also, you'd have to deal with possible desync issues (where addons don't agree whose edit came first). Consider having the addons elect a "leader" among themselves that has the final say (addon tells leader "I edited this", leader sends out "this was edited" message to everyone else). Can be as simple as "the first addon that joins the channel while no leader is present". You'd also need to handle transitions where the leader logs off, though. I never said it'd be easy.
    UI & AddOns expert | Interface & Macros moderator - My work

  4. #4
    Thank you both, that was very useful! I was looking at wowwiki and the information there was quite short and not very specific in some cases.

    My goal is that the player can use it with any group he/she joins, so it shouldn't be hardcoded to a particular channel. But I understand that I can use SendAddonMessage and it "wouldn't matter" if I'm using the, for instance, Guild channel, as the players wouldn't see the messages (my main concern was to make the game experience worse by having too many messages displayed or bothering the player in some way). As for the security of the channel, I also understand it would be the same as with a private channel with no password: only the ones that know the name of the channel can join.

    Having more than one player editing the same cell at the same time is something I really have to consider, yes!

  5. #5
    SendAddonMessage for supported channels is essentially a chat message that players cannot see (and that fires CHAT_MSG_ADDON instead of the related CHAT_MSG_X event). You also don't have to worry about message scope.

    If you use "actual" chat channels (SendAddonMessage doesn't work for those), you'd need to resort to SendChatMessage and using either ChatFrame_AddMessageEventHandler or the chat settings to hide all messages in that channel from being viewed by the user.

    PS: http://wowprogramming.com/docs/
    UI & AddOns expert | Interface & Macros moderator - My work

  6. #6
    Perfect, I will try with SendAddonMessage then and see how it goes.

    As for the UI part, I was considering using LibSpreadsheet (can't post the link yet) but, before that, I tried to create 10 editbox instances using a template in XML and a loop in Lua. Which option would you recommend? I'm having troubles learning how to import the library and am afraid when the API updates something may broke down. As for the code, I'm not getting anything to show on the window but no errors either. Before posting snippets, I'd like to try to debug again on my own, but I only know that I should check FrameXML.log, which seems correct, and I validated the XML file. How do you do your debugging?

    Thank you very much!!

  7. #7
    FrameXML.log is actually quite deep into debugging already. It's what you should check if your file doesn't load at all.

    Otherwise, make sure Lua error display is enabled. Consider getting a better bug display (such as !BugGrabber in combination with BugSack), too.


    PS: Honestly, there's no need to use XML ever. I certainly haven't used it in a very long time. It's a matter of preference, of course, but you won't hear anything good about XML from me.
    Look into CreateFrame.
    UI & AddOns expert | Interface & Macros moderator - My work

  8. #8
    Thanks for the tip! Now I can see one of the errors I have. Anyway, I'll rewrite this trying to use less XML, as this way is not that clear to me.

    As for the communication part, I'm working on the design and I think I'll have to combine some of the ideas you suggested. Again, thank you very much!

  9. #9
    So I have an editable table now, for the UI part. As for the communication, I'm still working on the design, thinking about the different possible situations.

    I'd like to have access to the the guild/raid leader info (just the name), as that player could have the master copy of the data and messages to him/her could be sent as whispers to get the current state of the data (that's why I need the name). However, GuildInfo() does not return it and I can only see functions to set leader, not to get it.

    One of the reasons to choose the leader for this role is that I'm not sure of how to store a "global" variable, that is, how could every player connected (in the raid, party or guild) have access to a variable like master == XXXX? It seems like assuming that master is also the leader of your group makes things easier.

  10. #10
    I'm no good programmer, so there might be other more efficient ways, but as for finding the guild/raid leaders you can make a loop over all the members using GetGuildRosterInfo() and GetRaidRosterInfo()

  11. #11
    Quote Originally Posted by terpilan View Post
    One of the reasons to choose the leader for this role is that I'm not sure of how to store a "global" variable, that is, how could every player connected (in the raid, party or guild) have access to a variable like master == XXXX? It seems like assuming that master is also the leader of your group makes things easier.
    There's no way to do such a thing exactly as you described, but you could setup communications that would have it work effectively the same (master always broadcasts his value when it changes and slaves automatically update their own values).

    And Sakpoth is correct I believe, you'll have to run a loop of those functions to find the raid/guild leader and as far as I'm aware that's the best method. Obviously you want to have event triggers to determine when the code should be re-ran so it doesn't have to recheck the leader an excessive amount of times.
    Last edited by aggixx; 2013-04-26 at 02:40 AM.

    Feral Druid Maintainer of SimC // Moderator at Fluid Druid forums
    Pawkets' Armory
    // Pawkets' Pro Raiders // <Forgotten Aspects>

  12. #12
    You don't want to use raid/guid leader, to be honest. Both of these might be offline, killing the entire communications system.

    Consider a model like this:
    Code:
    1. When starting/connecting/whatever, the addon broadcasts "is there a master?", then waits a defined timeout for any responses.
    2. If the addon that is currently master sees this, it responds. If any addon that is not currently master sees this, it responds too.
    3a. If a response from a master addon is received, communicate with the master addon to get up-to-date info.
    3b1. If a response from a non-master addon is received, but no response from a master addon is received, the addon broadcasts "we need to pick a new master".
    3b2. The addons that already have data now wait a random amount of time, then broadcast "I am now master". All other addons seeing this broadcast communicate with the new master to synchronize data.
    3c. If no response is received, the addon assumes that it is the only one currently active, broadcasts "I am now master" and starts with fresh data.

    Now, anytime a field is changed, the addon communicates its change to the master addon. If no response from the master is received after a defined number of retries, the addon once again broadcasts "We need to pick a new master".

    Notes:
    1. Data sent while master status is transitioning may be lost. Indicate that a master is currently being chosen to the user (somehow - potentially a message "<name> has disconnected. Please wait...") and disable inputs until a new master is established.
    2. This model will be quite tricky to implement. (Well, I never said it would be easy, did I? You picked your poison.)
    3. You need some way to handle race conditions - addons may disagree over which one shouted "I am now master" first due to delays. It is also theoretically possible that the random delay used in 3b2 above comes up equal for two addons. Random numbers may help.
    UI & AddOns expert | Interface & Macros moderator - My work

  13. #13
    Thanks everyone!

    I think I will stick to a simpler protocol, as Treeston's suggestion seems too complex for what I aim for. Picking up ideas from your posts:

    The master will be the one that runs the addon first (need to update if he/she disconnects, I will follow aggixx' advice to implement that). The master will use the loop to send an invitation via whisper to all the members of the group to the channel, that is, a message containing the prefix that will be used for SendAddonMessage. This way who joins the discussion is controlled. After that, as you already said, the master broadcasts the updates previously received via whisper from one of the slaves.

    To know who connected first, I was thinking of using timestamps. It seems a bit tricky though, because due to time difference between countries, etc., using functions such as GetTime() or time() doesn't seem a reliable method. Also, I'm not sure about how critical race conditions would be in this case.

    I have to stop working on this project for a week, will keep thinking about it after that.

  14. #14
    Check out LibSyncTime.

    PS: SendAddonMessage doesn't work for actual chat channels. Assuming you used 'channel' to mean 'group of players'.
    UI & AddOns expert | Interface & Macros moderator - My work

  15. #15
    Quote Originally Posted by Treeston View Post
    Check out LibSyncTime.
    Looks good, thank you!

    Quote Originally Posted by Treeston View Post
    PS: SendAddonMessage doesn't work for actual chat channels. Assuming you used 'channel' to mean 'group of players'.
    Right, I meant the group of players that join the "conversation", that is, the clients that know the prefix and will receive data

  16. #16
    So back to this again! I'm perfecting the communication protocol, as some parts didn't fit as well as I thought. To use timestamps, I wanted to save them as each player starts the game and then share them via chat. I used OnLoad to catch the timestamp but apparently that only works with XML and I am using just Lua following your suggestion.

    So if I want to use Lua, how do I tell WoW that I want a particular section of code to be executed when the addons are loaded? I know you can catch events for that but in order to register for these events, you have to create a frame and where would the code for that creation go? Right now I have it as a slash command but that, of course, would come later :S

    As for the communication part, I think I'm missing something. I have registered a prefix

    Code:
    local success = RegisterAddonMessagePrefix(addon_prefix)
    	if (success) then
    		print ("Prefix registered")
    		ColLabWoW:RegisterEvent("CHAT_MSG_ADDON")
    		SendAddonMessage(addon_prefix, "Test Messageee")
    
    	else
    		print ("Error: the prefix registration failed")
    	end
    But the following prints don't produce anything:

    Code:
    function ColLabWoW_OnEvent(frame, event, ...)
    	if (event == "CHAT_MSG_ADDON") then
    		print "Event caught"
    		-- Arguments: "prefix", "message", "channel", "sender"
    		local prefix_received = select(1, ...)
    		local message = select(2, ...)
    		local channel = select(3, ...)
    		local sender = select(4, ...)
    		if (prefix_received == addon_prefix) then 
    			if (message == "time%d+") then
    				print ("Timestamp received")
    			elseif (message == "Test Messageee") then
    				print ("Test message received")
    			end
    		end
      end
    
    end
    Thanks as usual!
    Last edited by terpilan; 2013-05-08 at 11:07 PM.

  17. #17
    Every Lua file is executed as it is parsed. Function definitions, CreateFrame calls, etc, are all part of that. Fetching the current time can be, too. In other words, you can simply drop it in the Lua file's body.

    Looking at your code:
    1) Do you :SetScript your OnEvent anywhere? You don't in the code you showed.
    2) I'm not sure what SendAddonMessage would send the message to if called with 2 arguments. For testing, use SendAddonMessage(addon_prefix, "Test messageee", "WHISPER", (UnitName("player"))).
    3) if (message == "time%d+") then - equals does not do pattern matching. If you simply want to check for presence of a timestamp, use if message:find("time%d+") then. However, I'd recommend:
    Code:
    local time = tonumber(message:match("time(%d+)"))
    if time then
        print("Timestamp received")
    else   -- etc
    4) Also, small nitpick:
    Code:
    local prefix_received, message, channel, sender = ...
    Saves you a bunch of lines and the overhead from calling select(). If you use all the arguments anyways, select() is unnecessary.
    UI & AddOns expert | Interface & Macros moderator - My work

  18. #18
    Quote Originally Posted by Treeston View Post
    1) Do you :SetScript your OnEvent anywhere? You don't in the code you showed.
    Yes, here is how I create the main frame:
    Code:
    ColLabWoW = CreateFrame("Frame", "ColLabWoW", UIParent)
    ColLabWoW:SetWidth(384)
    ColLabWoW:SetHeight(512)
    ColLabWoW:SetPoint("CENTER", UIParent, "CENTER")
    ColLabWoW:SetBackdrop({
      bgFile = "Interface\\CHATFRAME\\CHATFRAMEBACKGROUND",
      edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
      tile = true, tileSize = 16, edgeSize = 16,
      insets = { left = 3, right = 3, top = 5, bottom = 3 }
    });
    -- Positionate window to the left, make it movable always and visible when the character is dead
    UIPanelWindows["ColLabWoW"] = {
      area = "left",
      pushable = 1,
      whileDead = 1,
    }
    ColLabWoW:SetScript("OnEvent", ColLabWoW_OnEvent)
    ColLabWoW:SetScript("OnUpdate", ColLabWoW_Update)
    ColLabWoW:Show()
    The result is a window with the selected edge but white background that has no interaction and it is in the center, not in the left. These are the first lines of my code, plus the call to the library to get the time.

    Quote Originally Posted by Treeston View Post
    2) I'm not sure what SendAddonMessage would send the message to if called with 2 arguments. For testing, use SendAddonMessage(addon_prefix, "Test messageee", "WHISPER", (UnitName("player"))).
    I've tried that and get the same result as before I have also used eventtrace to see if the event was generated if I write the command /script SendAddonMessage... having previously registered the prefix the same way. No events where fired.

    I've changed the rest according to your suggestions, thank you very much!

  19. #19
    You :SetScript before defining the function. By the time :SetScript is called, ColLabWoW_OnEvent will actually still be nil.
    UI & AddOns expert | Interface & Macros moderator - My work

  20. #20
    I see, thanks I have put the scripts code first and then my code but still nothing happens.

    Code:
    ColLabWoW = CreateFrame("Frame", "ColLabWoW", UIParent)
    ColLabWoW:SetWidth(384)
    ColLabWoW:SetHeight(512)
    ColLabWoW:SetPoint("CENTER", UIParent, "CENTER")
    ColLabWoW:SetBackdrop({
      bgFile = "Interface\\CHATFRAME\\CHATFRAMEBACKGROUND",
      edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
      tile = true, tileSize = 16, edgeSize = 16,
      insets = { left = 3, right = 3, top = 5, bottom = 3 }
    });
    -- Positionate window to the left, make it movable always and visible when the character is dead
    UIPanelWindows["ColLabWoW"] = {
      area = "left",
      pushable = 1,
      whileDead = 1,
    }
    ColLabWoW:SetScript("OnEvent", ColLabWoW_OnEvent)
    ColLabWoW:SetScript("OnUpdate", ColLabWoW_Update)
    ColLabWoW:Show()
    
    
     -- Get group name
      local groupName, grouptype = "MyGuild", "Guild" -- TODO getGroupInfo()
      addon_prefix = GenerateChannelName(groupName, grouptype)
      print (addon_prefix .. " is the generated prefix")
      
      local success = RegisterAddonMessagePrefix(addon_prefix)
      if (success) then
        print ("Prefix registered")
        ColLabWoW:RegisterEvent("CHAT_MSG_ADDON")
        SendAddonMessage(addon_prefix, "Test Message","WHISPER", (UnitName("player")))
        --SendAddonMessage(addon_prefix, "time: " .. tostring(curtime))
    
      else
        print ("Error: the prefix registration failed")
      end
    The last message on the chat window is "Prefix registered". My On_Event now looks like this:

    Code:
    -- Run whenever an event fires for which the frame is registered (wowprogramming.com)
    function ColLabWoW_OnEvent(frame, event, ...)
      print "Event caught"
    	if (event == "CHAT_MSG_ADDON") then
    		-- Arguments: "prefix", "message", "channel", "sender"
      local prefix_received, message, channel, sender = ...
    		if (prefix_received == addon_prefix) then 
          local time = tonumber(message:match("time: (%d+)"))
          if time then
            print("Timestamp received: " + curtime)
    			elseif (message == "Test Message") then
    				print ("Test message received")
    			end
          end
      end
    
    end
    So I'm probably missing something else. I will work more on this later and edit this post whenever I solve it. Thanks for everything!

    EDIT: When I tried to generate several Editboxes, I couldn't close the game by pressing Esc, etc. I have commented it till I go back to work on the UI
    Last edited by terpilan; 2013-05-09 at 12:32 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •