Closed PeXArtZ closed 5 years ago
Are you creating a ScheduledThreadPoolExecutor
each time a client joins or leaves? O.o
Also, if you're accessing that "ints" variable from multiple threads (which you undoubtedly are if you keep creating new executors), you're going to run into concurrency issues, specifically data races.
But most likely, the cause of this issue is because you're re-registering the same event listener multiple times. Make sure that over the entire lifecycle of your TS3Query, you only register this specific event listener once.
The Thread was only for test purposes. Normally, Im using the last line in my post. I registered every event in a 'events.java' and got a new .java for every function. I noticed, that the bot is much slower, when I take all my code and paste it in one file. Im trying to register every event once, hope this works.
you register the event for server, and not for channel
I dont got much experience with java. You said "re-registering the same event listener multiple times". Do you mean:
Load.api.addTS3Listeners(new TS3Listener()
or
Load.api.registerAllEvents();
or
Load.api.addTS3Listeners(new TS3EventAdapter()
I've registered all events now in my main file, but the error is still there.
The first and third one. A TS3EventAdapter
is just a TS3Listener
with all methods being empty, so you don't have to override every single one.
Anyway - if you're new to Java, it would probably be a good idea if we could see the entirety of your code, as the error is most likely hiding in there somewhere ๐
Alright. Heres my code of the online counter:
`package de.frostiqz.cloudbot.events;
import com.github.theholywaffle.teamspeak3.api.ChannelProperty; import com.github.theholywaffle.teamspeak3.api.event.ClientJoinEvent; import com.github.theholywaffle.teamspeak3.api.event.ClientLeaveEvent; import com.github.theholywaffle.teamspeak3.api.event.TS3EventAdapter; import com.github.theholywaffle.teamspeak3.api.exception.TS3CommandFailedException; import de.frostiqz.cloudbot.Load; import de.frostiqz.cloudbot.storage.Data;
public class OnlineCount {
public static void start() {
Load.api.registerEvent(TS3EventType.SERVER);
Load.api.addTS3Listeners(new TS3EventAdapter() {
@Override
public void onClientJoin(ClientJoinEvent e) {
try {
Load.api.editChannel(5, ChannelProperty.CHANNEL_NAME , "[cspacer]ยป Aktuell Online: " + (Load.api.getHostInfo().getTotalClientsOnline()) + "/32 ยซ");
} catch (TS3CommandFailedException ex) {
System.out.println(Data.getPrefix() + "Fehler beim OnlineCounter (ClientJoin)");
System.err.println(ex.getMessage());
}
}
@Override
public void onClientLeave(ClientLeaveEvent e) {
// Online Count
try {
Load.api.editChannel(5, ChannelProperty.CHANNEL_NAME , "[cspacer]ยป Aktuell Online: " + (Load.api.getHostInfo().getTotalClientsOnline()) + "/32 ยซ");
} catch (TS3CommandFailedException ex) {
System.out.println(Data.getPrefix() + "Fehler beim OnlineCounter (ClientLeave)");
System.err.println(ex.getMessage());
}
}
});
}
}`
That's pretty much still the same code shown above. I'm much more interested in the code that calls this OnlineCount.start()
method of yours. And the code that calls that method. And so on, until we land in main(String[] args)
๐
(Also, use triple-backticks ( ` *3) for a proper multi-line code block)
Its my main method, which starts the OnlineCount. Heres the code (I deleted some stuff like passwords, etc):
package de.frostiqz.cloudbot;
import com.github.theholywaffle.teamspeak3.TS3Api;
import com.github.theholywaffle.teamspeak3.TS3Config;
import com.github.theholywaffle.teamspeak3.TS3Query;
import com.github.theholywaffle.teamspeak3.api.wrapper.Client;
import de.frostiqz.cloudbot.console.Console;
import de.frostiqz.cloudbot.events.*;
import de.frostiqz.cloudbot.storage.Data;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
public class Load {
public static final TS3Config config = new TS3Config();
public static final TS3Query query = new TS3Query(config);
public static final TS3Api api = query.getApi();
public static final ArrayList<String> words = new ArrayList<>();
public static void main(String[] args) throws IOException, URISyntaxException {
// Start Screen
System.out.println("");
System.out.println(" _____ _ _ _ _ _ _____ __ __");
System.out.println(" / ____| | | | | | | | | | __ \\ \\ \\ / /");
System.out.println(" | | | | ___ _ _ __| | |__ ___ | |_ | |__ _ _ | |__) |__ \\ V / ");
System.out.println(" | | | |/ _ \\| | | |/ _` | '_ \\ / _ \\| __| | '_ \\| | | | | ___/ _ \\ > < ");
System.out.println(" | |____| | (_) | |_| | (_| | |_) | (_) | |_ | |_) | |_| | | | | __// . \\ ");
System.out.println(" \\_____|_|\\___/ \\__,_|\\__,_|_.__/ \\___/ \\__| |_.__/ \\__, | |_| \\___/_/ \\_\\");
System.out.println(" __/ | ");
System.out.println(" |___/ ");
System.out.println("");
System.out.println(Data.getPrefix() + "Cloudbot by Tobias - PeX");
System.out.println("");
// Starting the app
System.out.println(Data.getPrefix() + "Cloudbot starting ...");
// Setting the Host IP
config.setHost("127.0.0.1");
System.out.println(Data.getPrefix() + "Host IP has been set to localhost ... ");
// Query is connecting
query.connect();
System.out.println(Data.getPrefix() + "Query connected ...");
// Setting the floodrate to unlimited
config.setFloodRate(TS3Query.FloodRate.UNLIMITED);
System.out.println(Data.getPrefix() + "Floodrate has been set to unlimited ...");
// Query is logging in
api.login("serveradmin", "password");
System.out.println(Data.getPrefix() + "API logged in ...");
// Selecting the virtual server
api.selectVirtualServerByPort(9987);
System.out.println(Data.getPrefix() + "Virtual Server selected ...");
// Setting the nickname
api.setNickname("Frostiqz");
System.out.println(Data.getPrefix() + "Nickname set ...");
// Loading the events
Load.api.registerAllEvents();
Events.loadEvents();
System.out.println(Data.getPrefix() + "Events loaded ...");
// Loading the AFKMover
AFKMover.start();
System.out.println(Data.getPrefix() + "AFKMover loaded ...");
// Loading the forbidden words and nickname checker
addWords();
checkNickname.start();
System.out.println(Data.getPrefix() + "Nickname Checker loaded ...");
// Loading the chatbot
ChatBot.start();
System.out.println(Data.getPrefix() + "Chatbot loaded ...");
// Loading OnlineCount
OnlineCount.start();
System.out.println(Data.getPrefix() + "OnlineCount loaded ...");
// Loading Support
Support.start();
System.out.println(Data.getPrefix() + "Support loaded ...");
// Loading the welcome message
WelcomeMessage.start();
System.out.println(Data.getPrefix() + "Welcome message loaded ...");
System.out.println("");
System.out.println(Data.getPrefix() + "Cloudbot sucessfully loaded!");
// Starting the console manager
System.out.println(Data.getPrefix() + "The console manager started. You can now start typing in commands.");
Console.start();
}
// forbidden words
public static void addWords() {
// Some pretty bad words :(
}
// Nickname Checker
public static void CheckClient(Client c) {
String name = c.getNickname().toLowerCase();
if (words.contains(name)) {
api.kickClientFromServer("Der Nickname " + c.getNickname() + " ist auf diesem Server nicht erlaubt!", c);
// Console Log
System.out.println(Data.getPrefix() + c.getNickname() + " got kicked for bad Nickname");
}
}
// Channel Checker
public static void CheckChannel(String id, String name) {
Client c = api.getClientByUId(id);
if (words.contains(name.toLowerCase())) {
api.kickClientFromServer("Dieser Channel-Name ist auf diesem Server nicht erlaubt!", c);
// Console Log
System.out.println(Data.getPrefix() + c.getNickname() + " got kicked for bad Channel name");
}
}
}
I got an events.java too, here is it:
package de.frostiqz.cloudbot.events;
import com.github.theholywaffle.teamspeak3.api.event.*;
import de.frostiqz.cloudbot.Load;
import de.frostiqz.cloudbot.storage.Data;
public class Events {
public static void loadEvents() {
Load.api.addTS3Listeners(new TS3Listener() {
@Override
public void onChannelCreate(ChannelCreateEvent e) {
// Channel Checker
Load.CheckChannel(e.getInvokerUniqueId(), Load.api.getChannelInfo(e.getChannelId()).getName());
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " created a channel named " + Load.api.getChannelInfo(e.getChannelId()).getName());
}
@Override
public void onChannelDeleted(ChannelDeletedEvent e) {
//Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " deleted a channel named " + Load.api.getChannelInfo(e.getChannelId()).getName());
}
@Override
public void onChannelDescriptionChanged(ChannelDescriptionEditedEvent e) {
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " changed the channel description of " + Load.api.getChannelInfo(e.getChannelId()).getName());
}
@Override
public void onChannelEdit(ChannelEditedEvent e) {
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " edited the channel " + Load.api.getChannelInfo(e.getChannelId()).getName());
}
@Override
public void onChannelMoved(ChannelMovedEvent e) {
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " moved the channel " + Load.api.getChannelInfo(e.getChannelId()).getName());
}
@Override
public void onChannelPasswordChanged(ChannelPasswordChangedEvent e) {
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " changed the password of channel " + Load.api.getChannelInfo(e.getChannelId()).getName());
}
@Override
public void onClientJoin(ClientJoinEvent e) {
}
@Override
public void onClientLeave(ClientLeaveEvent e) {
}
@Override
public void onClientMoved(ClientMovedEvent e) {
}
@Override
public void onPrivilegeKeyUsed(PrivilegeKeyUsedEvent e) {
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " used a privilege key.");
}
@Override
public void onServerEdit(ServerEditedEvent e) {
// Console Log
System.out.println(Data.getPrefix() + e.getInvokerName() + " edited the virtual server.");
}
@Override
public void onTextMessage(TextMessageEvent e) {
}
});
}
}
Im sorry if its too messy for you ๐
So many people follow that YouTube tutorial, it's insane. And the guy doesn't even explain the core concepts correctly...
First of all, if you change the TS3Config
object after passing it to a TS3Query
object, nothing will happen.
public static final TS3Config config = new TS3Config();
public static final TS3Query query = new TS3Query(config);
is wrong and does the same thing as
public static final TS3Query query = new TS3Query(new TS3Config());
That isn't what you wanted.
You need to do things in the following order:
TS3Config
object.TS3Config
object.TS3Config
object to the constructor of TS3Query
.TS3Config
object ever existed.(Or check out any of our examples)
Second, if you already call
api.registerAllEvents();
from your main
method, you don't need to call registerEvent
again from every single listener. Now I don't think that's what's causing the duplicate events, but it just results in unnecessary API calls.
Finally, I don't see anything in your code that would result in the even listener being added multiple times, at least not in these two classes. I wonder if the server is actually sending you the same event notification multiple times. (It really shouldn't!)
So what I'd like you to do is call config.setEnableCommunicationsLogging(true)
before passing that TS3Config
to the constructor of TS3Query
. After setting that to true, all of the messages sent between the client and the server should be logged in your console.
If you then run your application again and get the same duplicated events, we can compare your application log with your TS3 server's log to determine what's really causing these issues ๐
Done. I dont know if thats right, but heres what im seeing in my console:
https://www.frostiqz.de/images/Screenshot_1.png
Heres my new Load.java:
package de.frostiqz.cloudbot;
import com.github.theholywaffle.teamspeak3.TS3Api;
import com.github.theholywaffle.teamspeak3.TS3Config;
import com.github.theholywaffle.teamspeak3.TS3Query;
import com.github.theholywaffle.teamspeak3.api.wrapper.Client;
import de.frostiqz.cloudbot.console.Console;
import de.frostiqz.cloudbot.events.*;
import de.frostiqz.cloudbot.storage.Data;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
public class Load {
public static TS3Api api;
private static TS3Config config;
public static final ArrayList<String> words = new ArrayList<>();
public static void main(String[] args) throws IOException, URISyntaxException {
// Start Screen
System.out.println("");
System.out.println(" _____ _ _ _ _ _ _____ __ __");
System.out.println(" / ____| | | | | | | | | | __ \\ \\ \\ / /");
System.out.println(" | | | | ___ _ _ __| | |__ ___ | |_ | |__ _ _ | |__) |__ \\ V / ");
System.out.println(" | | | |/ _ \\| | | |/ _` | '_ \\ / _ \\| __| | '_ \\| | | | | ___/ _ \\ > < ");
System.out.println(" | |____| | (_) | |_| | (_| | |_) | (_) | |_ | |_) | |_| | | | | __// . \\ ");
System.out.println(" \\_____|_|\\___/ \\__,_|\\__,_|_.__/ \\___/ \\__| |_.__/ \\__, | |_| \\___/_/ \\_\\");
System.out.println(" __/ | ");
System.out.println(" |___/ ");
System.out.println("");
System.out.println(Data.getPrefix() + "Cloudbot by Tobias - PeX");
System.out.println("");
System.out.println(Data.getPrefix() + "Starting App...");
// Ts3 Config
config = new TS3Config();
config.setHost("127.0.0.1");
System.out.println(Data.getPrefix() + "Host IP set...");
config.setEnableCommunicationsLogging(true);
System.out.println(Data.getPrefix() + "Logging enabled...");
config.setFloodRate(TS3Query.FloodRate.UNLIMITED);
System.out.println(Data.getPrefix() + "Floodrate set to unlimited...");
// Query Connect
final TS3Query query = new TS3Query(config);
query.connect();
System.out.println(Data.getPrefix() + "Query connected...");
// Api
api = query.getApi();
api.login("serveradmin", "password");
System.out.println(Data.getPrefix() + "Logged in...");
api.selectVirtualServerById(1);
api.setNickname("Frostiqz");
System.out.println(Data.getPrefix() + "Nickname set...");
api.sendChannelMessage("Frostiqz Bot - Online!");
// forbidden words
addWords();
checkNickname.start();
System.out.println(Data.getPrefix() + "Nickname & Channel loaded...");
// Events
api.registerAllEvents();
Events.loadEvents();
System.out.println(Data.getPrefix() + "Events loaded...");
// AFK Mover
AFKMover.start();
System.out.println(Data.getPrefix() + "AFKMover loaded...");
// ChatBot
ChatBot.start();
System.out.println(Data.getPrefix() + "Chatbot loaded...");
// Online Counter
OnlineCount.start();
System.out.println(Data.getPrefix() + "OnlineCount loaded...");
// Support
Support.start();
System.out.println(Data.getPrefix() + "Support loaded...");
// Welcome Message
WelcomeMessage.start();
System.out.println(Data.getPrefix() + "Welcome message loaded...");
System.out.println("");
System.out.println(Data.getPrefix() + "App sucessfully loaded!");
// Console
System.out.println(Data.getPrefix() + "Console manager startet. You can now start typing in commands.");
Console.start();
}
// forbidden words list
public static void addWords() {
// Some pretty bad words :(
}
// Nickname Checker
public static void CheckClient(Client c) {
String name = c.getNickname().toLowerCase();
if (words.contains(name)) {
api.kickClientFromServer("Der Nickname " + c.getNickname() + " ist auf diesem Server nicht erlaubt!", c);
// Console Log
System.out.println(Data.getPrefix() + c.getNickname() + " got kicked for bad Nickname");
}
}
// Channel Checker
public static void CheckChannel(String id, String name) {
Client c = api.getClientByUId(id);
if (words.contains(name.toLowerCase())) {
api.kickClientFromServer("Dieser Channel-Name ist auf diesem Server nicht erlaubt!", c);
// Console Log
System.out.println(Data.getPrefix() + c.getNickname() + " got kicked for bad Channel name");
}
}
}
I updated from 1.1 to 1.2 also (Is this version much faster? In 1.1 i got a delay of about 1-3 secs, now it reacts instantly)
Done. I dont know if thats right, but heres what im seeing in my console:
Yeah, that looks correct. What you should look for are lines about notifycliententerview
events. The question is whether those get duplicated or not. Might be a bit easier if you just write the whole program output to a file ๐ (i.e. use java -jar File.jar > out.txt
or something along those lines)
Heres my new Load.java
I think you got rid of the wrong field ๐
Keeping public static final TS3Query query;
around for when you want to shut the query down (using query.exit()
) is probably a good idea. You should get rid of that public static final TS3Config config;
field though, and just use a local variable for that instead.
Either way, the current code should work ๐
I updated from 1.1 to 1.2 also
Good idea ๐
(Is this version much faster? In 1.1 i got a delay of about 1-3 secs, now it reacts instantly)
Nah, that's because you were modifying TS3Config
after using it in the TS3Query
constructor, which you've now fixed. Before, you still only got the default flood rate that would wait 350ms between commands, and now you're actually using the UNLIMITED
flood rate ๐
What you should look for are lines about notifycliententerview events. The question is whether those get duplicated or not.
Yeah. I got 8 of them in less than a minute. Nobody joined in this time
Nah, that's because you were modifying TS3Config after using it in the TS3Query constructor, which you've now fixed. Before, you still only got the default flood rate that would wait 350ms between commands, and now you're actually using the UNLIMITED flood rate ๐
Oh, thats nice. Thanks ๐
Yeah. I got 8 of them in less than a minute. Nobody joined in this time
That seems like it should be impossible O.o
Could you send me the parts of that log file that contain those notifycliententerview
events?
2019-01-09 23:30:17.713 [DEBUG] [event] < notifycliententerview cfid=0 ctid=8 reasonid=0 clid=35 client_unique_identifier=ServerQuery client_nickname=Unknown\sfrom\s37.187.252.194:59939 client_input_muted=0 client_output_muted=0 client_outputonly_muted=0 client_input_hardware=0 client_output_hardware=0 client_meta_data client_is_recording=0 client_database_id=5 client_channel_group_id=10 client_servergroups=1,10 client_away=0 client_away_message client_type=1 client_flag_avatar client_talk_power=10 client_talk_request=0 client_talk_request_msg client_description client_is_talker=0 client_is_priority_speaker=0 client_unread_messages=0 client_nickname_phonetic client_needed_serverquery_view_power=75 client_icon_id=0 client_is_channel_commander=0 client_country=FR client_channel_group_inherited_channel_id=8 client_badges client_myteamspeak_id client_integrations
2019-01-09 23:30:17.723 [DEBUG] [event] < notifycliententerview cfid=0 ctid=8 reasonid=0 clid=35 client_unique_identifier=ServerQuery client_nickname=Unknown\sfrom\s37.187.252.194:59939 client_input_muted=0 client_output_muted=0 client_outputonly_muted=0 client_input_hardware=0 client_output_hardware=0 client_meta_data client_is_recording=0 client_database_id=5 client_channel_group_id=10 client_servergroups=1,10 client_away=0 client_away_message client_type=1 client_flag_avatar client_talk_power=10 client_talk_request=0 client_talk_request_msg client_description client_is_talker=0 client_is_priority_speaker=0 client_unread_messages=0 client_nickname_phonetic client_needed_serverquery_view_power=75 client_icon_id=0 client_is_channel_commander=0 client_country=FR client_channel_group_inherited_channel_id=8 client_badges client_myteamspeak_id client_integrations
2019-01-09 23:30:17.748 [DEBUG] [event] < notifycliententerview cfid=0 ctid=8 reasonid=0 clid=36 client_unique_identifier=ServerQuery client_nickname=Unknown\sfrom\s37.187.252.194:60733 client_input_muted=0 client_output_muted=0 client_outputonly_muted=0 client_input_hardware=0 client_output_hardware=0 client_meta_data client_is_recording=0 client_database_id=5 client_channel_group_id=10 client_servergroups=1,10 client_away=0 client_away_message client_type=1 client_flag_avatar client_talk_power=10 client_talk_request=0 client_talk_request_msg client_description client_is_talker=0 client_is_priority_speaker=0 client_unread_messages=0 client_nickname_phonetic client_needed_serverquery_view_power=75 client_icon_id=0 client_is_channel_commander=0 client_country=FR client_channel_group_inherited_channel_id=8 client_badges client_myteamspeak_id client_integrations
That are the first 3 I got. For me, it looks like these 3 are just the same (Same IP-Adress).
api.getHostInfo().getTotalClientsOnline()
This shouldnt get server query clients as well, right?
So you're getting events for server query clients, which you just can't see from your TS3 client (unless you check the checkbox in your bookmarks). In other words, no random / duplicated events after all. Phew!
And I didn't even notice that. You shouldn't be using getHostInfo()
, but rather getServerInfo()
.
As far as I can see from my test with n=1
, hostinfo
's virtualservers_total_clients_online
property doesn't seem to count server query clients, but only regular clients. Moreover, hostinfo
includes information about the entire TS3 server instance, not just the virtual server you are on. I'm almost certain that's not the information you're looking for.
VirtualServer#getClientsOnline()
is the number of both regular clients and server query clients on the current virtual server. There's also VirtualServer#getQueryClientsOnline()
, so you can also figure out how many of those total clients are server queries and how many are regular clients ๐
Thats possible. There are some server query clients from server lists. Maybe they are causing these errors.
Thanks you so much. I really appreciate your work in here.
You're welcome ๐
Good luck with the development of your TS3 bot!
Hello,
I made a bot a while ago and got a online counter in it, which shows the currently online users in a channel. This worked good, since I noticed now, that I got a lot of error messages from it. I got up to 8 (i guess) error messages per minute, which all said, that this channel name is already in use. To find out I created a new channel and tested it with this code: `
public class OnlineCount {
`
After that, this happened: https://www.frostiqz.de/images/Screenshot_1.png Nobody joined or left, but the bot kept doing its stuff.
Heres the original line I used to edit the channel:
Load.api.editChannel(5, ChannelProperty.CHANNEL_NAME , "[cspacer]ยป Aktuell Online: " + (Load.api.getHostInfo().getTotalClientsOnline()) + "/32 ยซ");