Skip to content

Commit 173b665

Browse files
committed
Move most time-expensive Twitch actions to other threads
1 parent 1f1251d commit 173b665

4 files changed

Lines changed: 140 additions & 44 deletions

File tree

src/main/java/me/mini_bomba/streamchatmod/StreamChatMod.java

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import me.mini_bomba.streamchatmod.asm.hooks.GuiScreenHook;
1111
import me.mini_bomba.streamchatmod.commands.TwitchChatCommand;
1212
import me.mini_bomba.streamchatmod.commands.TwitchCommand;
13+
import me.mini_bomba.streamchatmod.runnables.TwitchAsyncClientAction;
1314
import me.mini_bomba.streamchatmod.runnables.TwitchFollowSoundScheduler;
1415
import me.mini_bomba.streamchatmod.runnables.TwitchMessageHandler;
1516
import net.minecraft.client.Minecraft;
@@ -32,9 +33,7 @@
3233
import org.apache.logging.log4j.Logger;
3334
import org.jetbrains.annotations.Nullable;
3435

35-
import java.util.Arrays;
36-
import java.util.Collections;
37-
import java.util.List;
36+
import java.util.*;
3837

3938
@Mod(modid = StreamChatMod.MODID, version = StreamChatMod.VERSION, clientSideOnly = true)
4039
public class StreamChatMod
@@ -53,6 +52,9 @@ public class StreamChatMod
5352
public Thread httpShutdownScheduler = null;
5453
public int loginMessageTimer = -1;
5554

55+
// Thread reference for running async Twitch client action, such as starting or stopping.
56+
public Thread twitchAsyncAction;
57+
5658
private final StreamEvents events;
5759

5860
public StreamChatMod() {
@@ -98,6 +100,87 @@ public void stop(FMLModDisabledEvent event) {
98100
config.saveIfChanged();
99101
}
100102

103+
private void asyncTwitchAction(Runnable action) throws ConcurrentModificationException {
104+
if (twitchAsyncAction != null) throw new ConcurrentModificationException("An async action is already running!");
105+
twitchAsyncAction = new Thread(new TwitchAsyncClientAction(this, action));
106+
twitchAsyncAction.start();
107+
}
108+
109+
public void asyncStartTwitch() throws ConcurrentModificationException {
110+
asyncTwitchAction(() -> {
111+
if (startTwitch())
112+
StreamUtils.queueAddMessage(EnumChatFormatting.GREEN+"Enabled the Twitch Chat!");
113+
else
114+
StreamUtils.queueAddMessage(EnumChatFormatting.RED+"Could not start the Twitch client, the token may be invalid!");
115+
});
116+
}
117+
118+
public void asyncStopTwitch() throws ConcurrentModificationException {
119+
asyncTwitchAction(() -> {
120+
stopTwitch();
121+
StreamUtils.queueAddMessage(EnumChatFormatting.GREEN+"Disabled the Twitch Chat!");
122+
});
123+
}
124+
125+
public void asyncRestartTwitch() throws ConcurrentModificationException {
126+
asyncTwitchAction(() -> {
127+
stopTwitch();
128+
if (startTwitch())
129+
StreamUtils.queueAddMessage(EnumChatFormatting.GREEN+"Restarted the Twitch Chat!");
130+
else
131+
StreamUtils.queueAddMessage(EnumChatFormatting.RED+"Could not restart the Twitch client, the token may be invalid!");
132+
});
133+
}
134+
135+
public void asyncRevokeTwitchToken() throws ConcurrentModificationException {
136+
asyncTwitchAction(() -> {
137+
stopTwitch();
138+
config.twitchEnabled.set(false);
139+
boolean revoked = config.revokeTwitchToken();
140+
if (revoked) {
141+
config.setTwitchToken("");
142+
StreamUtils.queueAddMessage(EnumChatFormatting.GREEN + "The token has been revoked!");
143+
} else {
144+
StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Could not revoke the token! It may be invalid, or the request could not have been sent!");
145+
}
146+
config.saveIfChanged();
147+
});
148+
}
149+
150+
public void asyncJoinTwitchChannel(String channel) throws ConcurrentModificationException {
151+
asyncTwitchAction(() -> {
152+
if (twitch == null) { StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Twitch chat is not enabled!"); return; }
153+
TwitchChat chat = twitch.getChat();
154+
if (chat == null) { StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Twitch chat is not enabled!"); return; }
155+
chat.joinChannel(channel);
156+
if (!chat.isChannelJoined(channel)) { StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Something went wrong: Could not join the channel."); return; }
157+
if (config.followEventEnabled.getBoolean()) twitch.getClientHelper().enableFollowEventListener(channel);
158+
String[] channelArray = config.twitchChannels.getStringList();
159+
ArrayList<String> channelList = new ArrayList<>(Arrays.asList(channelArray));
160+
channelList.add(channel);
161+
config.twitchChannels.set(channelList.toArray(new String[0]));
162+
config.saveIfChanged();
163+
StreamUtils.queueAddMessage(EnumChatFormatting.GREEN+"Joined "+channel+"'s chat!");
164+
});
165+
}
166+
167+
public void asyncLeaveTwitchChannel(String channel) throws ConcurrentModificationException {
168+
asyncTwitchAction(() -> {
169+
if (twitch == null) { StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Twitch chat is not enabled!"); return; }
170+
TwitchChat chat = twitch.getChat();
171+
if (chat == null) { StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Twitch chat is not enabled!"); return; }
172+
chat.leaveChannel(channel);
173+
if (chat.isChannelJoined(channel)) { StreamUtils.queueAddMessage(EnumChatFormatting.RED + "Something went wrong: Could not leave the channel."); return; }
174+
if (config.followEventEnabled.getBoolean()) twitch.getClientHelper().disableFollowEventListener(channel);
175+
String[] channelArray = config.twitchChannels.getStringList();
176+
ArrayList<String> channelList = new ArrayList<>(Arrays.asList(channelArray));
177+
channelList.remove(channel);
178+
config.twitchChannels.set(channelList.toArray(new String[0]));
179+
config.saveIfChanged();
180+
StreamUtils.queueAddMessage(EnumChatFormatting.GREEN+"Left "+channel+"'s chat!");
181+
});
182+
}
183+
101184
public boolean startTwitch() {
102185
if (twitch != null || !config.twitchEnabled.getBoolean()) return false;
103186
String token = config.twitchToken.getString();

src/main/java/me/mini_bomba/streamchatmod/StreamUtils.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package me.mini_bomba.streamchatmod;
22

3-
import com.mojang.realmsclient.gui.ChatFormatting;
43
import com.sun.net.httpserver.HttpExchange;
54
import com.sun.net.httpserver.HttpHandler;
65
import net.minecraft.client.Minecraft;
76
import net.minecraft.client.entity.EntityPlayerSP;
87
import net.minecraft.command.ICommandSender;
98
import net.minecraft.util.ChatComponentText;
9+
import net.minecraft.util.EnumChatFormatting;
1010
import net.minecraft.util.IChatComponent;
1111
import org.apache.logging.log4j.LogManager;
1212
import org.apache.logging.log4j.Logger;
@@ -65,6 +65,14 @@ public static void addMessages(String[] message) {
6565
}
6666
}
6767

68+
public static void queueAddMessage(String message) {
69+
Minecraft.getMinecraft().addScheduledTask(() -> addMessage(message));
70+
}
71+
72+
public static void queueAddMessages(String[] messages) {
73+
Minecraft.getMinecraft().addScheduledTask(() -> addMessages(messages));
74+
}
75+
6876
public static void playSound(String sound, float volume, float pitch) {
6977
Minecraft mc = Minecraft.getMinecraft();
7078
if (mc != null) {
@@ -143,12 +151,14 @@ public void handle(HttpExchange exchange) throws IOException {
143151
String token = exchange.getRequestURI().getQuery();
144152
if (token != null) {
145153
mod.config.setTwitchToken(token);
154+
mod.config.twitchEnabled.set(true);
146155
mod.config.saveIfChanged();
147-
mod.stopTwitch();
148-
if (mod.startTwitch())
149-
addMessage(ChatFormatting.GREEN+"The Twitch token has been successfully set!");
150-
else
151-
addMessage(ChatFormatting.RED+"Could not restart the Twitch client, the token may be invalid!");
156+
if (mod.twitchAsyncAction == null) {
157+
addMessage(EnumChatFormatting.GRAY + "Token set, restarting twitch chat...");
158+
mod.asyncRestartTwitch();
159+
} else {
160+
addMessage(EnumChatFormatting.RED + "There was an async action running, could not restart Twitch client. Run /twitch restart to finish setup.");
161+
}
152162
}
153163
exchange.sendResponseHeaders(token == null ? 400 : 200, 0);
154164
exchange.close();

src/main/java/me/mini_bomba/streamchatmod/commands/TwitchCommand.java

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -79,32 +79,30 @@ public void processCommand(ICommandSender sender, String[] args) throws CommandE
7979
case "start":
8080
if (!mod.config.isTwitchTokenSet()) throw new CommandException("Twitch token is not configured! Use /twitch token to configure it.");
8181
if (mod.twitch != null) throw new CommandException("Twitch chat is already enabled!");
82+
if (mod.twitchAsyncAction != null) throw new CommandException("An action for the Twitch Chat is currently pending, please wait.");
8283
mod.config.twitchEnabled.set(true);
8384
mod.config.saveIfChanged();
84-
if (mod.startTwitch())
85-
StreamUtils.addMessage(sender, EnumChatFormatting.GREEN+"Enabled the Twitch Chat!");
86-
else
87-
StreamUtils.addMessage(sender, EnumChatFormatting.RED+"Could not start the Twitch client, the token may be invalid!");
85+
mod.asyncStartTwitch();
86+
StreamUtils.addMessage(EnumChatFormatting.GRAY + "Starting Twitch Chat...");
8887
break;
8988
case "disable":
9089
case "off":
9190
case "stop":
9291
if (mod.twitch == null && !mod.config.twitchEnabled.getBoolean()) throw new CommandException("Twitch chat is already disabled!");
92+
if (mod.twitchAsyncAction != null) throw new CommandException("An action for the Twitch Chat is currently pending, please wait.");
9393
mod.config.twitchEnabled.set(false);
9494
mod.config.saveIfChanged();
95-
mod.stopTwitch();
96-
StreamUtils.addMessage(sender, EnumChatFormatting.GREEN+"Disabled the Twitch Chat!");
95+
mod.asyncStopTwitch();
96+
StreamUtils.addMessage(EnumChatFormatting.GRAY + "Stopping Twitch Chat...");
9797
break;
9898
case "restart":
9999
case "reload":
100100
case "r":
101101
if (!mod.config.isTwitchTokenSet()) throw new CommandException("Twitch token is not configured! Use /twitch token to configure it.");
102102
if (!mod.config.twitchEnabled.getBoolean()) throw new CommandException("Twitch chat is not enabled!");
103-
mod.stopTwitch();
104-
if (mod.startTwitch())
105-
StreamUtils.addMessage(sender, EnumChatFormatting.GREEN+"Restarted the Twitch Chat!");
106-
else
107-
StreamUtils.addMessage(sender, EnumChatFormatting.RED+"Could not restart the Twitch client, the token may be invalid!");
103+
if (mod.twitchAsyncAction != null) throw new CommandException("An action for the Twitch Chat is currently pending, please wait.");
104+
mod.asyncRestartTwitch();
105+
StreamUtils.addMessage(EnumChatFormatting.GRAY + "Restarting Twitch Chat...");
108106
break;
109107
case "mode":
110108
case "chatmode":
@@ -276,13 +274,9 @@ public void processCommand(ICommandSender sender, String[] args) throws CommandE
276274
if (chat == null) throw new CommandException("Please enable Twitch chat first!");
277275
if (channel.equals("")) throw new CommandException("Missing parameter: channel to join");
278276
if (channelList.contains(channel) && chat.isChannelJoined(channel)) throw new CommandException("Channel "+channel+" is already joined!");
279-
chat.joinChannel(channel);
280-
if (!chat.isChannelJoined(channel)) throw new CommandException("Something went wrong: Could not join the channel.");
281-
if (mod.config.followEventEnabled.getBoolean()) mod.twitch.getClientHelper().enableFollowEventListener(channel);
282-
channelList.add(channel);
283-
mod.config.twitchChannels.set(channelList.toArray(new String[0]));
284-
mod.config.saveIfChanged();
285-
StreamUtils.addMessage(sender, EnumChatFormatting.GREEN+"Joined "+channel+"'s chat!");
277+
if (mod.twitchAsyncAction != null) throw new CommandException("An action for the Twitch Chat is currently pending, please wait.");
278+
mod.asyncJoinTwitchChannel(channel);
279+
StreamUtils.addMessage(EnumChatFormatting.GRAY + "Joining channel...");
286280
break;
287281
case "leave":
288282
case "l":
@@ -293,13 +287,9 @@ public void processCommand(ICommandSender sender, String[] args) throws CommandE
293287
if (chat == null) throw new CommandException("Please enable Twitch chat first!");
294288
if (channel.equals("")) throw new CommandException("Missing parameter: channel to leave");
295289
if (!channelList.contains(channel) && !chat.isChannelJoined(channel)) throw new CommandException("Channel "+channel+" is not joined!");
296-
chat.leaveChannel(channel);
297-
if (chat.isChannelJoined(channel)) throw new CommandException("Something went wrong: Could not leave the channel.");
298-
if (mod.config.followEventEnabled.getBoolean()) mod.twitch.getClientHelper().disableFollowEventListener(channel);
299-
channelList.remove(channel);
300-
mod.config.twitchChannels.set(channelList.toArray(new String[0]));
301-
mod.config.saveIfChanged();
302-
StreamUtils.addMessage(sender, EnumChatFormatting.GREEN+"Left "+channel+"'s chat!");
290+
if (mod.twitchAsyncAction != null) throw new CommandException("An action for the Twitch Chat is currently pending, please wait.");
291+
mod.asyncLeaveTwitchChannel(channel);
292+
StreamUtils.addMessage(EnumChatFormatting.GRAY + "Leaving channel...");
303293
break;
304294
case "list":
305295
case "show":
@@ -423,16 +413,7 @@ public void processCommand(ICommandSender sender, String[] args) throws CommandE
423413
case "deltoken":
424414
case "resettoken":
425415
StreamUtils.addMessage(EnumChatFormatting.GRAY + "Revoking your current token...");
426-
mod.stopTwitch();
427-
mod.config.twitchEnabled.set(false);
428-
boolean revoked = mod.config.revokeTwitchToken();
429-
if (revoked) {
430-
mod.config.setTwitchToken("");
431-
StreamUtils.addMessage(EnumChatFormatting.GREEN + "The token has been revoked!");
432-
} else {
433-
StreamUtils.addMessage(EnumChatFormatting.RED + "Could not revoke the token! It may be invalid, or the request could not have been sent!");
434-
}
435-
mod.config.saveIfChanged();
416+
mod.asyncRevokeTwitchToken();
436417
break;
437418
default:
438419
throw new CommandException("Unknown subcommand: use /twitch help to see available subcommands.");
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package me.mini_bomba.streamchatmod.runnables;
2+
3+
import lombok.SneakyThrows;
4+
import me.mini_bomba.streamchatmod.StreamChatMod;
5+
6+
public class TwitchAsyncClientAction implements Runnable {
7+
8+
private final StreamChatMod mod;
9+
private final Runnable action;
10+
11+
public TwitchAsyncClientAction(StreamChatMod mod, Runnable action) {
12+
this.mod = mod;
13+
this.action = action;
14+
}
15+
16+
@SneakyThrows
17+
@Override
18+
public void run() {
19+
action.run();
20+
mod.twitchAsyncAction = null;
21+
}
22+
}

0 commit comments

Comments
 (0)