Forum

Page 1 of 5 123 ... LastLast
Results 1 to 15 of 72
  1. #1
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145

    ts3Functions.getClientList()... How do I use that?

    I want to use
    Code:
    ts3functions.getClientList()
    and
    Code:
    ts3functions.getChannelList()
    in my plugin.
    Now I have a problem:
    I am using C++/CLI for my plugin, since it can also use .NET DLLs.
    I want to assign the return of getClientList to a variable of the type that it wants:

    Code:
    anyID clientList;
    anyID* clientListPtr = &clientList;
    
    uint64 channelList;
    uint64* channelListPtr = &channelList;
    
    uint64 schid = ts3Functions.getCurrentServerConnectionHandlerID();
    
    ts3Functions.getClientList(schid, &clientListPtr); // Double-pointer, since the "result" argument is tagged with **
    ts3Functions.getChannelList(schid, &channelListPtr); // Double-pointer, since the "result" argument is tagged with **
    
    char* resultChannelVar;
    ts3Functions.getChannelVariableAsString(schid, channelList[0], /* Read Below */, &resultChannelVar);
    Now my question:
    Where I wrote /* Read Below */, how do I know which flag has which ID?
    (And I suppose the Pointer-To-A-Pointer way is right for **result?)
    And the channelList[0] throws me an Error:
    Code:
    Der Ausdruck muss ber einen "pointer-to-object"- oder "handle-to-C++/CLI-array"-Typ verfgen
    Code:
    The expression must contain a "pointer-to-object"- or "handle-to-C++/CLI-array"-type
    Any idea why? I don't really manage to solve that Error...

  2. #2
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    The flags are defined in the ChannelProperties (in public_definitions.h) and ChannelPropertiesRare (in public_rare_definitions.h) enums.

    getClientList (resp. getChannelList and others) receives a pointer to an array of anyID/uint64. The clientlib will allocate memory for that array and fill it. After the call and if it returns no error (result is ERROR_ok), your variable will point to a null-terminated array.

    Code:
    anyID* channellist;
    unsigned int res = ts3Functions.getChannelList(schid, &channellist);
    if (res == ERROR_ok) {
      for (int i = 0; channellist[i]; ++i) {
        //do whatever you like with the channel id channellist[i]
      }
    
      ts3Functions.freeMemory(channellist);
    }
    else {
      //oops, something went wrong (res == errorcode)
    }
    Notice the freeMemory call. Because the memory is allocated in the TS3 client's memory segment (and not your plugin's), it must be freed by the client.

  3. #3
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    So basically I create a non-initialized pointer, the clientlib allocates memory, and changes the pointer to that position?

  4. #4
    Join Date
    February 2017
    Posts
    45
    yeah that's correct. here is an example: https://git.rwth-aachen.de/carstenf/...oller.cpp#L222

    btw i just noticed that i never free the memory. oops

  5. #5
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    What does your plugin do btw?
    Looks to me like a Re-write of the TS3 interface...
    (Suggestion: if it is, add an option so the user can design his own interface via an HTML/CSS API... if you're good enough to do that, 'cos I'm not^^)

    And another thing I just realized:
    Like, if I want to get the channelname for example, I have to use getChannelVariableAsString() and if I want to get the max. Client count I use getChannelVariableAsInt() right?

  6. #6
    Join Date
    September 2012
    Posts
    6,080
    Yes.

    get*AsString for things that are strings
    get*AsUInt64 for things that are unsigned integers or can exceed 32bit range (Connection Handler Ids, Channel Ids, client Ids and the like)
    get*AsInt for things that are signed integers (permissions for example)
    When sending PMs please make sure to include a reference link to the thread in question in the body of your message.

  7. #7
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    Got it, thanks!

    But since I am using a pointer for the returns, I cant use a for loop sine I don't know the length of the array...
    The old sizeof(myList)/sizeof(uint64) isn't gonna work here...
    Is there a function to get the count of items in a return from getChannelList, or should I just use a while-loop which breaks on index error?

  8. #8
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    Quote Originally Posted by Thomas View Post
    getClientList (resp. getChannelList and others) receives a pointer to an array of anyID/uint64. The clientlib will allocate memory for that array and fill it. After the call and if it returns no error (result is ERROR_ok), your variable will point to a null-terminated array.

    Code:
    anyID* channellist;
    unsigned int res = ts3Functions.getChannelList(schid, &channellist);
    if (res == ERROR_ok) {
      for (int i = 0; channellist[i]; ++i) {
        //do whatever you like with the channel id channellist[i]
      }
    
      ts3Functions.freeMemory(channellist);
    }
    else {
      //oops, something went wrong (res == errorcode)
    }
    Notice the freeMemory call. Because the memory is allocated in the TS3 client's memory segment (and not your plugin's), it must be freed by the client.
    The ids in the TS3 ecosystem start with 1 (except that channel id = 0 is a special case eg. for filetransfers, but it won't be contained in the result of getChannelList). So as long as the arrays are null-terminated, you can safely iterate with for-loops.

  9. #9
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    Alright got it.
    Now another problem occured:

    How do I create hotkeys?
    Like, I've found this:
    Code:
    static struct PluginHotkey* createHotkey(const char* keyword, const char* description) {
    	struct PluginHotkey* hotkey = (struct PluginHotkey*)malloc(sizeof(struct PluginHotkey));
    	_strcpy(hotkey->keyword, PLUGIN_HOTKEY_BUFSZ, keyword);
    	_strcpy(hotkey->description, PLUGIN_HOTKEY_BUFSZ, description);
    	return hotkey;
    }
    
    void ts3plugin_initHotkeys(struct PluginHotkey*** hotkeys) {
    	/* Register hotkeys giving a keyword and a description.
    	 * The keyword will be later passed to ts3plugin_onHotkeyEvent to identify which hotkey was triggered.
    	 * The description is shown in the clients hotkey dialog. */
    	BEGIN_CREATE_HOTKEYS(3);  /* Create 3 hotkeys. Size must be correct for allocating memory. */
    	CREATE_HOTKEY("keyword_1", "Test hotkey 1");
    	CREATE_HOTKEY("keyword_2", "Test hotkey 2");
    	CREATE_HOTKEY("keyword_3", "Test hotkey 3");
    	END_CREATE_HOTKEYS;
    
    	/* The client will call ts3plugin_freeMemory to release all allocated memory */
    }
    
    void ts3plugin_onHotkeyEvent(const char* keyword) {
    	/* Identify the hotkey by keyword ("keyword_1", "keyword_2" or "keyword_3" in this example) and handle here... */
    }
    and this:
    Code:
    ts3Functions.requestHotKeyInputDialog(const char *pluginID, const char *keyword, int isDown, void *qParentWindow)
    Now, what does a pluginID look like? Since it is initially declared as NULL, how do I set one? Is there any rule?
    Also, what is meant by const char *keyword ?
    What does isDown mean? (I suppose this is a int-bool?)
    And what do I have to do with qParentWindow?

    Could anyone help me with creating a hotkey via input that calls a custom callback in my plugin?

  10. #10
    Join Date
    September 2012
    Posts
    6,080
    Well, it's all pretty decently explained in the code comments of the example but here you go...

    You initially tell the client which hotkey actions are offered by your plugin in the initHotkeys callback that is called by the client after the plugin is loaded.
    The client will then offer those hotkeys to users in the Advanced Hotkeys section of the Add Hotkey dialog in Options > Hotkeys > Add.

    Whenever one of the hotkey actions you offer is configured by the user and the corresponding hotkey pressed, the client will call the ts3plugin_onHotkeyEvent in your plugin so you can do whatever it is you want to do when the hotkey was pressed.
    Since you can obviously offer more than one hotkey action, but only have one callback that is called, you will need to know which hotkey action was triggered. That's what the keyword parameter is for. It will contain the internal name of the hotkey action that the hotkey pressed was bound to. You specified an internal name (identifier) as well as a UI description for your hotkey actions when you created them in the initHotkeys callback. The keyword parameter of the onHotkeyEvent callback will contain one of those internal names, so that you can distinguish them and react accordingly.

    The requestHotkeyInputDialog function call is so that you can trigger actually configuring a hotkey for one of your hotkey actions. The user can at any time configure your hotkey actions as a hotkey in the Tools > Options > Hotkeys dialog, but a plugin author may have their own UI and it would be convenient to be able to set hotkeys from right there, rather than having to tell the user to head to the client options to do that. For example one of the soundboard plugins offers to bind a hotkey to play a sound. It would be inconvenient (and probably confusing) to send the user to the options and figure out which sound is which hotkey action, so you can assign a hotkey from within the plugin UI where you can see which sound you're setting a hotkey up for.
    So in any case, if you call that function the client will show the "Press Hotkey" window, and after a key was pressed will create the hotkey binding automatically with the action specified in the keyword parameter. The isDown is a boolean and decides whether the hotkey will be created "on key press" or "on key release" (remember the Add Hotkey dialog in Options > Hotkeys > Add dialog).
    The qParentWindow parameter is a parameter where you can optionally specify the dialog that is to be the parent of the gray hotkey input dialog the client displays. Usually you'll just pass nullptr there.

    The pluginId is set in the ts3plugin_registerPluginID callback, which too is called upon plugin load by the client. The default implementation in the example just copies the id given by the client to the static variable pluginID. You do need to use whatever ID is given to you by the client in said callback and must not make up your own.
    When sending PMs please make sure to include a reference link to the thread in question in the body of your message.

  11. #11
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    Name:  Screenshot_69.png
Views: 409
Size:  3.5 KB

    So, I added the hotkey callbacks and all, now only... I can't find where to actually trigger the hotkey...
    I tryed it with changing the <command> to my keyword, still would not trigger...
    I will add Loggings now for further information if it actually is the hotkey or my code.

    I added a ts3plugin_initHotkeys([...]) that is copied and changed from the example plugin...
    ts3plugin_registerPluginID([...]), ts3plugin_onHotkeyEvent([...]) are also there...

    Do I actually HAVE to call the requestHotkeyInputDialog? Or is the whole function optional and initially for external UIs?
    Also, is there a function to mute yourself? so like, toggle microphone status?
    Last edited by Jhhhj_original; March 9th, 2017 at 07:50 PM.

  12. #12
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    Quote Originally Posted by Jhhhj_original View Post
    I tryed it with changing the <command> to my keyword, still would not trigger...
    That's not neccessary. Hotkeys depend only on initHotkeys and registerPluginId (and ts3plugin_freeMemory) and will trigger onHotkeyEvent.

    Quote Originally Posted by Jhhhj_original View Post
    Do I actually HAVE to call the requestHotkeyInputDialog? Or is the whole function optional and initially for external UIs?
    Yes, it's optional. If everything went well, you should see your plugin hotkeys in the settings dialog (Plugins->Plugin Hotkey-><your plugin name>-><keyword1>, ... So that your user can set the keybindings manually.

  13. #13
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    Alright...
    Then I have no idea why it is not working...
    Code:
    static char* pluginID = NULL;
    
    #define BEGIN_CREATE_HOTKEYS(x) const size_t sz = x + 1; size_t n = 0; *hotkeys = (struct PluginHotkey**)malloc(sizeof(struct PluginHotkey*) * sz);
    #define CREATE_HOTKEY(a, b) (*hotkeys)[n++] = createHotkey(a, b);
    #define END_CREATE_HOTKEYS (*hotkeys)[n++] = NULL; assert(n == sz);
    
    static struct PluginHotkey* createHotkey(const char* keyword, const char* description) {
    	struct PluginHotkey* hotkey = (struct PluginHotkey*)malloc(sizeof(struct PluginHotkey));
    	_strcpy(hotkey->keyword, PLUGIN_HOTKEY_BUFSZ, keyword);
    	_strcpy(hotkey->description, PLUGIN_HOTKEY_BUFSZ, description);
    	return hotkey;
    }
    
    void ts3plugin_registerPluginID(const char* id) {
    	const size_t sz = strlen(id) + 1;
    	pluginID = (char*)malloc(sz * sizeof(char));
    	_strcpy(pluginID, sz, id);
    }
    
    void ts3plugin_initHotkeys(struct PluginHotkey*** hotkeys) {
    	BEGIN_CREATE_HOTKEYS(2);
    	CREATE_HOTKEY("Start", "Initialize SR");
    	CREATE_HOTKEY("Stop", "End SR");
    	END_CREATE_HOTKEYS;
    }
    
    ref class tsapi
    {
            void init_process() {
                    // Start function-chaining here
            }
            // More (unrelated )functions here
    }
    // This part is underneath the class, else my compiler will throw an error that it does
    // not find the class.
    
    void ts3plugin_onHotkeyEvent(const char* keyword) {
    	if (strcmp(keyword, "Start") == 1) {
    		tsapi^ a = gcnew tsapi();
    		ts3Functions.logMessage("Got Start call for SR!", LogLevel_INFO, "vc", 1);
    		a->init_process();
    	}
            // Stop not implemented yet
    }
    And my Header file:
    Code:
    // Part above
    exp void ts3plugin_registerPluginID(const char* id);
    exp void ts3plugin_initHotkeys(struct PluginHotkey*** hotkeys);
    exp void ts3plugin_onHotkeyEvent(const char* keyword);
    // Not really anything below, just the closing
    ANY idea?
    I just don't get it...

  14. #14
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    145
    After trying around a bit, I found that the ts3plugin_initHotkeys() does not get called, even though I declare and export it. Somehow the Client does not call that function.
    ts3plugin_registerPluginID() is being called just fine, it logs what I want.
    The initHotkeys() does not even get called, since it does not log my message.

  15. #15
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    Did you export ts3plugin_freeMemory? That's needed to free the memory allocated in initHotkeys (and other).

    So in absence of freeMemory, the client might not call the functions which depends on it.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. ts3Functions.requestChannelGroupClientList
    By Bluscream in forum Suggestions and Feedback
    Replies: 1
    Last Post: October 26th, 2016, 06:44 AM
  2. Problem with ts3Functions.getClientID()
    By Dione in forum Client Plugins / Lua Scripts
    Replies: 2
    Last Post: August 30th, 2014, 11:40 AM
  3. Replies: 1
    Last Post: March 14th, 2013, 10:06 AM
  4. ts3Functions.createIdentity - where the heck is it?
    By sea212 in forum Client Plugins / Lua Scripts
    Replies: 7
    Last Post: March 1st, 2010, 07:14 PM
  5. [Solved] ts3Functions.requestClientMove
    By tomix in forum Client Plugins / Lua Scripts
    Replies: 2
    Last Post: February 22nd, 2010, 10:31 AM

Posting Permissions

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