TeamSpeak-3-Java-API icon indicating copy to clipboard operation
TeamSpeak-3-Java-API copied to clipboard

Old channel for ClientMovedEvent?

Open Linnun opened this issue 8 years ago • 7 comments

Is it possible for a ClientMovedEvent to know, which channel a client switched away from?

Currently you know which client switched and which channel he switched to. But which channel did he switch from?

Linnun avatar May 07 '16 02:05 Linnun

The server doesn't provide that info to us I believe. So I'm afraid you'll have to keep track of old channels yourself.

2016-05-07 4:45 GMT+02:00 Linnun [email protected]:

Is it possible for a ClientMovedEvent to know, which channel a client switched away from?

Currently you know which client switched and which channel he switched to. But which channel did he switch from?

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/TheHolyWaffle/TeamSpeak-3-Java-API/issues/96

TheHolyWaffle avatar May 07 '16 09:05 TheHolyWaffle

You could put everone joining a specific channel into an ArrayList and remove them if the switch to a channel != your channel

Scrumplex avatar May 07 '16 10:05 Scrumplex

Oh, that's too bad. I hoped the server would provide that information.

Linnun avatar May 07 '16 15:05 Linnun

Quick follow-up question. Is there any interest in having this functionality in this API, or is this something that everyone would have to implement themselves?

By this I mean having some kind of cached clientlist that gets updated on all kinds of ClientEvent, so that you can for example know where they left from. Also the end users would have to make alot less ServerQuery commands, since you don't need to continually query for the clientlist, if you already have it cached.

Linnun avatar Jun 03 '16 09:06 Linnun

I've been thinking about adding something like a "cache", but not just for clients but also for channels, virtual servers, channel and server groups, etc.

That cache would have to be implemented in the API itself, as users do not have the in-depth access to all the functions you'd need to implement something like that reliably. E.g. we could make it so that if a client calls "moveClient(int)" and we got a response of "ok", we would modify that client's data in the cache. We could also the use the "whoami" command that gets sent as a keep-alive signal (provided no other commands have been sent for a minute) to update the query's chached data.

I haven't really worked towards adding such a cache, though, for a few reasons:

  • Not all API users would actually use that cache, and many would only use a small part of it
  • It adds complexity to our own code
  • It adds a little complexity to the API
  • There's a certain memory usage aspect that users might not expect
  • It would actually take quite a bit of time to implement it properly.
  • I wonder if it is actually necessary: likely only a few users are limited by the 20-commands-per-second limit

But if that's a feature that's important to you, we could start designing such a cache, perhaps in a different branch. If it turns out to be a useful feature that doesn't cause too much code bloating, we could then merge it into master :smile:

rogermb avatar Jun 03 '16 12:06 rogermb

A few thoughts:

  • Not all API users would have to use the cache. It could become a boolean option in TS3Config to enable or disable this cache
  • Assuming this cache exists, how would we get the client's old channel in onClientMoved()? Would we have to modify (or override) the ClientMovedEvent in a custom way? Is there any 'clean' way to do this? (I consider manually extending the TS3SQ events dirty)
  • How reliable is it to receive all events? If you only miss one ClientMovedEvent, your entire cache becomes erroneous. What about reconnects? Sure, we could build a new cache. What about lags? Do they sometimes cause events to fail? If so, how do we know? The cache is pointless if we need to synchronize it every minute.
  • What's with the moveClient(int) scenario you talked about? Does it not throw a ClientMovedEvent, if you are invoking this client move yourself?

This is the actual use-case I'm currently facing: I have a channel X. If a user from servergroup Y is moved out of channel X by a user from servergroup Z, a certain action should trigger. Currently I can't think of a proper way to solve this. Well, I could listen to all kinds of ClientEvents and manually keep track of who is in channel X and check on any ClientMoveEvent if a client from that list was moved - but I don't really like that approach.

Linnun avatar Jun 03 '16 12:06 Linnun

Not all API users would have to use the cache. It could become a boolean option in TS3Config to enable or disable this cache

Yeah, something like that would be a good idea.

how would we get the client's old channel in onClientMoved() Is there any 'clean' way to do this?

Well, not by adding a getter to ClientMoveEvent ^^ I was thinking about having users follow a path along the lines of cache -> getClient -> channelId, but that would also not be very clean. More importantly, however, it would have concurrency issues we cannot account for. Honestly, I think the best option would be to add a getter to ClientMoveEvent that throws an exception if the cache is not enabled. That way users would know that a) something went wrong b) what went wrong and c) what they need to do to fix it.

How reliable is it to receive all events?

Seeing how the connection is based on TCP, it is extremely unlikely we'd just "miss" a packet (in the region of "not gonna happen in a thousand years"). What could happen is that there's some sort of connection problem, in which case we'd get disconnected and we'd have to clear our cache.

What's with the moveClient(int) scenario you talked about? Does it not throw a ClientMovedEvent, if you are invoking this client move yourself?

Depends on whether the query is actually listening to that event type. We cannot make any assumptions.

This is the actual use-case I'm currently facing: [...]

I'd suggest the following approach:

  • Maintain a Map from client IDs to their UIds
    • Listen to SERVER events
    • On client join, put their UId into the map
    • On client leave, remove it again (optional)
  • Use getServerGroupClients for group Y and Z. For both lists, do some transformations to get to a Set of unique IDs, for which you can call contains to look up if a client is a member of said server group.
    • I don't know how often your clients get assigned server groups, so it might be a good idea to keep that set updated semi-regularily, e.g. update it all 2-5 minutes.
  • Listen to events of type CHANNEL for only channel X.
  • Whenever you get a ClientMovedEvent:
    • If getTargetChannelId is the ID of channel X -> Ignore
    • If getInvokerId == -1 -> Ignore
    • If getInvokerUniqueId is not in the set of Z -> Ignore
    • Use your clid -> uid Map to get the UId of the client who was moved
    • If that client's UId is not in the set of Y -> Ignore
    • Finally, if it's gone through all that without being ignored, do your stuff.

rogermb avatar Jun 03 '16 13:06 rogermb