Moving further along with the connection-level commands that are received, and now have model objects to store variables. No callbacks to the server yet, but that will happen soon. Right now I have channel listings and basic client information done.

This commit is contained in:
Manevolent
2018-07-28 22:34:18 -04:00
parent f124583fa6
commit f533e2e918
26 changed files with 1282 additions and 186 deletions
@@ -1,6 +1,6 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.part.CommandParameter;
import com.github.manevolent.ts3j.command.parameter.CommandParameter;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
public interface Command {
@@ -1,6 +1,9 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.client.ClientChannelListFinishedProcessor;
import com.github.manevolent.ts3j.command.client.ClientChannelListProcessor;
import com.github.manevolent.ts3j.command.client.ClientInitServerProcessor;
import com.github.manevolent.ts3j.command.client.ClientNotifyClientEnterViewProcessor;
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
import com.github.manevolent.ts3j.util.Ts3Debugging;
@@ -15,19 +18,28 @@ public final class CommandHandler implements CommandProcessor {
}
@Override
public void process(AbstractTeamspeakClientSocket socket, ComplexCommand complexCommand)
public void process(AbstractTeamspeakClientSocket socket, MultiCommand multiCommand)
throws CommandProcessException {
Ts3Debugging.debug("[COMMAND] " + complexCommand.build());
CommandProcessor processor = commandProcessors.get(complexCommand.getName().toLowerCase());
CommandProcessor processor = commandProcessors.get(multiCommand.getName().toLowerCase());
if (processor != null)
processor.process(socket, complexCommand);
processor.process(socket, multiCommand);
}
public static CommandHandler createLocalClient() {
@Override
public void process(AbstractTeamspeakClientSocket socket, SingleCommand command)
throws CommandProcessException {
CommandProcessor processor = commandProcessors.get(command.getName().toLowerCase());
if (processor != null)
processor.process(socket, command);
}
public static CommandHandler createLocalClientHandler() {
CommandHandler handler = new CommandHandler();
handler.commandProcessors.put("initserver", new ClientInitServerProcessor());
handler.commandProcessors.put("channellist", new ClientChannelListProcessor());
handler.commandProcessors.put("notifycliententerview", new ClientNotifyClientEnterViewProcessor());
handler.commandProcessors.put("channellistfinished", new ClientChannelListFinishedProcessor());
return handler;
}
@@ -3,6 +3,14 @@ package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
public interface CommandProcessor {
void process(AbstractTeamspeakClientSocket socket, ComplexCommand complexCommand)
throws CommandProcessException;
default void process(AbstractTeamspeakClientSocket client,
MultiCommand multiCommand)
throws CommandProcessException {
for (SingleCommand singleCommand : multiCommand.simplify()) process(client, singleCommand);
}
default void process(AbstractTeamspeakClientSocket client,
SingleCommand singleCommand)
throws CommandProcessException { }
}
@@ -1,90 +0,0 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.part.CommandOption;
import com.github.manevolent.ts3j.command.part.CommandParameter;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.util.Ts3String;
import java.util.*;
public class ComplexCommand implements Command {
private final String name;
private final ProtocolRole role;
private final List<CommandParameter> params;
public ComplexCommand(String name, ProtocolRole role, CommandParameter... params) {
this(name, role, Arrays.asList(params));
}
public ComplexCommand(String name, ProtocolRole role, List<CommandParameter> params) {
this.name = name;
this.role = role;
this.params = new LinkedList<>();
for (CommandParameter parameter : params) add(parameter);
}
@Override
public String getName() {
return name;
}
@Override
public ProtocolRole getRole() {
return role;
}
@Override
public Iterable<CommandParameter> getParameters() {
return Collections.unmodifiableCollection(params);
}
public boolean has(String key) {
return params.stream().anyMatch(x -> x.getName().equalsIgnoreCase(key));
}
public void add(CommandParameter parameter) {
if (parameter.getName().trim().length() <= 0)
throw new IllegalArgumentException("invalid key (empty)");
this.params.add(parameter);
}
public SimpleCommand simplify() {
return new SimpleCommand(
name,
role,
params
);
}
public static ComplexCommand parse(ProtocolRole role, String commandText) {
String[] commandParts = commandText.split(" ");
String commandLabel = commandParts[0].toLowerCase();
List<CommandParameter> parameterList = new LinkedList<>();
for (int i = 1; i < commandParts.length; i ++) {
String commandParamText = commandParts[i].trim();
if (commandParamText.length() <= 0) continue;
if (commandParamText.startsWith("-")) { // Option
parameterList.add(new CommandOption(commandParamText.substring(1)));
} else {
String[] commandParamParts = commandParamText.split("=", 2);
parameterList.add(new CommandSingleParameter(
commandParamParts[0].toLowerCase(),
commandParamParts.length > 1 ?
Ts3String.unescape(commandParamParts[1]) :
null
)
);
}
}
return new ComplexCommand(commandLabel, role, parameterList);
}
}
@@ -0,0 +1,117 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.parameter.CommandOption;
import com.github.manevolent.ts3j.command.parameter.CommandParameter;
import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.util.Ts3String;
import java.util.*;
public class MultiCommand implements Command {
private final String name;
private final ProtocolRole role;
private final List<SingleCommand> commands;
public MultiCommand(String name, ProtocolRole role, SingleCommand... params) {
this(name, role, Arrays.asList(params));
}
public MultiCommand(String name, ProtocolRole role, List<SingleCommand> params) {
this.name = name;
this.role = role;
this.commands = Collections.unmodifiableList(params);
}
@Override
public String getName() {
return name;
}
@Override
public ProtocolRole getRole() {
return role;
}
@Override
public Iterable<CommandParameter> getParameters() {
throw new UnsupportedOperationException();
}
@Override
public void add(CommandParameter parameter) {
throw new UnsupportedOperationException();
}
public List<SingleCommand> simplify() {
return Collections.unmodifiableList(commands);
}
public SingleCommand simplifyFirst() {
return commands.stream().findFirst().orElse(null);
}
public SingleCommand simplifyOne() {
if (commands.size() > 1) throw new IllegalArgumentException("ambiguous; more than one command");
return commands.stream().findFirst().orElse(null);
}
@Override
public String build() {
StringBuilder builder = new StringBuilder();
builder.append(getName().toLowerCase());
List<SingleCommand> singleCommands = simplify();
for (int i = 0; i < singleCommands.size(); i ++) {
boolean first = i == 0;
boolean last = i == singleCommands.size() - 1;
if (!first) builder.append("\\p");
for (CommandParameter parameter : singleCommands.get(i).getParameters()) {
builder.append(" " + (parameter.toString()).trim());
}
}
return builder.toString();
}
public static MultiCommand parse(ProtocolRole role, String text) {
String[] labelAndText = text.split(" ", 2);
String label = labelAndText[0].toLowerCase();
String[] commands = labelAndText.length > 1 ? labelAndText[1].split("\\|") : new String[0];
List<SingleCommand> singleCommands = new LinkedList<>();
for (String commandText : commands) {
String[] commandParts = commandText.split(" ");
List<CommandParameter> parameterList = new LinkedList<>();
for (int i = 0; i < commandParts.length; i++) {
String commandParamText = commandParts[i].trim();
if (commandParamText.length() <= 0) continue;
if (commandParamText.startsWith("-")) { // Option
parameterList.add(new CommandOption(commandParamText.substring(1)));
} else {
String[] commandParamParts = commandParamText.split("=", 2);
parameterList.add(new CommandSingleParameter(
commandParamParts[0].toLowerCase(),
commandParamParts.length > 1 ?
Ts3String.unescape(commandParamParts[1]) :
null
)
);
}
}
singleCommands.add(new SingleCommand(label, role, parameterList));
}
return new MultiCommand(label, role, singleCommands);
}
}
@@ -1,23 +1,20 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.part.CommandOption;
import com.github.manevolent.ts3j.command.part.CommandParameter;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.command.parameter.CommandParameter;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.util.Ts3String;
import java.util.*;
public class SimpleCommand implements Command {
public class SingleCommand implements Command {
private final String name;
private final ProtocolRole role;
private final Map<String, CommandParameter> params;
public SimpleCommand(String name, ProtocolRole role, CommandParameter... params) {
public SingleCommand(String name, ProtocolRole role, CommandParameter... params) {
this(name, role, Arrays.asList(params));
}
public SimpleCommand(String name, ProtocolRole role, List<CommandParameter> params) {
public SingleCommand(String name, ProtocolRole role, List<CommandParameter> params) {
this.name = name;
this.role = role;
@@ -47,7 +44,7 @@ public class SimpleCommand implements Command {
public void add(CommandParameter parameter) {
CommandParameter existing = get(parameter.getName());
if (existing != null) throw new IllegalArgumentException(parameter.getName() + " already set");
if (existing != null) throw new IllegalArgumentException(getName() + ": " + parameter.getName() + " already set");
if (parameter.getName().trim().length() <= 0)
throw new IllegalArgumentException("invalid key (empty)");
@@ -63,7 +60,7 @@ public class SimpleCommand implements Command {
return this.params.remove(key);
}
public static SimpleCommand parse(ProtocolRole role, String commandText) {
return ComplexCommand.parse(role, commandText).simplify();
public static SingleCommand parse(ProtocolRole role, String commandText) {
return MultiCommand.parse(role, commandText).simplifyFirst();
}
}
@@ -0,0 +1,20 @@
package com.github.manevolent.ts3j.command.client;
import com.github.manevolent.ts3j.command.CommandProcessException;
import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
public class ClientChannelListFinishedProcessor implements CommandProcessor {
@Override
public void process(AbstractTeamspeakClientSocket client,
MultiCommand command) throws CommandProcessException {
if (client.getState() == ClientConnectionState.RETRIEVING_DATA)
try {
client.setState(ClientConnectionState.CONNECTED);
} catch (Exception e) {
throw new CommandProcessException(e);
}
}
}
@@ -0,0 +1,45 @@
package com.github.manevolent.ts3j.command.client;
import com.github.manevolent.ts3j.command.CommandProcessException;
import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.SingleCommand;
import com.github.manevolent.ts3j.enums.CodecType;
import com.github.manevolent.ts3j.model.Channel;
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
public class ClientChannelListProcessor implements CommandProcessor {
@Override
public void process(AbstractTeamspeakClientSocket client,
SingleCommand command) throws CommandProcessException {
int cid = Integer.parseInt(command.get("cid").getValue());
Channel channel = client.getVirtualServer().getChannelById(cid);
if (channel == null)
channel = new Channel(
client.getVirtualServer(), Integer.parseInt(command.get("cid").getValue())
);
channel.setName(command.get("channel_name").getValue());
channel.setCodec(CodecType.fromId(Integer.parseInt(command.get("channel_codec").getValue())));
channel.setCodecQuality(Integer.parseInt(command.get("channel_codec_quality").getValue()));
channel.setMaxClients(Integer.parseInt(command.get("channel_maxclients").getValue()));
channel.setMaxFamilyClients(Integer.parseInt(command.get("channel_maxfamilyclients").getValue()));
channel.setOrder(Integer.parseInt(command.get("channel_order").getValue()));
channel.setPermanent(command.get("channel_flag_permanent").getValue().equals("1"));
channel.setSemiPermanent(command.get("channel_flag_semi_permanent").getValue().equals("1"));
channel.setServerDefault(command.get("channel_flag_default").getValue().equals("1"));
channel.setHasPassword(command.get("channel_flag_password").getValue().equals("1"));
channel.setCodecLatencyFactor(command.get("channel_codec_latency_factor").getValue().equals("1"));
channel.setUnencrypted(command.get("channel_codec_is_unencrypted").getValue().equals("1"));
channel.setDeleteDelay(Integer.parseInt(command.get("channel_delete_delay").getValue()));
channel.setUnlimited(command.get("channel_flag_maxclients_unlimited").getValue().equals("1"));
channel.setFamilyUnlimited(command.get("channel_flag_maxfamilyclients_unlimited").getValue().equals("1"));
channel.setInheritingMaxfamilyUnlimited(command.get("channel_flag_maxfamilyclients_inherited").getValue().equals("1"));
channel.setNeededTalkPower(Integer.parseInt(command.get("channel_needed_talk_power").getValue()));
channel.setForcingSilence(command.get("channel_forced_silence").getValue().equals("1"));
channel.setIconId(Integer.parseInt(command.get("channel_icon_id").getValue()));
channel.setPrivated(command.get("channel_flag_private").getValue().equals("1"));
client.getVirtualServer().updateChannel(channel);
}
}
@@ -2,24 +2,46 @@ package com.github.manevolent.ts3j.command.client;
import com.github.manevolent.ts3j.command.CommandProcessException;
import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.ComplexCommand;
import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.command.SingleCommand;
import com.github.manevolent.ts3j.enums.HostBannerMode;
import com.github.manevolent.ts3j.enums.HostMessageMode;
import com.github.manevolent.ts3j.enums.VoiceEncryptionMode;
import com.github.manevolent.ts3j.model.VirtualServer;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
import com.github.manevolent.ts3j.util.Ts3Debugging;
public class ClientInitServerProcessor implements CommandProcessor {
@Override
public void process(AbstractTeamspeakClientSocket socket, ComplexCommand complexCommand) throws CommandProcessException {
SimpleCommand command = complexCommand.simplify();
public void process(AbstractTeamspeakClientSocket client,
MultiCommand multiCommand) throws CommandProcessException {
SingleCommand command = multiCommand.simplifyOne();
Ts3Debugging.debug(command.get("virtualserver_welcomemessage").getValue());
VirtualServer server = new VirtualServer();
if (socket.getState() == ClientConnectionState.RETRIEVING_DATA)
try {
socket.setState(ClientConnectionState.CONNECTED);
} catch (Exception e) {
throw new CommandProcessException(e);
}
server.setName(command.get("virtualserver_name").getValue());
server.setWelcomeMessage(command.get("virtualserver_welcomemessage").getValue());
server.setPlatform(command.get("virtualserver_platform").getValue());
server.setVersion(command.get("virtualserver_version").getValue());
server.setMaxClients(Integer.parseInt(command.get("virtualserver_maxclients").getValue()));
server.setCreated(Long.parseLong(command.get("virtualserver_created").getValue()));
server.setEncryptionMode(VoiceEncryptionMode.fromId(Integer.parseInt(command.get("virtualserver_codec_encryption_mode").getValue())));
server.setHostMessageMode(HostMessageMode.fromId(Integer.parseInt(command.get("virtualserver_hostmessage_mode").getValue())));
server.setDefaultGroupId(Integer.parseInt(command.get("virtualserver_default_server_group").getValue()));
server.setDefaultChannelGroupId(Integer.parseInt(command.get("virtualserver_default_channel_group").getValue()));
server.setHostbannerUrl(command.get("virtualserver_hostbanner_gfx_url").getValue());
server.setPrioritySpeakerDimmModifier(Double.parseDouble(command.get("virtualserver_priority_speaker_dimm_modificator").getValue()));
server.setId(Integer.parseInt(command.get("virtualserver_id").getValue()));
server.setNamePhonetic(command.get("virtualserver_name_phonetic").getValue());
server.setIconId(Long.parseLong(command.get("virtualserver_icon_id").getValue()));
server.setServerIp(command.get("virtualserver_ip").getValue());
server.setAskForPriviledgeKey(Integer.parseInt(command.get("virtualserver_ask_for_privilegekey").getValue()) == 1);
server.setHostBannerMode(HostBannerMode.fromId(Integer.parseInt(command.get("virtualserver_hostbanner_mode").getValue())));
server.setDefaultTempChannelDeleteDelay(Integer.parseInt(command.get("virtualserver_channel_temp_delete_delay_default").getValue()));
server.setNickname(command.get("virtualserver_nickname").getValue());
client.setVirtualServer(server);
client.setClientId(Integer.parseInt(command.get("aclid").getValue()));
}
}
@@ -0,0 +1,71 @@
package com.github.manevolent.ts3j.command.client;
import com.github.manevolent.ts3j.command.CommandProcessException;
import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.SingleCommand;
import com.github.manevolent.ts3j.enums.ClientType;
import com.github.manevolent.ts3j.identity.Uid;
import com.github.manevolent.ts3j.model.Client;
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
import java.util.Arrays;
import java.util.stream.Collectors;
public class ClientNotifyClientEnterViewProcessor implements CommandProcessor {
@Override
public void process(AbstractTeamspeakClientSocket client,
SingleCommand command) throws CommandProcessException {
int clientId = Integer.parseInt(command.get("clid").getValue());
Client newClient = client.getVirtualServer().getClientById(clientId);
if (newClient == null)
newClient = new Client(
Integer.parseInt(command.get("clid").getValue()),
Integer.parseInt(command.get("client_database_id").getValue()),
client.getVirtualServer(),
new Uid(command.get("client_unique_identifier").getValue()),
ClientType.fromId(Integer.parseInt(command.get("client_type").getValue())));
newClient.setNickname(command.get("client_nickname").getValue());
newClient.setInputMuted(command.get("client_input_muted").getValue().equals("1"));
newClient.setOutputMuted(command.get("client_output_muted").getValue().equals("1"));
newClient.setInputHardware(command.get("client_input_hardware").getValue().equals("1"));
newClient.setOutputHardware(command.get("client_output_hardware").getValue().equals("1"));
newClient.setRecording(command.get("client_is_recording").getValue().equals("1"));
newClient.setChannelGroupId(Integer.parseInt(command.get("client_channel_group_id").getValue()));
newClient.setServerGroupIds(
Arrays.stream(command.get("client_servergroups").getValue().split(",")).map(
Integer::parseInt
).collect(Collectors.toList())
);
newClient.setAway(command.get("client_away").getValue().equals("1"));
newClient.setTalkPower(Integer.parseInt(command.get("client_talk_power").getValue()));
newClient.setTalkRequested(command.get("client_talk_request").getValue().equals("1"));
newClient.setTalker(command.get("client_is_talker").getValue().equals("1"));
newClient.setPrioritySpeaker(command.get("client_is_priority_speaker").getValue().equals("1"));
newClient.setUnreadMessages(Integer.parseInt(command.get("client_unread_messages").getValue()));
newClient.setNeededServerQueryViewPower(Integer.parseInt(command.get("client_needed_serverquery_view_power").getValue()));
newClient.setIconId(Integer.parseInt(command.get("client_icon_id").getValue()));
newClient.setChannelCommander(command.get("client_is_channel_commander").getValue().equals("1"));
newClient.setCountry(command.get("client_country").getValue());
newClient.setInheritedChannelGroupChannelId(Integer.parseInt(command.get("client_channel_group_inherited_channel_id").getValue()));
client.getVirtualServer().updateConnectedClient(newClient);
}
}
@@ -1,4 +1,4 @@
package com.github.manevolent.ts3j.command.part;
package com.github.manevolent.ts3j.command.parameter;
public class CommandOption implements CommandParameter {
private final String key;
@@ -1,4 +1,4 @@
package com.github.manevolent.ts3j.command.part;
package com.github.manevolent.ts3j.command.parameter;
import java.util.Base64;
@@ -1,4 +1,4 @@
package com.github.manevolent.ts3j.command.part;
package com.github.manevolent.ts3j.command.parameter;
import com.github.manevolent.ts3j.util.Ts3String;
@@ -1,7 +1,7 @@
package com.github.manevolent.ts3j.command.response;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.ComplexCommand;
import com.github.manevolent.ts3j.command.MultiCommand;
import java.util.concurrent.TimeoutException;
@@ -11,7 +11,7 @@ public class CommandResponse {
private final int returnCode;
private long dispatchedTime;
private ComplexCommand returnedCommand;
private MultiCommand returnedCommand;
public CommandResponse(Command command, int returnCode) {
this.command = command;
@@ -26,12 +26,12 @@ public class CommandResponse {
return returnCode;
}
public ComplexCommand getResponse()
public MultiCommand getResponse()
throws InterruptedException, TimeoutException {
return getResponse(Integer.MAX_VALUE);
}
public ComplexCommand getResponse(int millis)
public MultiCommand getResponse(int millis)
throws InterruptedException, TimeoutException {
if (returnCode < 0) return null;
@@ -59,11 +59,11 @@ public class CommandResponse {
this.dispatchedTime = dispatchedTime;
}
public ComplexCommand getReturnedCommand() {
public MultiCommand getReturnedCommand() {
return returnedCommand;
}
public void setReturnedCommand(ComplexCommand returnedCommand) {
public void setReturnedCommand(MultiCommand returnedCommand) {
synchronized (returnLock) {
if (this.returnCode < 0 ) throw new IllegalArgumentException("not expecting return command");
if (this.returnedCommand != null) throw new IllegalArgumentException("already set return command");
@@ -21,7 +21,7 @@ public class Identity {
}
private final ECPoint publicKey;
private final String fingerprint;
private final Uid uid;
private final String publicKeyString;
private long keyOffset = 0L, lastCheckedKeyOffset = 0L;
@@ -29,7 +29,7 @@ public class Identity {
public Identity(ECPoint publicKey) {
this.publicKey = publicKey;
this.publicKeyString = Base64.getEncoder().encodeToString(Ts3Crypt.encodePublicKey(publicKey));
this.fingerprint = generateFingerprint();
this.uid = generateUid();
}
public ECPoint getPublicKey() {
@@ -40,8 +40,8 @@ public class Identity {
return publicKeyString;
}
public String getFingerprint() {
return fingerprint;
public Uid getUid() {
return uid;
}
public long getLastCheckedKeyOffset() {
@@ -129,11 +129,9 @@ public class Identity {
return curr;
}
private String generateFingerprint() {
private Uid generateUid() {
synchronized (sha1) {
return Base64.getEncoder().encodeToString(
sha1.digest(getPublicKeyString().getBytes(Charset.forName("ASCII")))
);
return new Uid(sha1.digest(getPublicKeyString().getBytes(Charset.forName("ASCII"))));
}
}
}
@@ -0,0 +1,42 @@
package com.github.manevolent.ts3j.identity;
import java.util.Arrays;
import java.util.Base64;
public final class Uid {
private final byte[] uidBytes;
private final String base64;
public Uid(String base64) {
this.base64 = base64;
this.uidBytes = Base64.getDecoder().decode(base64);
}
public Uid(byte[] bytes) {
this.uidBytes = bytes;
this.base64 = Base64.getEncoder().encodeToString(bytes);
}
public String toBase64() {
return base64;
}
public byte[] toByteArray() {
return uidBytes;
}
@Override
public int hashCode() {
return Arrays.hashCode(toByteArray());
}
@Override
public boolean equals(Object b) {
if (b == null || !(b instanceof Uid)) return false;
return equals((Uid) b);
}
public boolean equals(Uid b) {
return b != null && Arrays.equals(b.toByteArray(), toByteArray());
}
}
@@ -0,0 +1,201 @@
package com.github.manevolent.ts3j.model;
import com.github.manevolent.ts3j.enums.CodecType;
public class Channel {
private final VirtualServer server;
private final int channelId;
private String name;
private CodecType codec;
private int codecQuality;
private int maxClients;
private int maxFamilyClients;
private int order;
private boolean permanent;
private boolean semiPermanent;
private boolean serverDefault;
private boolean hasPassword;
private boolean codecLatencyFactor;
private boolean unencrypted;
private int deleteDelay;
private boolean unlimited;
private boolean familyUnlimited;
private boolean inheritingMaxfamilyUnlimited;
private int neededTalkPower;
private boolean forcingSilence;
private int iconId;
private boolean privated;
public Channel(VirtualServer server, int channelId) {
this.server = server;
this.channelId = channelId;
}
public int getChannelId() {
return channelId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CodecType getCodec() {
return codec;
}
public void setCodec(CodecType codec) {
this.codec = codec;
}
public int getCodecQuality() {
return codecQuality;
}
public void setCodecQuality(int codecQuality) {
this.codecQuality = codecQuality;
}
public int getMaxClients() {
return maxClients;
}
public void setMaxClients(int maxClients) {
this.maxClients = maxClients;
}
public int getMaxFamilyClients() {
return maxFamilyClients;
}
public void setMaxFamilyClients(int maxFamilyClients) {
this.maxFamilyClients = maxFamilyClients;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public boolean isPermanent() {
return permanent;
}
public void setPermanent(boolean permanent) {
this.permanent = permanent;
}
public boolean isSemiPermanent() {
return semiPermanent;
}
public void setSemiPermanent(boolean semiPermanent) {
this.semiPermanent = semiPermanent;
}
public boolean isServerDefault() {
return serverDefault;
}
public void setServerDefault(boolean serverDefault) {
this.serverDefault = serverDefault;
}
public boolean hasPassword() {
return hasPassword;
}
public void setHasPassword(boolean hasPassword) {
this.hasPassword = hasPassword;
}
public boolean isCodecLatencyFactor() {
return codecLatencyFactor;
}
public void setCodecLatencyFactor(boolean codecLatencyFactor) {
this.codecLatencyFactor = codecLatencyFactor;
}
public boolean isUnencrypted() {
return unencrypted;
}
public void setUnencrypted(boolean unencrypted) {
this.unencrypted = unencrypted;
}
public int getDeleteDelay() {
return deleteDelay;
}
public void setDeleteDelay(int deleteDelay) {
this.deleteDelay = deleteDelay;
}
public boolean isUnlimited() {
return unlimited;
}
public void setUnlimited(boolean unlimited) {
this.unlimited = unlimited;
}
public boolean isFamilyUnlimited() {
return familyUnlimited;
}
public void setFamilyUnlimited(boolean familyUnlimited) {
this.familyUnlimited = familyUnlimited;
}
public boolean isInheritingMaxfamilyUnlimited() {
return inheritingMaxfamilyUnlimited;
}
public void setInheritingMaxfamilyUnlimited(boolean inheritingMaxfamilyUnlimited) {
this.inheritingMaxfamilyUnlimited = inheritingMaxfamilyUnlimited;
}
public int getNeededTalkPower() {
return neededTalkPower;
}
public void setNeededTalkPower(int neededTalkPower) {
this.neededTalkPower = neededTalkPower;
}
public boolean isForcingSilence() {
return forcingSilence;
}
public void setForcingSilence(boolean forcingSilence) {
this.forcingSilence = forcingSilence;
}
public int getIconId() {
return iconId;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public boolean isPrivated() {
return privated;
}
public void setPrivated(boolean privated) {
this.privated = privated;
}
public VirtualServer getServer() {
return server;
}
}
@@ -0,0 +1,261 @@
package com.github.manevolent.ts3j.model;
import com.github.manevolent.ts3j.enums.ClientType;
import com.github.manevolent.ts3j.identity.Uid;
import java.util.*;
public class Client {
private final int id;
private final int databaseId;
private final VirtualServer server;
private final Uid uid;
private final ClientType clientType;
private String nickname;
private int channelId;
private int channelGroupId;
private boolean speaking;
private boolean outputMuted, inputMuted;
private boolean recording;
private boolean inputHardware, outputHardware;
private boolean away;
private int talkPower;
private boolean talkRequested;
private boolean talker;
private boolean prioritySpeaker;
private int unreadMessages;
private int neededServerQueryViewPower;
private int iconId;
private boolean channelCommander;
private String country;
private int inheritedChannelGroupChannelId;
private List<Integer> serverGroupIds = new ArrayList<>();
public Client(int id, int databaseId, VirtualServer server, Uid uid, ClientType clientType) {
this.id = id;
this.databaseId = databaseId;
this.server = server;
this.uid = uid;
this.clientType = clientType;
}
public VirtualServer getServer() {
return server;
}
public Uid getUid() {
return uid;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
/**
* Gets the client's current client connection ID.
* @return client ID.
*/
public int getId() {
return id;
}
/**
* Gets the current connected-to channel.
* @return channel currently connected to by the client.
*/
public Channel getChannel() {
return server.getChannelById(getChannelId());
}
/**
* Gets the channel ID currently connected to.
* @return channel currently connected to by the client.
*/
public int getChannelId() {
return channelId;
}
public void setChannelId(int channelId) {
this.channelId = channelId;
}
public boolean isSpeaking() {
return speaking;
}
public void setSpeaking(boolean speaking) {
this.speaking = speaking;
}
public boolean isInputMuted() {
return inputMuted;
}
public void setInputMuted(boolean inputMuted) {
this.inputMuted = inputMuted;
}
public boolean isOutputMuted() {
return outputMuted;
}
public void setOutputMuted(boolean outputMuted) {
this.outputMuted = outputMuted;
}
public boolean hasOutputHardware() {
return outputHardware;
}
public void setOutputHardware(boolean outputHardware) {
this.outputHardware = outputHardware;
}
public boolean hasInputHardware() {
return inputHardware;
}
public void setInputHardware(boolean inputHardware) {
this.inputHardware = inputHardware;
}
public int getDatabaseId() {
return databaseId;
}
public boolean isRecording() {
return recording;
}
public void setRecording(boolean recording) {
this.recording = recording;
}
public boolean isInServerGroup(int serverGroupId) {
return serverGroupIds.contains(serverGroupId);
}
public void setServerGroupIds(Collection<Integer> serverGroupIds) {
synchronized (serverGroupIds) {
this.serverGroupIds.clear();
this.serverGroupIds.addAll(serverGroupIds);
}
}
public void setServerGroupIds(Integer... serverGroupIds) {
setServerGroupIds(Arrays.asList(serverGroupIds));
}
public Iterable<Integer> getServerGroupIds() {
return Collections.unmodifiableList(serverGroupIds);
}
public int getChannelGroupId() {
return channelGroupId;
}
public void setChannelGroupId(int channelGroupId) {
this.channelGroupId = channelGroupId;
}
public boolean isAway() {
return away;
}
public void setAway(boolean away) {
this.away = away;
}
public int getTalkPower() {
return talkPower;
}
public void setTalkPower(int talkPower) {
this.talkPower = talkPower;
}
public boolean isTalkRequested() {
return talkRequested;
}
public void setTalkRequested(boolean talkRequested) {
this.talkRequested = talkRequested;
}
public boolean isTalker() {
return talker;
}
public void setTalker(boolean talker) {
this.talker = talker;
}
public boolean isPrioritySpeaker() {
return prioritySpeaker;
}
public void setPrioritySpeaker(boolean prioritySpeaker) {
this.prioritySpeaker = prioritySpeaker;
}
public int getUnreadMessages() {
return unreadMessages;
}
public void setUnreadMessages(int unreadMessages) {
this.unreadMessages = unreadMessages;
}
public int getNeededServerQueryViewPower() {
return neededServerQueryViewPower;
}
public void setNeededServerQueryViewPower(int neededServerQueryViewPower) {
this.neededServerQueryViewPower = neededServerQueryViewPower;
}
public int getIconId() {
return iconId;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public boolean isChannelCommander() {
return channelCommander;
}
public void setChannelCommander(boolean channelCommander) {
this.channelCommander = channelCommander;
}
public int getInheritedChannelGroupChannelId() {
return inheritedChannelGroupChannelId;
}
public void setInheritedChannelGroupChannelId(int inheritedChannelGroupChannelId) {
this.inheritedChannelGroupChannelId = inheritedChannelGroupChannelId;
}
public ClientType getClientType() {
return clientType;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
@@ -0,0 +1,322 @@
package com.github.manevolent.ts3j.model;
import com.github.manevolent.ts3j.enums.HostBannerMode;
import com.github.manevolent.ts3j.enums.HostMessageMode;
import com.github.manevolent.ts3j.enums.VoiceEncryptionMode;
import com.github.manevolent.ts3j.identity.Uid;
import java.util.*;
import java.util.stream.Collectors;
public class VirtualServer {
private int id;
private String name;
private String nickname;
private String welcomeMessage;
private String platform;
private String version;
private int maxClients;
private long created;
private VoiceEncryptionMode encryptionMode;
private HostMessageMode hostMessageMode;
private int defaultGroupId;
private int defaultChannelGroupId;
private String hostbannerUrl;
private int hostBannerInterval;
private double prioritySpeakerDimmModifier;
private String namePhonetic;
private long iconId;
private String serverIp;
private boolean askForPriviledgeKey;
private HostBannerMode hostBannerMode;
private int defaultTempChannelDeleteDelay;
/**
* Connected clients
*/
private final Map<Integer, Client> clients = new HashMap<>();
/**
* Server channels
*/
private final Map<Integer, Channel> channels = new HashMap<>();
public VirtualServer() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getWelcomeMessage() {
return welcomeMessage;
}
public void setWelcomeMessage(String welcomeMessage) {
this.welcomeMessage = welcomeMessage;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public int getMaxClients() {
return maxClients;
}
public void setMaxClients(int maxClients) {
this.maxClients = maxClients;
}
public long getCreated() {
return created;
}
public void setCreated(long created) {
this.created = created;
}
public VoiceEncryptionMode getEncryptionMode() {
return encryptionMode;
}
public void setEncryptionMode(VoiceEncryptionMode encryptionMode) {
this.encryptionMode = encryptionMode;
}
public HostMessageMode getHostMessageMode() {
return hostMessageMode;
}
public void setHostMessageMode(HostMessageMode hostMessageMode) {
this.hostMessageMode = hostMessageMode;
}
/**
* Gets the default server group ID users are assigned to when connecting to the server for the first time.
* @return default server group ID.
*/
public int getDefaultGroupId() {
return defaultGroupId;
}
public void setDefaultGroupId(int defaultGroupId) {
this.defaultGroupId = defaultGroupId;
}
public int getDefaultChannelGroupId() {
return defaultChannelGroupId;
}
public void setDefaultChannelGroupId(int defaultChannelGroupId) {
this.defaultChannelGroupId = defaultChannelGroupId;
}
public String getHostbannerUrl() {
return hostbannerUrl;
}
public void setHostbannerUrl(String hostbannerUrl) {
this.hostbannerUrl = hostbannerUrl;
}
public int getHostBannerInterval() {
return hostBannerInterval;
}
public void setHostBannerInterval(int hostBannerInterval) {
this.hostBannerInterval = hostBannerInterval;
}
public double getPrioritySpeakerDimmModifier() {
return prioritySpeakerDimmModifier;
}
public void setPrioritySpeakerDimmModifier(double prioritySpeakerDimmModifier) {
this.prioritySpeakerDimmModifier = prioritySpeakerDimmModifier;
}
public String getNamePhonetic() {
return namePhonetic;
}
public void setNamePhonetic(String namePhonetic) {
this.namePhonetic = namePhonetic;
}
public long getIconId() {
return iconId;
}
public void setIconId(long iconId) {
this.iconId = iconId;
}
public String getServerIp() {
return serverIp;
}
public void setServerIp(String serverIp) {
this.serverIp = serverIp;
}
public boolean isAskForPriviledgeKey() {
return askForPriviledgeKey;
}
public void setAskForPriviledgeKey(boolean askForPriviledgeKey) {
this.askForPriviledgeKey = askForPriviledgeKey;
}
public HostBannerMode getHostBannerMode() {
return hostBannerMode;
}
public void setHostBannerMode(HostBannerMode hostBannerMode) {
this.hostBannerMode = hostBannerMode;
}
public int getDefaultTempChannelDeleteDelay() {
return defaultTempChannelDeleteDelay;
}
public void setDefaultTempChannelDeleteDelay(int defaultTempChannelDeleteDelay) {
this.defaultTempChannelDeleteDelay = defaultTempChannelDeleteDelay;
}
/**
* Gets a channel by its numeric ID.
* @param id channel ID.
* @return the corresponding channel, if such a channel exists, null otherwise.
*/
public Channel getChannelById(int id) {
return channels.get(id);
}
/**
* Tries to add a new channel to the server. If the channel ID is already in use, IllegalStateException is thrown.
* @param channel channel to add.
*/
public void updateChannel(Channel channel) {
synchronized (channels) {
this.channels.put(channel.getChannelId(), channel);
}
}
/**
* Gets all channels by a specific name.
* @param name Name to search for.
* @return List of channels with the specified name.
*/
public List<Channel> getChannelsByName(String name) {
return this.channels.values()
.stream()
.filter(x -> x.getName().equalsIgnoreCase(name))
.collect(Collectors.toList());
}
/**
* Gets the first channel by a specific name.
* @param name Name to search for.
* @return List of channels with the specified name.
*/
public Channel getChannelByName(String name) throws IllegalArgumentException {
return getChannelsOrderd()
.stream()
.filter(x -> x.getName().equalsIgnoreCase(name))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("channel not found"));
}
/**
* Gets a real channel list, potentially unordered.
* @return Unordered channel list.
*/
public Collection<Channel> getChannels() {
return Collections.unmodifiableCollection(channels.values());
}
/**
* Gets the channel list as ordered by the server.
* @return Iterable list of ordered channels.
*/
public Collection<Channel> getChannelsOrderd() {
return Collections.unmodifiableCollection(channels.values()
.stream()
.sorted(Comparator.comparingInt(Channel::getOrder))
.collect(Collectors.toList())
);
}
/**
* Gets a list of known clients to the server.
* @return clients
*/
public Collection<Client> getClients() {
throw new UnsupportedOperationException(); // TODO
}
/**
* Gets a list of connected clients.
* @return connected clients.
*/
public Collection<Client> getConnectedClients() {
return Collections.unmodifiableCollection(clients.values());
}
public List<Client> getClientsByUid(Uid uid) {
return getConnectedClients().stream().filter(x -> x.getUid().equals(uid)).collect(Collectors.toList());
}
public void updateConnectedClient(Client client) {
synchronized (clients) {
this.clients.put(client.getId(), client);
}
}
public void removeConnectedClient(Client client) {
synchronized (clients) {
if (this.clients.containsKey(client.getId()))
this.clients.remove(client.getId());
else
throw new IllegalStateException("client ID doesn't exist");
}
}
public Client getClientById(int clientId) {
return this.clients.get(clientId);
}
}
@@ -1,8 +1,7 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.ComplexCommand;
import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
@@ -29,8 +28,8 @@ public class PacketBody2Command extends PacketBody {
return text;
}
public ComplexCommand parse() {
return ComplexCommand.parse(getRole(), getText());
public MultiCommand parse() {
return MultiCommand.parse(getRole(), getText());
}
public void setText(String text) {
@@ -1,7 +1,7 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.ComplexCommand;
import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
@@ -28,8 +28,8 @@ public class PacketBody3CommandLow extends PacketBody {
return text;
}
public ComplexCommand parse() {
return ComplexCommand.parse(getRole(), getText());
public MultiCommand parse() {
return MultiCommand.parse(getRole(), getText());
}
public void setText(String text) {
@@ -1,8 +1,8 @@
package com.github.manevolent.ts3j.protocol.packet.handler.local;
import com.github.manevolent.ts3j.command.CommandHandler;
import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.command.SingleCommand;
import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
@@ -116,7 +116,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
alphaBytes = new byte[10];
new Random().nextBytes(alphaBytes);
SimpleCommand initiv = new SimpleCommand("clientinitiv", ProtocolRole.CLIENT);
SingleCommand initiv = new SingleCommand("clientinitiv", ProtocolRole.CLIENT);
initiv.add(new CommandSingleParameter("alpha", Base64.getEncoder().encodeToString(alphaBytes)));
@@ -142,7 +142,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
}
} else if (packet.getBody() instanceof PacketBody2Command) {
SimpleCommand command = ((PacketBody2Command) packet.getBody()).parse().simplify();
SingleCommand command = ((PacketBody2Command) packet.getBody()).parse().simplifyOne();
if (command.getName().equalsIgnoreCase("initivexpand2")) {
// 3.2.2 initivexpand2 (Client <- Server)
@@ -177,7 +177,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
);
getClient().sendCommand(
new SimpleCommand(
new SingleCommand(
"clientek", ProtocolRole.CLIENT,
new CommandSingleParameter("ek", Base64.getEncoder().encodeToString(keyPair.getKey())),
new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(signature))
@@ -186,7 +186,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
getClient().setSecureParameters(Ts3Crypt.cryptoInit2(license, alphaBytes, beta, keyPair.getValue()));
getClient().sendCommand(new SimpleCommand(
getClient().sendCommand(new SingleCommand(
"clientinit",
ProtocolRole.CLIENT,
new CommandSingleParameter("client_nickname", getClient().getNickname()),
@@ -229,7 +229,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
)
);
getClient().setCommandProcessor(CommandHandler.createLocalClient());
getClient().setCommandProcessor(CommandHandler.createLocalClientHandler());
getClient().setState(ClientConnectionState.RETRIEVING_DATA);
} else if (command.getName().equals("error")) {
getClient().setState(ClientConnectionState.DISCONNECTED);
@@ -2,11 +2,12 @@ package com.github.manevolent.ts3j.protocol.packet.handler.local;
import com.github.manevolent.ts3j.command.CommandProcessException;
import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.ComplexCommand;
import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command;
import com.github.manevolent.ts3j.protocol.packet.PacketBody3CommandLow;
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
@@ -16,7 +17,7 @@ public abstract class LocalClientHandlerFull extends LocalClientHandler {
super(client);
}
protected void handleCommand(ComplexCommand command) throws CommandProcessException {
protected void handleCommand(MultiCommand command) throws CommandProcessException {
CommandProcessor processor = getClient().getCommandProcessor();
if (processor != null) processor.process(getClient(), command);
}
@@ -25,8 +26,9 @@ public abstract class LocalClientHandlerFull extends LocalClientHandler {
public void handlePacket(Packet packet) throws IOException, TimeoutException {
switch (packet.getBody().getType()) {
case COMMAND:
Ts3Debugging.debug(((PacketBody2Command) packet.getBody()).getText());
try {
handleCommand(ComplexCommand.parse(
handleCommand(MultiCommand.parse(
packet.getRole(),
((PacketBody2Command) packet.getBody()).getText()
));
@@ -36,7 +38,7 @@ public abstract class LocalClientHandlerFull extends LocalClientHandler {
break;
case COMMAND_LOW:
try {
handleCommand(ComplexCommand.parse(
handleCommand(MultiCommand.parse(
packet.getRole(),
((PacketBody3CommandLow) packet.getBody()).getText()
));
@@ -2,9 +2,11 @@ package com.github.manevolent.ts3j.protocol.socket.client;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter;
import com.github.manevolent.ts3j.command.response.CommandResponse;
import com.github.manevolent.ts3j.identity.Identity;
import com.github.manevolent.ts3j.model.Client;
import com.github.manevolent.ts3j.model.VirtualServer;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
@@ -43,10 +45,13 @@ public abstract class AbstractTeamspeakClientSocket
private final Map<PacketBodyType, Reassembly> reassemblyQueue = new ConcurrentHashMap<>();
private final Map<String, Object> clientOptions = new ConcurrentHashMap<>();
private final Map<Integer, PacketResponse> sendQueue = new ConcurrentHashMap<>();
private final Map<PacketBodyType, PacketCounter> sendCounter = new ConcurrentHashMap<>();
private final Map<PacketBodyType, PacketCounter> localSendCounter = new ConcurrentHashMap<>();
private final Map<PacketBodyType, PacketCounter> remoteSendCounter = new ConcurrentHashMap<>();
private CommandProcessor commandProcessor;
private VirtualServer virtualServer;
private Identity identity = null;
private ClientConnectionState connectionState = null;
private PacketTransformation transformation = new DefaultPacketTransformation();
@@ -80,10 +85,13 @@ public abstract class AbstractTeamspeakClientSocket
// Initialize counters
for (PacketBodyType bodyType : PacketBodyType.values())
if (bodyType == PacketBodyType.INIT1)
sendCounter.put(PacketBodyType.INIT1, new PacketCounterZero());
else
sendCounter.put(bodyType, new PacketCounterFull());
if (bodyType == PacketBodyType.INIT1) {
localSendCounter.put(PacketBodyType.INIT1, new PacketCounterZero());
remoteSendCounter.put(PacketBodyType.INIT1, new PacketCounterZero());
} else {
localSendCounter.put(bodyType, new PacketCounterFull());
remoteSendCounter.put(bodyType, new PacketCounterFull(-1));
}
}
protected void start() {
@@ -228,7 +236,7 @@ public abstract class AbstractTeamspeakClientSocket
// Set header values
packet.getBody().setHeaderValues(packet.getHeader());
Pair<Integer, Integer> counterResult = sendCounter.get(packet.getHeader().getType()).next();
Pair<Integer, Integer> counterResult = localSendCounter.get(packet.getHeader().getType()).next();
if (packet.getHeader() instanceof ClientPacketHeader)
((ClientPacketHeader) packet.getHeader()).setClientId(clientId);
@@ -460,11 +468,22 @@ public abstract class AbstractTeamspeakClientSocket
}
// Find if the packet must be reassembled
if (packet.getBody() instanceof PacketBodyFragment) {
if (packet.getHeader().getType().isSplittable()) {
packet = reassemblyQueue.get(packet.getHeader().getType()).put(packet);
if (packet == null) continue;
}
if (packet.getHeader().getType().canResend() && packet.getHeader().getType() != PacketBodyType.INIT1)
// Find if we already acknowledged this packet
if (!remoteSendCounter
.get(packet.getHeader().getType())
.setPacketId(packet.getHeader().getPacketId())) {
Ts3Debugging.debug("[PROTOCOL] Ignoring handling resent packet: " +
packet.getHeader().getType().name() + " id=" +
packet.getHeader().getPacketId());
continue; // Drop the packet
}
// Find if the packet is itself an acknowledgement
boolean handle;
PacketResponse response;
@@ -580,6 +599,18 @@ public abstract class AbstractTeamspeakClientSocket
this.commandProcessor = commandProcessor;
}
public VirtualServer getVirtualServer() {
return virtualServer;
}
public void setVirtualServer(VirtualServer virtualServer) {
this.virtualServer = virtualServer;
}
public Client getSelf() {
return virtualServer.getClientById(clientId);
}
private class NetworkHandler implements Runnable {
@Override
public void run() {
@@ -692,6 +723,8 @@ public abstract class AbstractTeamspeakClientSocket
private interface PacketCounter {
Pair<Integer, Integer> next();
int getPacketId();
boolean setPacketId(int packetId);
int getGenerationId();
}
@@ -701,6 +734,16 @@ public abstract class AbstractTeamspeakClientSocket
return new Pair<>(0,0);
}
@Override
public int getPacketId() {
return 0;
}
@Override
public boolean setPacketId(int packetId) {
throw new UnsupportedOperationException();
}
@Override
public int getGenerationId() {
return 0;
@@ -712,10 +755,34 @@ public abstract class AbstractTeamspeakClientSocket
private int packetId = 0;
private int generationId = 0;
public PacketCounterFull() {
}
public PacketCounterFull(int start) {
setPacketId(start);
}
public int getPacketId() {
return packetId;
}
@Override
public boolean setPacketId(int packetId) {
synchronized (sendLock) {
if (this.packetId == packetId) return false;
if (packetId >= 65536) {
packetId = 0;
generationId++;
} else {
this.packetId = packetId;
}
return true;
}
}
public int getGenerationId() {
return generationId;
}
@@ -723,13 +790,7 @@ public abstract class AbstractTeamspeakClientSocket
@Override
public Pair<Integer, Integer> next() {
synchronized (sendLock) {
packetId++;
if (packetId >= 65536) {
packetId = 0;
generationId++;
}
setPacketId(packetId + 1);
return new Pair<>(packetId, generationId);
}
}
@@ -781,6 +842,8 @@ public abstract class AbstractTeamspeakClientSocket
}
public Packet put(Packet packet) {
Packet reassembled;
if (packet.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) {
boolean oldState = state;
@@ -791,19 +854,22 @@ public abstract class AbstractTeamspeakClientSocket
queue.put(packet.getHeader().getPacketId(), packet);
if (oldState)
return reassemble(packet);
return null;
if (oldState) {
reassembled = reassemble(packet);
} else {
reassembled = null;
}
} else {
if (state) {
queue.put(packet.getHeader().getPacketId(), packet);
return null;
reassembled = null;
} else {
return packet;
reassembled = packet;
}
}
return reassembled;
}
public boolean isReassembling() {