Working on commands, having trouble with acks right now

This commit is contained in:
Manevolent
2018-08-01 16:29:31 -04:00
parent 401f00c83d
commit 964936abdf
5 changed files with 360 additions and 64 deletions
@@ -11,6 +11,7 @@ import java.util.*;
public class MultiCommand implements Command { public class MultiCommand implements Command {
private final String name; private final String name;
private final ProtocolRole role; private final ProtocolRole role;
private final List<CommandParameter> staticParameters = new LinkedList<>();
private final List<SingleCommand> commands; private final List<SingleCommand> commands;
public MultiCommand(String name, ProtocolRole role, SingleCommand... params) { public MultiCommand(String name, ProtocolRole role, SingleCommand... params) {
@@ -35,12 +36,12 @@ public class MultiCommand implements Command {
@Override @Override
public Iterable<CommandParameter> getParameters() { public Iterable<CommandParameter> getParameters() {
throw new UnsupportedOperationException(); return staticParameters;
} }
@Override @Override
public void add(CommandParameter parameter) { public void add(CommandParameter parameter) {
throw new UnsupportedOperationException(); staticParameters.add(parameter);
} }
public List<SingleCommand> simplify() { public List<SingleCommand> simplify() {
@@ -58,9 +59,11 @@ public class MultiCommand implements Command {
@Override @Override
public String build() { public String build() {
SingleCommand staticSingle = new SingleCommand(getName(), getRole(), staticParameters);
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append(getName().toLowerCase()); builder.append(staticSingle.build());
List<SingleCommand> singleCommands = simplify(); List<SingleCommand> singleCommands = simplify();
@@ -1,6 +1,7 @@
package com.github.manevolent.ts3j.command.response; package com.github.manevolent.ts3j.command.response;
import com.github.manevolent.ts3j.command.MultiCommand; import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.util.Ts3Debugging;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@@ -11,8 +12,7 @@ import java.util.function.Function;
public abstract class AbstractCommandResponse<T> implements CommandResponse<T> { public abstract class AbstractCommandResponse<T> implements CommandResponse<T> {
private final Function<Iterable<MultiCommand>, T> processor; private final Function<Iterable<MultiCommand>, T> processor;
private CompletableFuture<Iterable<MultiCommand>> future = private final CompletableFuture<Iterable<MultiCommand>> future = new CompletableFuture<>();
new CompletableFuture<>();
public AbstractCommandResponse(Function<Iterable<MultiCommand>, T> processor) { public AbstractCommandResponse(Function<Iterable<MultiCommand>, T> processor) {
this.processor = processor; this.processor = processor;
@@ -36,10 +36,12 @@ public abstract class AbstractCommandResponse<T> implements CommandResponse<T> {
@Override @Override
public T get() public T get()
throws ExecutionException, InterruptedException { throws ExecutionException, InterruptedException {
Ts3Debugging.debug("WAITING ON " + future);
return map(future.get()); return map(future.get());
} }
protected void completeSuccess(Iterable<MultiCommand> commands) { protected void completeSuccess(Iterable<MultiCommand> commands) {
Ts3Debugging.debug("SUCCEEDING " + future);
future.complete(commands); future.complete(commands);
} }
@@ -373,7 +373,8 @@ public abstract class AbstractTeamspeakClientSocket
packet.getHeader().getPacketId(), packet.getHeader().getPacketId(),
response = new PacketResponse( response = new PacketResponse(
networkPacket, networkPacket,
networkPacket.getHeader().getType().canResend() ? Integer.MAX_VALUE : 0 sendQueue,
networkPacket.getHeader().getType().canResend() ? 10 : 0
) )
); );
else else
@@ -560,20 +561,6 @@ public abstract class AbstractTeamspeakClientSocket
} }
} }
if (packet.getHeader().getType().canResend() &&
packet.getHeader().getType() != PacketBodyType.INIT1) {
// Find if we already acknowledged this packet
if (!counter.put(packet.getHeader().getPacketId())) {
Ts3Debugging.debug("[PROTOCOL] Ignoring handling resent packet: " +
packet + " " +
packet.getHeader().getType().name() + " id=" +
packet.getHeader().getPacketId() + " generation=" +
packet.getHeader().getGeneration());
continue; // Drop the packet
}
}
// Find if the packet must be reassembled // Find if the packet must be reassembled
if (packet.getHeader().getType().isSplittable()) { if (packet.getHeader().getType().isSplittable()) {
packet = reassemblyQueue.get(packet.getHeader().getType()).put(packet); packet = reassemblyQueue.get(packet.getHeader().getType()).put(packet);
@@ -582,7 +569,7 @@ public abstract class AbstractTeamspeakClientSocket
// Find if the packet is itself an acknowledgement // Find if the packet is itself an acknowledgement
boolean handle; boolean handle;
PacketResponse response; final PacketResponse response;
switch (packet.getHeader().getType()) { switch (packet.getHeader().getType()) {
case ACK: case ACK:
response = sendQueue.get(((PacketBody6Ack)packet.getBody()).getPacketId()); response = sendQueue.get(((PacketBody6Ack)packet.getBody()).getPacketId());
@@ -601,6 +588,11 @@ public abstract class AbstractTeamspeakClientSocket
response = null; response = null;
} }
if (packet.getHeader().getType().canResend() && packet.getHeader().getType() != PacketBodyType.INIT1) {
// Find if we already acknowledged this packet
handle = handle & counter.put(packet.getHeader().getPacketId());
}
if (response != null) { if (response != null) {
response.acknowledge(packet); response.acknowledge(packet);
} else if (handle) { } else if (handle) {
@@ -737,12 +729,14 @@ public abstract class AbstractTeamspeakClientSocket
private class PacketResponse { private class PacketResponse {
private final NetworkPacket sentPacket; private final NetworkPacket sentPacket;
private final CompletableFuture<Packet> future = new CompletableFuture<>(); private final CompletableFuture<Packet> future = new CompletableFuture<>();
private final Map<Integer, PacketResponse> responsibleQueue;
private long lastSent = 0L; private long lastSent = 0L;
private int tries = 1, maxTries; private int tries = 1, maxTries;
private boolean willResend; private boolean willResend;
public PacketResponse(NetworkPacket sentPacket,int maxTries) { public PacketResponse(NetworkPacket sentPacket, Map<Integer, PacketResponse> responsibleQueue, int maxTries) {
this.sentPacket = sentPacket; this.sentPacket = sentPacket;
this.responsibleQueue = responsibleQueue;
this.maxTries = maxTries; this.maxTries = maxTries;
this.lastSent = System.currentTimeMillis(); this.lastSent = System.currentTimeMillis();
this.willResend = maxTries > 0; this.willResend = maxTries > 0;
@@ -801,6 +795,7 @@ public abstract class AbstractTeamspeakClientSocket
} }
public void acknowledge(Packet p) { public void acknowledge(Packet p) {
responsibleQueue.remove(sentPacket.getHeader().getPacketId());
future.complete(p); future.complete(p);
} }
@@ -1,15 +1,18 @@
package com.github.manevolent.ts3j.protocol.socket.client; package com.github.manevolent.ts3j.protocol.socket.client;
import com.github.manevolent.ts3j.api.Ban;
import com.github.manevolent.ts3j.api.Channel; import com.github.manevolent.ts3j.api.Channel;
import com.github.manevolent.ts3j.api.Client;
import com.github.manevolent.ts3j.api.Permission;
import com.github.manevolent.ts3j.audio.Microphone; import com.github.manevolent.ts3j.audio.Microphone;
import com.github.manevolent.ts3j.command.*; import com.github.manevolent.ts3j.command.*;
import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter; import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter;
import com.github.manevolent.ts3j.command.response.AbstractCommandResponse; import com.github.manevolent.ts3j.command.response.AbstractCommandResponse;
import com.github.manevolent.ts3j.command.response.CommandResponse; import com.github.manevolent.ts3j.command.response.CommandResponse;
import com.github.manevolent.ts3j.enums.CodecType;
import com.github.manevolent.ts3j.event.TS3Event; import com.github.manevolent.ts3j.event.TS3Event;
import com.github.manevolent.ts3j.event.TS3Listener; import com.github.manevolent.ts3j.event.TS3Listener;
import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.identity.Uid;
import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.ProtocolRole; import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
@@ -24,8 +27,11 @@ import java.net.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
public class LocalTeamspeakClientSocket public class LocalTeamspeakClientSocket
extends AbstractTeamspeakClientSocket extends AbstractTeamspeakClientSocket
@@ -53,7 +59,7 @@ public class LocalTeamspeakClientSocket
private int acceptingReturnCode = 0; private int acceptingReturnCode = 0;
private int lastReturnCode = 0; private int lastReturnCode = 0;
private Map<Integer, CommandProcessor> awaitingCommands = new HashMap<>(); private Map<Integer, CommandProcessor> awaitingCommands = new ConcurrentHashMap<>();
public LocalTeamspeakClientSocket() { public LocalTeamspeakClientSocket() {
super(); super();
@@ -161,7 +167,7 @@ public class LocalTeamspeakClientSocket
0.020F, 0.020F,
new MicrophoneTask() new MicrophoneTask()
); );
microphoneThread.setDaemon(true);
microphoneThread.start(); microphoneThread.start();
} catch (TimeoutException e) { } catch (TimeoutException e) {
setState(ClientConnectionState.DISCONNECTED); setState(ClientConnectionState.DISCONNECTED);
@@ -229,20 +235,24 @@ public class LocalTeamspeakClientSocket
) throws IOException, TimeoutException { ) throws IOException, TimeoutException {
int returnCode; int returnCode;
ClientCommandResponse<T> response; final ClientCommandResponse<T> response;
// sync may not be necessary here // sync may not be necessary here
synchronized (commandSendLock) { synchronized (commandSendLock) {
returnCode = lastReturnCode++; returnCode = lastReturnCode++;
awaitingCommands.put(returnCode, response = new ClientCommandResponse<>(returnCode, processor)); awaitingCommands.put(returnCode, response = new ClientCommandResponse<>(returnCode, processor));
acceptingReturnCode = calculateAcceptingReturnCode();
} }
try {
command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode))); command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode)));
try {
writePacket(new PacketBody2Command(getRole().getOut(), command)); writePacket(new PacketBody2Command(getRole().getOut(), command));
} catch (Throwable e) { } catch (Throwable e) {
synchronized (commandSendLock) {
awaitingCommands.remove(returnCode); awaitingCommands.remove(returnCode);
acceptingReturnCode = calculateAcceptingReturnCode();
}
throw e; throw e;
} }
@@ -255,16 +265,14 @@ public class LocalTeamspeakClientSocket
Function<SingleCommand, T> processor Function<SingleCommand, T> processor
) throws IOException, TimeoutException { ) throws IOException, TimeoutException {
int returnCode; int returnCode;
final ClientCommandResponse<Iterable<T>> response;
// sync may not be necessary here // sync may not be necessary here
synchronized (commandSendLock) { synchronized (commandSendLock) {
returnCode = lastReturnCode++; returnCode = lastReturnCode++;
}
command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode))); command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode)));
ClientCommandResponse<Iterable<T>> response;
awaitingCommands.put(returnCode, response = new ClientCommandResponse<>(returnCode, multiCommands -> { awaitingCommands.put(returnCode, response = new ClientCommandResponse<>(returnCode, multiCommands -> {
List<SingleCommand> commands = new LinkedList<>(); List<SingleCommand> commands = new LinkedList<>();
for (MultiCommand multiCommand : multiCommands) commands.addAll(multiCommand.simplify()); for (MultiCommand multiCommand : multiCommands) commands.addAll(multiCommand.simplify());
@@ -276,11 +284,18 @@ public class LocalTeamspeakClientSocket
return results; return results;
})); }));
acceptingReturnCode = calculateAcceptingReturnCode();
}
try { try {
writePacket(new PacketBody2Command(getRole().getOut(), command)); writePacket(new PacketBody2Command(getRole().getOut(), command));
} catch (Throwable e) { } catch (Throwable e) {
synchronized (commandSendLock) {
awaitingCommands.remove(returnCode); awaitingCommands.remove(returnCode);
acceptingReturnCode = calculateAcceptingReturnCode();
}
throw e; throw e;
} }
@@ -302,6 +317,13 @@ public class LocalTeamspeakClientSocket
} }
} }
private int calculateAcceptingReturnCode() {
int lowest = Integer.MAX_VALUE;
for (Integer integer : awaitingCommands.keySet()) lowest = Math.min(lowest, integer);
return lowest;
}
private class MicrophoneTask implements Runnable { private class MicrophoneTask implements Runnable {
@Override @Override
public void run() { public void run() {
@@ -349,10 +371,30 @@ public class LocalTeamspeakClientSocket
public void process(AbstractTeamspeakClientSocket client, public void process(AbstractTeamspeakClientSocket client,
MultiCommand multiCommand) MultiCommand multiCommand)
throws CommandProcessException { throws CommandProcessException {
if (multiCommand.getName().equalsIgnoreCase("error")) { if (multiCommand.getName().equalsIgnoreCase("error"))
SingleCommand command = multiCommand.simplifyFirst(); handleError(multiCommand.simplifyOne());
else
commands.add(multiCommand);
}
@Override
public void process(AbstractTeamspeakClientSocket client,
SingleCommand command)
throws CommandProcessException {
if (command.getName().equalsIgnoreCase("error"))
handleError(command);
else
commands.add(new MultiCommand(command.getName(), ProtocolRole.CLIENT, command));
}
private void handleError(SingleCommand command) {
int errorId = Integer.parseInt(command.get("id").getValue()); int errorId = Integer.parseInt(command.get("id").getValue());
synchronized (awaitingCommands) {
awaitingCommands.remove(returnCode);
acceptingReturnCode = calculateAcceptingReturnCode();
}
switch (errorId) { switch (errorId) {
case 0: case 0:
completeSuccess(commands); completeSuccess(commands);
@@ -361,12 +403,6 @@ public class LocalTeamspeakClientSocket
completeFailure(new CommandException(command.get("msg").getValue(), errorId)); completeFailure(new CommandException(command.get("msg").getValue(), errorId));
break; break;
} }
synchronized (awaitingCommands) {
awaitingCommands.remove(returnCode);
}
} else
commands.add(multiCommand);
} }
public int getReturnCode() { public int getReturnCode() {
@@ -399,4 +435,250 @@ public class LocalTeamspeakClientSocket
} }
} }
} }
/**
* Commands
*/
public void addBan(String ip, String name, Uid uid, Integer timeInSeconds, String reason)
throws IOException, TimeoutException, ExecutionException, InterruptedException, CommandException {
Command banCommand = new SingleCommand("banadd", ProtocolRole.CLIENT);
if (ip != null) banCommand.add(new CommandSingleParameter("ip", ip));
if (name != null) banCommand.add(new CommandSingleParameter("name", name));
if (uid != null) banCommand.add(new CommandSingleParameter("uid", uid.toBase64()));
if (timeInSeconds != null) banCommand.add(new CommandSingleParameter("time", timeInSeconds.toString()));
if (reason != null) banCommand.add(new CommandSingleParameter("banreason", reason));
executeCommand(banCommand).complete();
}
public Iterable<Ban> banClient(int clid, Integer timeInSeconds, String reason)
throws IOException, TimeoutException, ExecutionException, InterruptedException, CommandException {
Command banCommand = new SingleCommand("banclient", ProtocolRole.CLIENT);
banCommand.add(new CommandSingleParameter("clid", Integer.toString(clid)));
if (timeInSeconds != null) banCommand.add(new CommandSingleParameter("time", timeInSeconds.toString()));
if (reason != null) banCommand.add(new CommandSingleParameter("banreason", reason));
return executeMappedCommand(banCommand, command -> new Ban(command.toMap())).get();
}
public Iterable<Ban> banDatabaseClient(int dbid, Integer timeInSeconds, String reason)
throws IOException, TimeoutException, ExecutionException, InterruptedException, CommandException {
Command banCommand = new SingleCommand("banclient", ProtocolRole.CLIENT);
banCommand.add(new CommandSingleParameter("cldbid", Integer.toString(dbid)));
if (timeInSeconds != null) banCommand.add(new CommandSingleParameter("time", timeInSeconds.toString()));
if (reason != null) banCommand.add(new CommandSingleParameter("banreason", reason));
return executeMappedCommand(banCommand, command -> new Ban(command.toMap())).get();
}
public Iterable<Ban> banClient(Uid uid, Integer timeInSeconds, String reason)
throws IOException, TimeoutException, ExecutionException, InterruptedException, CommandException {
Command banCommand = new SingleCommand("banclient", ProtocolRole.CLIENT);
banCommand.add(new CommandSingleParameter("uid", uid.toBase64()));
if (timeInSeconds != null) banCommand.add(new CommandSingleParameter("time", timeInSeconds.toString()));
if (reason != null) banCommand.add(new CommandSingleParameter("banreason", reason));
return executeMappedCommand(banCommand, command -> new Ban(command.toMap())).get();
}
public Iterable<Ban> banClient(Client client, Integer timeInSeconds, String reason)
throws InterruptedException, ExecutionException, TimeoutException, CommandException, IOException {
return banClient(client.getId(), timeInSeconds, reason);
}
public void deleteBan(int banId)
throws IOException, TimeoutException, ExecutionException, InterruptedException, CommandException {
Command banCommand = new SingleCommand("deleteban", ProtocolRole.CLIENT);
banCommand.add(new CommandSingleParameter("banid", Integer.toString(banId)));
executeCommand(banCommand).complete();
}
public void deleteAllBans()
throws IOException, TimeoutException, ExecutionException, InterruptedException, CommandException {
executeCommand(new SingleCommand("bandelall", ProtocolRole.CLIENT)).complete();
}
// TODO notifybanlist is returned instead
public Iterable<Ban> listBans()
throws InterruptedException, ExecutionException, TimeoutException, CommandException, IOException {
Command banCommand = new SingleCommand("banlist", ProtocolRole.CLIENT);
return executeMappedCommand(banCommand, command -> new Ban(command.toMap())).get();
}
public void deleteBan(Ban ban)
throws InterruptedException, ExecutionException, TimeoutException, CommandException, IOException {
deleteBan(ban.getId());
}
public CommandResponse<Iterable<MultiCommand>> channelAddPermission(int channelId, Permission... permission)
throws IOException, TimeoutException {
MultiCommand command = new MultiCommand(
"channeladdperm",
ProtocolRole.CLIENT,
Arrays.stream(permission).map(x ->
new SingleCommand("channeladdperm", ProtocolRole.CLIENT, // name is redundant here
new CommandSingleParameter("permsid", x.getName()),
new CommandSingleParameter("permvalue", Integer.toString(x.getValue()))
)).collect(Collectors.toList()
));
command.add(new CommandSingleParameter("cid", Integer.toString(channelId)));
return executeCommand(command);
}
public CommandResponse<Iterable<MultiCommand>> channelClientAddPermission(int channelId,
int clientDatabaseId,
Permission... permission)
throws IOException, TimeoutException {
MultiCommand command = new MultiCommand(
"channelclientaddperm",
ProtocolRole.CLIENT,
Arrays.stream(permission).map(x ->
new SingleCommand("channelclientaddperm", ProtocolRole.CLIENT, // name is redundant here
new CommandSingleParameter("permsid", x.getName()),
new CommandSingleParameter("permvalue", Integer.toString(x.getValue()))
)).collect(Collectors.toList()
));
command.add(new CommandSingleParameter("cid", Integer.toString(channelId)));
command.add(new CommandSingleParameter("cldbid", Integer.toString(channelId)));
return executeCommand(command);
}
public void channelClientDeletePermission(int channelId,
int clientDatabaseId,
Permission... permission)
throws IOException, TimeoutException, ExecutionException, InterruptedException {
MultiCommand command = new MultiCommand(
"channeldelperm",
ProtocolRole.CLIENT,
Arrays.stream(permission).map(x ->
new SingleCommand("channeldelperm", ProtocolRole.CLIENT, // name is redundant here
new CommandSingleParameter("permsid", x.getName()),
new CommandSingleParameter("permvalue", Integer.toString(x.getValue()))
)).collect(Collectors.toList()
));
command.add(new CommandSingleParameter("cid", Integer.toString(channelId)));
command.add(new CommandSingleParameter("cldbid", Integer.toString(channelId)));
executeCommand(command).complete();
}
/**
* Displays a list of clients that are in the channel specified by the cid
parameter. Included information is the clientID, client database id, nickname,
channelID and client type.
Please take note that you can only view clients in channels that you are
currently subscribed to.
Here is a list of the additional display paramters you will receive for
each of the possible modifier parameters.
* @param channelId channel ID to look at
* @return list of channel clients
* @throws IOException
* @throws TimeoutException
*/
// TODO command not found
public Iterable<Client> listChannelClients(int channelId)
throws InterruptedException, ExecutionException, TimeoutException, CommandException, IOException {
Command command = new SingleCommand("channelclientlist", ProtocolRole.CLIENT);
command.add(new CommandSingleParameter("cid", Integer.toString(channelId)));
return executeMappedCommand(command, x -> new Client(x.toMap())).get();
}
/**
* Moves one or more clients specified with clid to the channel with ID cid. If
the target channel has a password, it needs to be specified with cpw. If the
channel has no password, the parameter can be omitted.
*/
public void clientMove(int clientId, int channelId, String password)
throws IOException, TimeoutException, ExecutionException, InterruptedException {
Command command = new SingleCommand("clientmove", ProtocolRole.CLIENT);
command.add(new CommandSingleParameter("cid", Integer.toString(channelId)));
if (password != null) command.add(new CommandSingleParameter("cpw", Integer.toString(channelId)));
command.add(new CommandSingleParameter("clid", Integer.toString(clientId)));
executeCommand(command).complete();
}
public void joinChannel(int channelId, String password)
throws InterruptedException, ExecutionException, TimeoutException, IOException {
clientMove(getClientId(), channelId, password);
}
/**
* Moves one or more clients specified with clid to the channel with ID cid. If
the target channel has a password, it needs to be specified with cpw. If the
channel has no password, the parameter can be omitted.
*/
public Iterable<Channel> listChannels()
throws IOException, TimeoutException, ExecutionException, InterruptedException {
Command command = new SingleCommand("channellist", ProtocolRole.CLIENT);
return executeMappedCommand(command, x -> new Channel(x.toMap())).get();
}
/**
* Moves one or more clients specified with clid to the channel with ID cid. If
the target channel has a password, it needs to be specified with cpw. If the
channel has no password, the parameter can be omitted.
*/
public Iterable<Client> listClients()
throws IOException, TimeoutException, ExecutionException, InterruptedException {
Command command = new SingleCommand("clientlist", ProtocolRole.CLIENT);
return executeMappedCommand(command, x -> new Client(x.toMap())).get();
}
/**
* Moves one or more clients specified with clid to the channel with ID cid. If
the target channel has a password, it needs to be specified with cpw. If the
channel has no password, the parameter can be omitted.
*/
public void clientPoke(int clientId, String message)
throws IOException, TimeoutException, ExecutionException, InterruptedException {
Command command = new SingleCommand("clientpoke", ProtocolRole.CLIENT);
command.add(new CommandSingleParameter("clid", Integer.toString(clientId)));
command.add(new CommandSingleParameter("msg", message));
executeCommand(command).complete();
}
/**
* Kicks one or more clients specified with clid from their currently joined
channel or from the server, depending on reasonid. The reasonmsg parameter
specifies a text message sent to the kicked clients. This parameter is optional
and may only have a maximum of 40 characters.
*/
public void kick(Collection<Integer> clientIds, String message)
throws IOException, TimeoutException, ExecutionException, InterruptedException {
MultiCommand command = new MultiCommand(
"clientkick",
ProtocolRole.CLIENT,
clientIds.stream().map(x ->
new SingleCommand("clientkick", ProtocolRole.CLIENT, // name is redundant here
new CommandSingleParameter("clid", Integer.toString(x))
)).collect(Collectors.toList()
));
command.add(new CommandSingleParameter("reasonid", Integer.toString(5)));
command.add(new CommandSingleParameter("reasonmsg", message));
executeCommand(command).complete();
}
} }
@@ -1,9 +1,13 @@
package com.github.manevolent.ts3j; package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.api.Ban;
import com.github.manevolent.ts3j.api.Channel;
import com.github.manevolent.ts3j.api.Client;
import com.github.manevolent.ts3j.audio.Microphone; import com.github.manevolent.ts3j.audio.Microphone;
import com.github.manevolent.ts3j.command.SingleCommand; import com.github.manevolent.ts3j.command.SingleCommand;
import com.github.manevolent.ts3j.enums.CodecType; import com.github.manevolent.ts3j.enums.CodecType;
import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.identity.Uid;
import com.github.manevolent.ts3j.protocol.ProtocolRole; import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
@@ -15,6 +19,7 @@ import java.math.BigInteger;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Base64; import java.util.Base64;
import java.util.Collections; import java.util.Collections;
import java.util.Random;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -24,11 +29,15 @@ public class ServerConnectionTest {
try { try {
Ts3Debugging.setEnabled(true); Ts3Debugging.setEnabled(true);
LocalIdentity localIdentity = LocalIdentity.generateNew(10); LocalIdentity identity = LocalIdentity.load(
new BigInteger(Base64.getDecoder().decode("Tj6YXM3qyRv8n25L2pH+OEJnRUl4auQf8+znjYrOmWU="))
);
identity.improveSecurity(10);
LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket(); LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket();
client.setIdentity(localIdentity); client.setIdentity(identity);
client.setNickname("Hello from Java"); client.setNickname("Hello from Java");
@@ -45,6 +54,11 @@ public class ServerConnectionTest {
while (true) { while (true) {
Thread.sleep(1000L); Thread.sleep(1000L);
for (Channel channel : client.listChannels()) {
Ts3Debugging.debug(channel.getName());
}
} }
} catch (Throwable ex) { } catch (Throwable ex) {
ex.printStackTrace(); ex.printStackTrace();