Forum

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

    Creating a TS3 Client integrated Plugin in C# .NET?

    Hello, as you can see from the title, I want to create a plugin in TS3 that is integrated into the client itself.
    I'd like to make that plugin in C#, since all the libraries I need are for .NET.

    Is there a way / API / SDK for doing just that?

    I basically just want to perform actions like move, kick, ban, switch, basic user stuff basically.

    Thank you.

    EDIT:
    Clarification: Its important for it to be C# .NET, and it can't be ServerQuery API. It has to be Client-integrated.
    Last edited by Jhhhj_original; February 9th, 2017 at 07:42 PM. Reason: Clarification

  2. #2
    Join Date
    September 2012
    Posts
    6,078
    The plugin SDK should be usable in most languages. You'd have to create your own C# wrapper around it or search for existing ones though.
    When sending PMs please make sure to include a reference link to the thread in question in the body of your message.

  3. #3
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    142
    Well, there is an existing C# wrapper in the Downloads section...
    But is it compatible with TS 3.1?
    As far as I know it is only for versions < 3.1...

  4. #4
    Join Date
    September 2012
    Posts
    6,078
    Quote Originally Posted by Jhhhj_original View Post
    Well, there is an existing C# wrapper in the Downloads section...
    The only C# wrapper that is there is the full SDK. That's used when you want to integrate TeamSpeak voice technology into your own application and different from when you want to extend the client / write plugins for the client. The latter requires the plugin SDK.
    When sending PMs please make sure to include a reference link to the thread in question in the body of your message.

  5. #5
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    142
    So basically I need the "Client Customisation" SDK with API version 21 written in C, and create a wrapper to use it in C#.
    The other thing is, I only found Header files in there. Where are the actual sourcecode files for the API Version 21 which I need to compile it to a DLL?

  6. #6
    Join Date
    September 2012
    Posts
    6,078
    Quote Originally Posted by Jhhhj_original View Post
    So basically I need the "Client Customisation" SDK with API version 21 written in C, and create a wrapper to use it in C#.
    Yes exactly. Unless of course someone else already did that, and you can use their wrapper.

    Quote Originally Posted by Jhhhj_original View Post
    The other thing is, I only found Header files in there. Where are the actual sourcecode files for the API Version 21 which I need to compile it to a DLL?
    The API is header only, however there is an example source that you can take a look at.

    Basically you add some special functions to your DLL that are exported and called by the client to notify you of certain events.
    Then you have the functions that you can call to make stuff happen, or get information. Those are in the TS3Functions struct.

    plugin.h defines all the callbacks that you can export from your DLL to get called by the client, you will have to implement those you need yourself.
    ts3_functions.h defines all the functions you can call and you get the struct filled in the ts3plugin_setFunctionPointers callback.
    The other headers just define some variables and enums.
    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
    142
    1) As I understand that, I take the functions from the existing header files, write them to my own new header file, compile it to a DLL and create a C# wrapper for that DLL.
    Did I get that right?

    2) So I call ts3plugin_setFunctionPointer to my custom functions / DLLs at 1)?

    3) The Functions work like a override function in C# right?
    Basically, define them and use "base.OverwrittenFunction()" in there? (Not exactly, but sortof like that?)
    Last edited by Jhhhj_original; February 12th, 2017 at 05:49 PM.

  8. #8
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    No, C# expert here, but I can try to describe the TS3 plugin system.

    Every plugin is a shared library (DLL on windows). These plugins export functions, which are then recognized by the TS3 client (identified by the function name, so you need to force standard C name mangling, don't know how to achieve that in C#).

    All exported functions (from now on called callbacks in this post) start with the prefix "ts3plugin_". The returntype and parameters depend on the specific callback (see plugin.h/plugin.c in the testplugin for all available callbacks).

    In my opinion, there are 3 categories of callbacks:
    1. The callbacks, "describing" your plugin like ts3plugin_name, ts3plugin_description and such. The client uses them to retrieve and then show information about your plugin to the user (see Settings->Addons in your TS3 client). These callbacks are required, so your plugin must export them to successfully load.
    2. Event callbacks like ts3plugin_onTextMessageEvent which are called whenever the specific event occurs (in this case, when someone wrote a textmessage to you). So if you want to react to some event, your library needs to export this function. These callbacks are optional, export as much as you need.
    3. Special callbacks like ts3plugin_init and ts3plugin_shutdown (self explaining I think) or ts3plugin_registerPluginID, ts3plugin_initMenus, ts3plugin_initHotkeys (we'll skip them for now) and ts3plugin_setFunctionPointers.


    ts3plugin_setFunctionPointers is called once before your plugin is initialized. You'll get a struct with all available functions (wrapped as function pointer), you can call in the TS3 clientlib. So for example, if you want to send a textmessage to another client, you would call requestSendPrivateTextMsg of the given struct. See include/ts3_functions.h for all available functions.

    So if you write a wrapper for a different language (C# in your case), you have to make sure, that all types exchanged between the client and your plugin are standard C types (like char* for strings) or at least, the client can handle them like standard C types (eg. sizeof(int) in the client has to be of the same length as sizeof(your_C#_int_type)).

    See the testplugin for further references and descriptions about the events. The basics are well documented in it.
    Last edited by Thomas; February 12th, 2017 at 07:56 PM.

  9. #9
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    142
    That actually helps a lot, thank you.

    I think I will try writing my plugin in C... Seems much easier, since I am far from a professional programmer.

  10. #10
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    142
    For understanding:

    unsigned int (*banclient)(uint64 serverConnectionHandlerID, anyID clientID, uint64 timeInSeconds, const char* banReason, const char* returnCode);

    means I call the function with ts3plugin_banclient(serverConnectionHandlerID, anyID, timeInSeconds)

    Now, I get the serverConnectionHandlerID with ts3plugin_getCurrentServerConnectionHandlerID(); right?
    And what is anyID supposed to mean?
    Or do I just call them with getCurrentServerConnectionHandlerID() and banclient(...)?

  11. #11
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    Quote Originally Posted by Jhhhj_original View Post
    means I call the function with ts3plugin_banclient(serverConnectionHandlerID, anyID, timeInSeconds)

    Now, I get the serverConnectionHandlerID with ts3plugin_getCurrentServerConnectionHandlerID(); right?
    And what is anyID supposed to mean?
    Or do I just call them with getCurrentServerConnectionHandlerID() and banclient(...)?
    I think, you are mixing (what I call) callbacks and ts3clientlib-functions.

    banclient and getCurrentServerConnectionHandlerID are both functions in the function-pointer-struct.

    Let's say, you wanna ban a client if he requests it with a textmessage and only if it's on your current server connection handler (your current active tab in the client ui). That would work with something like this (I skipped some overhead):
    Code:
    //...
    static struct TS3Functions ts3libfuncs;
    //...
    
    void ts3plugin_setFunctionPointers(const struct TS3Functions functions) {
      ts3libfuncs = functions;
    }
    
    //...
    
    int ts3plugin_onTextMessageEvent(uint64 schid, anyID targetMode, anyID toID, anyID fromID, const char* fromName, const char* fromUniqueIdentifier, const char* message, int ffIgnored) {
      uint64 curschid;
      if (ts3libfuncs.getCurrentServerConnectionHandlerID(&curschid) != ERROR_ok) {
        //oops, something went wrong getting the current schid, but we are too lazy to get an error message with ts3lib.getErrorMessage(err, ...)
        return 0;
      }
    
      if (curschid != schid)
        return 0;
    
      if (targetMode != TextMessageTarget_CLIENT) {
        //react only on private messages
        return 0;
      }
            
      if (!strcmp(message, "!banme")) {
        unsigned int err = ts3libfuncs.banclient(schid, fromID, 1000, "Who wants to be banned, gets banned", NULL);
      }
    
      return 0; //the client will show the textmessage as normal
    }
    This code is neither failsafe nor tested and does not check for permissions or errors. But I don't want to blow up this example (keyword: returncodes).

  12. #12
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    142
    That actually helped quite a bit, thanks.
    But like, about the permissions, if there are no permissions to perforn an action it simply would not do it, or would it crash (or worse)?

    Also, I couldn't find a Register Hotkey function.

    and for the custom header, is a

    Code:
    extern "C": {
        Datatype Func()
        Datatype Func2()
        Datatype Func3()
    }
    Needed? Or does it need to have any special keywords or orders?

    Or do I just straight include the default headers?

    And in the static struct ts3functions I write the pointers to the functions or the function declaration?

    Also, in your code you used
    "return 0; //the client will show the textmessage as normal"

    Won't need it, but just being curious:
    How would it not be shown normal then?
    (Or not shown at all?)
    Last edited by Jhhhj_original; February 13th, 2017 at 12:02 PM.

  13. #13
    Join Date
    September 2012
    Posts
    6,078
    Quote Originally Posted by Jhhhj_original View Post
    That actually helped quite a bit, thanks.
    But like, about the permissions, if there are no permissions to perforn an action it simply would not do it, or would it crash (or worse)?
    It just wouldn't do it. In case you don't do any error handling (through return codes and the on(Permission)ErrorEvent) the client would print an error into the server chat, as if you manually tried to do the requested action.

    Quote Originally Posted by Jhhhj_original View Post
    Also, I couldn't find a Register Hotkey function.
    You have to tell the client which hotkeys your plugin offers through the ts3plugin_initHotkeys callback.
    You can then call ts3functions.requestHotkeyInputDialog to open the dialog for the user to press the desired hotkey, or the user can set up the hotkey in the regular client options, just like any other hotkey.
    The client will call the ts3plugin_onHotkeyEvent callback whenever the user presses any hotkey he set up that targets one of your hotkeys you specified to handle through the initHotkeys callback.

    Quote Originally Posted by Jhhhj_original View Post
    and for the custom header, is a

    Code:
    extern "C": {
        Datatype Func()
        Datatype Func2()
        Datatype Func3()
    }
    Needed? Or does it need to have any special keywords or orders?

    Or do I just straight include the default headers?
    If you're talking about C# I don't know.
    In C(++) you just go ahead and include the necessary header files from the plugin SDK.


    Quote Originally Posted by Jhhhj_original View Post
    And in the static struct ts3functions I write the pointers to the functions or the function declaration?
    You don't have to define the struct, the definition is already there. Just use it as is.
    If you're talking about C# again I don't know what you have to do for that. Maybe you'll find some information by looking through the regular SDK C# Wrapper.
    For C# you may also want to check out this thread where someone has started wrapping the PluginSDK for C#.

    Quote Originally Posted by Jhhhj_original View Post
    Also, in your code you used
    "return 0; //the client will show the textmessage as normal"

    Won't need it, but just being curious:
    How would it not be shown normal then?
    (Or not shown at all?)
    As documented in the example files of the plugin SDK, the onTextMessageEvent and onClientPokeEvent functions can opt to suppress the message by returning 1. The client will not display any message in that case.
    When sending PMs please make sure to include a reference link to the thread in question in the body of your message.

  14. #14
    Join Date
    October 2015
    Location
    Germany, Bavaria
    Posts
    142
    Alright I've got sortof a test script going...
    It just keeps throwing
    Code:
    \usr\src\debug\cygwin-2.3.1-1\winsup\cygwin\lib\libcmain.c|39|undefined reference to `WinMain'|
    Its the only compiler Error I get.
    I took that code in like 39 from the example plugin:
    Code:
    #ifdef _WIN32
    /* Helper function to convert wchar_T to Utf-8 encoded strings on Windows */
    static int wcharToUtf8(const wchar_t* str, char** result) {
    	int outlen = WideCharToMultiByte(CP_UTF8, 0, str, -1, 0, 0, 0, 0);
    	*result = (char*)malloc(outlen);
    	if(WideCharToMultiByte(CP_UTF8, 0, str, -1, *result, outlen, 0, 0) == 0) {
    		*result = NULL;
    		return -1;
    	}
    	return 0;
    }
    #endif
    I use Cygwin GCC to compile, and I have no idea why it throws an Error.
    Anyone know?
    Is that code really necessary?

  15. #15
    Join Date
    September 2005
    Location
    Germany / Dortmund
    Posts
    1,376
    Are you compiling manually? Did you add "-shared" to your compile command?

    Maybe you should use some IDE which offers a shared library project type (like Codeblocks or DevCPP).

Thread Information

Users Browsing this Thread

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

Similar Threads

  1. Creating plugin Java
    By Retzugil in forum Client Plugins / Lua Scripts
    Replies: 1
    Last Post: May 15th, 2014, 07:23 AM
  2. Creating my first TS3 plugin and it won't load
    By DrPastah in forum Client Plugins / Lua Scripts
    Replies: 5
    Last Post: March 12th, 2014, 11:14 AM
  3. Problems while creating a plugin..
    By Loreman in forum General Questions
    Replies: 0
    Last Post: October 6th, 2013, 03:45 PM
  4. Creating complex GUI with Plugin
    By TigerSnail in forum General Questions
    Replies: 3
    Last Post: May 23rd, 2013, 08:43 AM

Tags for this Thread

Posting Permissions

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