Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added clientinfo command support #16

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

Henner25
Copy link

@Henner25 Henner25 commented Jul 1, 2018

Added support for the clientinfo command which returns detailed information about individual online clients.

The client id clid is not included in the server response, probably because it was used to request the data in the first place, but I think it is useful to include it in the struct.

Naming scheme

I dropped the "client_" prefix from the response fields and camelcased the rest, for example client_talk_power -> TalkPower but connection_packets_sent_total -> ConnectionPacketsSentTotal.

Notes about specific fields of the server response

These information are also included as comments at the field definitions.

  • client_meta_data and client_badges: They are currently commented out in the struct definition. Because of insufficient data I could not infer a proper type for them and I do not want to risk a later API change because the type I picked was wrong.

  • client_is_talker: The manual states that this reflects whether the client has enough talk power to talk, however this was always 0 during testing.

  • client_lastconnected: According to the manual

    Creation date and time of the clients last connection to the server

    But the actual values I receive corresponded to the timestamp of me being online for the last time, that is the time just before I disconnected from the server.

Test

The mock-response for the test is a mix of real data and the example from the manual because the latter consists of very few fields only. I scrambled possibly identifying information (IP-addresses, UIDs etc.), hopefully without introducing illegal characters.

N.B.

The first commit adds the ChannelID field to the OnlineClient struct. I think there was a mixup between clid and cid.

Copy link
Contributor

@stevenh stevenh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, looking good, just a few niggles.

If it is possible to use time.Time and time.Duration for the relevant fields, that would be good.

Also some metalinter issues to fix, see travis results.

server_cmds.go Outdated
// Takes the form of "/channelID"
DefaultChannel string `ms:"client_default_channel"`

//TODO: I never managed to receive any value for this field
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little niggle could we format it like:
// TODO(Henner25): ....

server_cmds.go Outdated

// Milliseconds since the client did something, for example sending
// a message, muting themselves or talking.
IdleTime int64 `ms:"client_idle_time"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we capture this as a time.Duration, so we don't have to know its in Milliseconds?

server_cmds.go Outdated
Servergroups int `ms:"client_servergroups"`

// UTC timestamp at which the client has been created.
Created int64 `ms:"client_created"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do this a time.Time, same for other time based fields?

This also adds a proper struct to hold the response data. Documentation
of the fields is a mix of the server query manual and experiments with
an actual server. I could not figure out the meaning or data type for
all fields, most notably `client_badges` and `client_meta_data` which I
even excluded from the struct definition.

Data for the tests is the incomplete data from the manual supplemented
by real query results. Fields like ip address and UUIDs have been
anonymized by replacing characters. I tried not to introduce characters
which were previously not used in the value.
@Henner25
Copy link
Author

Henner25 commented Jul 1, 2018

Ah, you're quick to respond. I saw the lints and fixed them by amending and force-pushing the commit.

TODO's will be tagged with my name.

I'd have loved to the time package types from the beginning. However the mapstructure decoder needs to know to consider the integer values to be milliseconds and utc timestamps and I wanted to use WithResponse() without changing its internals. But you're right. Having the correct types is much better than having to manually convert it with the correct unit, so I'll fix it.

@stevenh
Copy link
Contributor

stevenh commented Jul 1, 2018

It should be pretty trivial to add the time.Duration decoder see:
https://github.com/multiplay/go-ts3/blob/master/helpers.go#L147

@Henner25
Copy link
Author

Henner25 commented Jul 2, 2018

The target field needs some way to declare the duration units since some data is returned as seconds (e.g. hostinfo->instance_uptime, others apparently as milliseconds (e.g. clientinfo->client_idle_time).

I'm thinking about specifying the unit in the struct tag itself; I think that makes sense since is basically a serialization/representation issue, too.
Alternatively, I'd try to extend the Cmd-API with an WithDecodeHook() function, which lets you specify custom decoders for specific fields of the response, and then some exported DecodeDurationHook(fromUnits) implementation for ease of use.

Do either of those sound sensible to you or am I missing something about the unit-issue?

@stevenh
Copy link
Contributor

stevenh commented Jul 2, 2018

I like the idea of adding a tag, that seems like a nice clean solution 👍

@Henner25
Copy link
Author

Henner25 commented Jul 3, 2018

I think it really won't work :( It seems that the mapstructure DecoderHooks cannot access the tags of a struct field anymore: After finding the correct struct field to decode into, the recursive decode call passes reflect.Value of the struct field but we'd need the reflect.StructField type to access the tags. All we have in the decoder is now the "time.Duration" value without knowing which struct (if any at all!) it came from.

I'll implement my alternative suggestion and see how nicely that turns out.

@stevenh
Copy link
Contributor

stevenh commented Jul 3, 2018 via email

@stevenh
Copy link
Contributor

stevenh commented Jul 10, 2018

Having looked at this the simplest solution may be to use wrapping struct which has a reference to the type being decoded so it can gain context.

This will still fall short if the struct requires two different decode methods e.g. one ms based duration and one second based duration as it still won't be able to tell which field without improving the hook system :(

server_cmds.go Outdated Show resolved Hide resolved
This fixes the Servergroups field of DetailedOnlineClient.

Credits for the discovery and solution go to irgendwr.
// sliceHookFunc supports int/string decoding to slice
func sliceHookFunc(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) {
if to == reflect.Slice {
if from == reflect.String {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of these if's cant be true so a switch on from would be better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants