Working on commands, having trouble with acks right now
This commit is contained in:
@@ -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();
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-17
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+322
-40
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode)));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode)));
|
||||||
|
|
||||||
writePacket(new PacketBody2Command(getRole().getOut(), command));
|
writePacket(new PacketBody2Command(getRole().getOut(), command));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
awaitingCommands.remove(returnCode);
|
synchronized (commandSendLock) {
|
||||||
|
awaitingCommands.remove(returnCode);
|
||||||
|
acceptingReturnCode = calculateAcceptingReturnCode();
|
||||||
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -255,31 +265,36 @@ 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)));
|
||||||
|
|
||||||
|
awaitingCommands.put(returnCode, response = new ClientCommandResponse<>(returnCode, multiCommands -> {
|
||||||
|
List<SingleCommand> commands = new LinkedList<>();
|
||||||
|
for (MultiCommand multiCommand : multiCommands) commands.addAll(multiCommand.simplify());
|
||||||
|
|
||||||
|
List<T> results = new LinkedList<>();
|
||||||
|
for (SingleCommand command1 : commands)
|
||||||
|
results.add(processor.apply(command1));
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}));
|
||||||
|
|
||||||
|
acceptingReturnCode = calculateAcceptingReturnCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode)));
|
|
||||||
|
|
||||||
ClientCommandResponse<Iterable<T>> response;
|
|
||||||
|
|
||||||
awaitingCommands.put(returnCode, response = new ClientCommandResponse<>(returnCode, multiCommands -> {
|
|
||||||
List<SingleCommand> commands = new LinkedList<>();
|
|
||||||
for (MultiCommand multiCommand : multiCommands) commands.addAll(multiCommand.simplify());
|
|
||||||
|
|
||||||
List<T> results = new LinkedList<>();
|
|
||||||
for (SingleCommand command1 : commands)
|
|
||||||
results.add(processor.apply(command1));
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writePacket(new PacketBody2Command(getRole().getOut(), command));
|
writePacket(new PacketBody2Command(getRole().getOut(), command));
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
awaitingCommands.remove(returnCode);
|
synchronized (commandSendLock) {
|
||||||
|
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,26 +371,40 @@ 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());
|
||||||
int errorId = Integer.parseInt(command.get("id").getValue());
|
else
|
||||||
|
|
||||||
switch (errorId) {
|
|
||||||
case 0:
|
|
||||||
completeSuccess(commands);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
completeFailure(new CommandException(command.get("msg").getValue(), errorId));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (awaitingCommands) {
|
|
||||||
awaitingCommands.remove(returnCode);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
commands.add(multiCommand);
|
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());
|
||||||
|
|
||||||
|
synchronized (awaitingCommands) {
|
||||||
|
awaitingCommands.remove(returnCode);
|
||||||
|
acceptingReturnCode = calculateAcceptingReturnCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (errorId) {
|
||||||
|
case 0:
|
||||||
|
completeSuccess(commands);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
completeFailure(new CommandException(command.get("msg").getValue(), errorId));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getReturnCode() {
|
public int getReturnCode() {
|
||||||
return returnCode;
|
return returnCode;
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user