mirror of
https://github.com/AutismDevelopment/Autism-Client.git
synced 2026-10-28 17:29:46 +00:00
Refactor: extract MacroChatObserve from MacroExecutor (upstream #4)
Move 12 chat observe/capture methods (observeIncomingChat, recordRecentChat, syncRecentChatServerContext, getCurrentMultiplayerServerKey, normalizeServerKey, matchesWaitingChat, matchesChatAction, captureChatAction x2, chatValues, extractIncomingChat, buildChatCapture) verbatim into MacroChatObserve. Shared MacroExecutor members referenced as MacroExecutor.xxx (mechanical qualification only); public API unchanged via observeIncomingChat delegate. All bodies verified identical against HEAD modulo that qualification.
This commit is contained in:
parent
db8fbb763e
commit
518bc78faa
2 changed files with 255 additions and 236 deletions
230
src/main/java/autismclient/util/macro/MacroChatObserve.java
Normal file
230
src/main/java/autismclient/util/macro/MacroChatObserve.java
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
package autismclient.util.macro;
|
||||||
|
|
||||||
|
import autismclient.modules.PackHideState;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.ServerData;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
final class MacroChatObserve {
|
||||||
|
private MacroChatObserve() {
|
||||||
|
}
|
||||||
|
public static void observeIncomingChat(Packet<?> packet) {
|
||||||
|
if (PackHideState.isHardLocked()) return;
|
||||||
|
MacroExecutor.ChatCapture capture = extractIncomingChat(packet);
|
||||||
|
if (capture == null || capture.message() == null || capture.message().isBlank()) return;
|
||||||
|
syncRecentChatServerContext();
|
||||||
|
recordRecentChat(capture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void recordRecentChat(MacroExecutor.ChatCapture capture) {
|
||||||
|
MacroExecutor.RecentChatMessage message = new MacroExecutor.RecentChatMessage(
|
||||||
|
capture.sender() == null ? "" : capture.sender(),
|
||||||
|
capture.message(),
|
||||||
|
capture.displayText(),
|
||||||
|
capture.displayComponent(),
|
||||||
|
capture.source(),
|
||||||
|
System.currentTimeMillis()
|
||||||
|
);
|
||||||
|
synchronized (MacroExecutor.recentChatLock) {
|
||||||
|
MacroExecutor.recentChatMessages.removeIf(existing -> existing.displayText().equals(message.displayText()));
|
||||||
|
MacroExecutor.recentChatMessages.addFirst(message);
|
||||||
|
while (MacroExecutor.recentChatMessages.size() > MacroExecutor.MAX_RECENT_CHAT_MESSAGES) {
|
||||||
|
MacroExecutor.recentChatMessages.removeLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void syncRecentChatServerContext() {
|
||||||
|
String currentServerKey = getCurrentMultiplayerServerKey();
|
||||||
|
if (currentServerKey.isEmpty()) return;
|
||||||
|
|
||||||
|
if (currentServerKey.equals(MacroExecutor.recentChatServerKeyMirror)) return;
|
||||||
|
synchronized (MacroExecutor.recentChatLock) {
|
||||||
|
if (MacroExecutor.recentChatServerKey.isEmpty()) {
|
||||||
|
MacroExecutor.recentChatServerKey = currentServerKey;
|
||||||
|
MacroExecutor.recentChatServerKeyMirror = currentServerKey;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!MacroExecutor.recentChatServerKey.equals(currentServerKey)) {
|
||||||
|
MacroExecutor.recentChatMessages.clear();
|
||||||
|
MacroExecutor.recentChatServerKey = currentServerKey;
|
||||||
|
MacroExecutor.recentChatServerKeyMirror = currentServerKey;
|
||||||
|
synchronized (MacroExecutor.recentGuiLock) {
|
||||||
|
MacroExecutor.recentGuiTitles.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MacroExecutor.recentChatServerKeyMirror = currentServerKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getCurrentMultiplayerServerKey() {
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (mc == null || mc.isLocalServer()) return "";
|
||||||
|
|
||||||
|
ServerData entry = mc.getCurrentServer();
|
||||||
|
if (entry != null && entry.ip != null && !entry.ip.isBlank()) {
|
||||||
|
return normalizeServerKey(entry.ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mc.getConnection() != null && mc.getConnection().getConnection() != null) {
|
||||||
|
SocketAddress address = mc.getConnection().getConnection().getRemoteAddress();
|
||||||
|
if (address instanceof InetSocketAddress inet) {
|
||||||
|
String host = inet.getHostString();
|
||||||
|
if ((host == null || host.isBlank()) && inet.getAddress() != null) {
|
||||||
|
host = inet.getAddress().getHostAddress();
|
||||||
|
}
|
||||||
|
if (host != null && !host.isBlank()) {
|
||||||
|
return normalizeServerKey(host + ":" + inet.getPort());
|
||||||
|
}
|
||||||
|
} else if (address != null) {
|
||||||
|
String raw = address.toString();
|
||||||
|
if (raw != null && !raw.isBlank()) {
|
||||||
|
return normalizeServerKey(raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeServerKey(String address) {
|
||||||
|
if (address == null) return "";
|
||||||
|
String stripped = address.startsWith("/") ? address.substring(1) : address;
|
||||||
|
return stripped.trim().toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean matchesWaitingChat(MacroExecutor.ChatCapture capture) {
|
||||||
|
String patternJson = MacroExecutor.waitingChatPatternJson.get();
|
||||||
|
if (!MacroExecutor.waitingChatIsRegex.get() && patternJson != null && !patternJson.isBlank() && capture.displayComponent() != null) {
|
||||||
|
String captureJson = MacroChatText.serializeTextComponent(capture.displayComponent());
|
||||||
|
if (patternJson.equals(captureJson)) return true;
|
||||||
|
}
|
||||||
|
String pattern = MacroChatText.normalizeManualText(MacroExecutor.waitingChatPattern.get());
|
||||||
|
if (pattern == null || pattern.isBlank()) return true;
|
||||||
|
if (MacroExecutor.waitingChatIsRegex.get()) {
|
||||||
|
try {
|
||||||
|
java.util.regex.Pattern compiled = MacroCapturePattern.compiled(pattern);
|
||||||
|
return compiled.matcher(capture.message()).matches() || compiled.matcher(capture.displayText()).matches();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int threshold = MacroExecutor.waitingChatFuzzyPercent.get();
|
||||||
|
return MacroChatText.fuzzyChatMatch(pattern, capture.message(), threshold)
|
||||||
|
|| MacroChatText.fuzzyChatMatch(pattern, capture.displayText(), threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matchesChatAction(WaitForChatAction action, MacroExecutor.ChatCapture capture) {
|
||||||
|
return captureChatAction(action, capture, MacroExecutor.currentVariableContext()).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static java.util.Optional<java.util.Map<String, MacroValue>> captureChatAction(WaitForChatAction action, MacroExecutor.ChatCapture capture) {
|
||||||
|
return captureChatAction(action, capture, MacroExecutor.currentVariableContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static java.util.Optional<java.util.Map<String, MacroValue>> captureChatAction(WaitForChatAction action, MacroExecutor.ChatCapture capture,
|
||||||
|
MacroVariableContext context) {
|
||||||
|
if (action == null || capture == null) return java.util.Optional.empty();
|
||||||
|
MacroCapturePattern.Mode mode = action.effectiveMatchMode();
|
||||||
|
if (mode == MacroCapturePattern.Mode.MATCH && action.patternJson != null && !action.patternJson.isBlank() && capture.displayComponent() != null) {
|
||||||
|
String captureJson = MacroChatText.serializeTextComponent(capture.displayComponent());
|
||||||
|
if (action.patternJson.equals(captureJson)) return java.util.Optional.of(chatValues(action, capture, java.util.Map.of()));
|
||||||
|
}
|
||||||
|
String pattern = MacroChatText.normalizeManualText(action.pattern);
|
||||||
|
if (mode == MacroCapturePattern.Mode.MATCH) {
|
||||||
|
MacroTemplate.Resolution patternResolution = MacroTemplate.resolve(pattern, context, Minecraft.getInstance());
|
||||||
|
if (!patternResolution.success()) return java.util.Optional.empty();
|
||||||
|
pattern = patternResolution.value();
|
||||||
|
}
|
||||||
|
if (pattern == null || pattern.isBlank()) return java.util.Optional.of(chatValues(action, capture, java.util.Map.of()));
|
||||||
|
if (mode != MacroCapturePattern.Mode.MATCH) {
|
||||||
|
var messageResult = MacroCapturePattern.match(mode, pattern, capture.message());
|
||||||
|
var result = messageResult.isPresent() ? messageResult : MacroCapturePattern.match(mode, pattern, capture.displayText());
|
||||||
|
return result.map(value -> chatValues(action, capture, value.values()));
|
||||||
|
}
|
||||||
|
int threshold = WaitForChatAction.clampFuzzyPercent(action.fuzzyPercent);
|
||||||
|
boolean matched = MacroChatText.fuzzyChatMatch(pattern, capture.message(), threshold)
|
||||||
|
|| MacroChatText.fuzzyChatMatch(pattern, capture.displayText(), threshold);
|
||||||
|
return matched ? java.util.Optional.of(chatValues(action, capture, java.util.Map.of())) : java.util.Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static java.util.Map<String, MacroValue> chatValues(WaitForChatAction action, MacroExecutor.ChatCapture capture,
|
||||||
|
java.util.Map<String, MacroValue> captures) {
|
||||||
|
java.util.Map<String, MacroValue> values = new java.util.LinkedHashMap<>(captures);
|
||||||
|
if (action.saveAs != null && !action.saveAs.isBlank()) {
|
||||||
|
java.util.Map<String, MacroValue> properties = new java.util.LinkedHashMap<>();
|
||||||
|
properties.put("sender", MacroValue.text(capture.sender()));
|
||||||
|
properties.put("message", MacroValue.text(capture.message()));
|
||||||
|
properties.put("display", MacroValue.text(capture.displayText()));
|
||||||
|
properties.put("source", MacroValue.text(capture.source().name()));
|
||||||
|
values.put(action.saveAs, MacroValue.structured(MacroValue.Kind.TEXT, capture.displayText(), properties));
|
||||||
|
}
|
||||||
|
return java.util.Map.copyOf(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MacroExecutor.ChatCapture extractIncomingChat(Packet<?> packet) {
|
||||||
|
|
||||||
|
if (packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket systemChat) {
|
||||||
|
Component content = systemChat.content();
|
||||||
|
if (content == null) return null;
|
||||||
|
String raw = content.getString();
|
||||||
|
if (raw == null || raw.isBlank()) return null;
|
||||||
|
return buildChatCapture("", raw, raw, content, MacroExecutor.ChatSource.PLAYER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet instanceof net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket disguisedChat) {
|
||||||
|
Component content = disguisedChat.message();
|
||||||
|
String text = content == null ? null : content.getString();
|
||||||
|
if (text == null || text.isBlank()) return null;
|
||||||
|
Component decorated = disguisedChat.chatType().decorate(content);
|
||||||
|
String sender = disguisedChat.chatType().name().getString();
|
||||||
|
return buildChatCapture(sender, text, null, decorated, MacroExecutor.ChatSource.PLAYER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket playerChat) {
|
||||||
|
|
||||||
|
Component content = playerChat.unsignedContent() != null
|
||||||
|
? playerChat.unsignedContent()
|
||||||
|
: Component.literal(playerChat.body().content());
|
||||||
|
String message = content.getString();
|
||||||
|
if (message == null || message.isBlank()) message = playerChat.body().content();
|
||||||
|
if (message == null || message.isBlank()) return null;
|
||||||
|
|
||||||
|
Component decorated = playerChat.chatType().decorate(content);
|
||||||
|
String sender = playerChat.chatType().name().getString();
|
||||||
|
if (sender == null || sender.isBlank()) {
|
||||||
|
String byUuid = MacroChatText.resolvePlayerName(playerChat.sender());
|
||||||
|
if (byUuid != null && !byUuid.isBlank()) sender = byUuid;
|
||||||
|
}
|
||||||
|
return buildChatCapture(sender, message, null, decorated, MacroExecutor.ChatSource.PLAYER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MacroExecutor.ChatCapture buildChatCapture(String sender, String message, String displayText, Component displayComponent, MacroExecutor.ChatSource source) {
|
||||||
|
String cleanMessage = MacroChatText.sanitizeChatText(message);
|
||||||
|
if (cleanMessage.isBlank()) return null;
|
||||||
|
String cleanSender = MacroChatText.sanitizeChatText(sender);
|
||||||
|
String cleanDisplay = MacroChatText.sanitizeChatText(displayText);
|
||||||
|
Component rendered = MacroChatText.literalizeTextComponent(displayComponent);
|
||||||
|
String display = cleanDisplay;
|
||||||
|
if (rendered == null || rendered.getString().isBlank()) {
|
||||||
|
rendered = cleanSender.isBlank()
|
||||||
|
? Component.literal(cleanMessage)
|
||||||
|
: Component.literal("<" + cleanSender + "> ").append(Component.literal(cleanMessage));
|
||||||
|
}
|
||||||
|
if (display == null || display.isBlank()) {
|
||||||
|
display = MacroChatText.sanitizeChatText(rendered.getString());
|
||||||
|
}
|
||||||
|
MacroExecutor.ChatSource resolvedSource = source != null ? source : MacroExecutor.ChatSource.PLAYER;
|
||||||
|
return new MacroExecutor.ChatCapture(cleanSender, cleanMessage, MacroChatText.sanitizeChatText(display), rendered, resolvedSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -115,18 +115,19 @@ public class MacroExecutor {
|
||||||
private static final Object packetWaitLock = new Object();
|
private static final Object packetWaitLock = new Object();
|
||||||
|
|
||||||
private static final AtomicBoolean isWaitingForChat = new AtomicBoolean(false);
|
private static final AtomicBoolean isWaitingForChat = new AtomicBoolean(false);
|
||||||
private static final AtomicReference<String> waitingChatPattern = new AtomicReference<>("");
|
static final AtomicReference<String> waitingChatPattern = new AtomicReference<>("");
|
||||||
private static final AtomicBoolean waitingChatIsRegex = new AtomicBoolean(false);
|
static final AtomicBoolean waitingChatIsRegex = new AtomicBoolean(false);
|
||||||
private static final AtomicReference<String> waitingChatPatternJson = new AtomicReference<>("");
|
static final AtomicReference<String> waitingChatPatternJson = new AtomicReference<>("");
|
||||||
private static final java.util.concurrent.atomic.AtomicInteger waitingChatFuzzyPercent = new java.util.concurrent.atomic.AtomicInteger(100);
|
static final java.util.concurrent.atomic.AtomicInteger waitingChatFuzzyPercent = new java.util.concurrent.atomic.AtomicInteger(100);
|
||||||
private static final AtomicReference<String> lastMatchedChat = new AtomicReference<>("");
|
private static final AtomicReference<String> lastMatchedChat = new AtomicReference<>("");
|
||||||
private static final Object recentChatLock = new Object();
|
static final Object recentChatLock = new Object();
|
||||||
private static final Deque<RecentChatMessage> recentChatMessages = new ArrayDeque<>();
|
static final Deque<RecentChatMessage> recentChatMessages = new ArrayDeque<>();
|
||||||
private static final int MAX_RECENT_CHAT_MESSAGES = 256;
|
static final int MAX_RECENT_CHAT_MESSAGES = 256;
|
||||||
private static final Object recentGuiLock = new Object();
|
static final Object recentGuiLock = new Object();
|
||||||
private static final Deque<RecentGuiTitle> recentGuiTitles = new ArrayDeque<>();
|
static final Deque<RecentGuiTitle> recentGuiTitles = new ArrayDeque<>();
|
||||||
private static final int MAX_RECENT_GUI_TITLES = 128;
|
private static final int MAX_RECENT_GUI_TITLES = 128;
|
||||||
private static String recentChatServerKey = "";
|
static String recentChatServerKey = "";
|
||||||
|
static volatile String recentChatServerKeyMirror = "";
|
||||||
|
|
||||||
private static volatile CompletableFuture<Void> activePacketFuture = null;
|
private static volatile CompletableFuture<Void> activePacketFuture = null;
|
||||||
private static volatile CompletableFuture<Void> activeChatFuture = null;
|
private static volatile CompletableFuture<Void> activeChatFuture = null;
|
||||||
|
|
@ -235,7 +236,7 @@ public class MacroExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ChatCapture(String sender, String message, String displayText, Component displayComponent, ChatSource source) {}
|
record ChatCapture(String sender, String message, String displayText, Component displayComponent, ChatSource source) {}
|
||||||
private record RepeatPacketContext(AutismMacro macro, int startIdx, int endIdx) {}
|
private record RepeatPacketContext(AutismMacro macro, int startIdx, int endIdx) {}
|
||||||
private record InlineNextActions(java.util.List<MacroAction> actions, int lastIndex) {
|
private record InlineNextActions(java.util.List<MacroAction> actions, int lastIndex) {
|
||||||
static InlineNextActions empty() {
|
static InlineNextActions empty() {
|
||||||
|
|
@ -2165,7 +2166,7 @@ public class MacroExecutor {
|
||||||
RunState run = CURRENT_RUN.get();
|
RunState run = CURRENT_RUN.get();
|
||||||
clearCaptureOutputs(action);
|
clearCaptureOutputs(action);
|
||||||
OneShotPacketListener listener = awaitReceive(packet -> {
|
OneShotPacketListener listener = awaitReceive(packet -> {
|
||||||
ChatCapture capture = extractIncomingChat(packet);
|
ChatCapture capture = MacroChatObserve.extractIncomingChat(packet);
|
||||||
if (capture == null) return false;
|
if (capture == null) return false;
|
||||||
RecentChatMessage message = new RecentChatMessage(
|
RecentChatMessage message = new RecentChatMessage(
|
||||||
capture.sender() == null ? "" : capture.sender(),
|
capture.sender() == null ? "" : capture.sender(),
|
||||||
|
|
@ -3114,8 +3115,8 @@ public class MacroExecutor {
|
||||||
if (action instanceof WaitForChatAction chat) {
|
if (action instanceof WaitForChatAction chat) {
|
||||||
RunState run = CURRENT_RUN.get();
|
RunState run = CURRENT_RUN.get();
|
||||||
OneShotPacketListener listener = awaitReceive(packet -> {
|
OneShotPacketListener listener = awaitReceive(packet -> {
|
||||||
ChatCapture capture = extractIncomingChat(packet);
|
ChatCapture capture = MacroChatObserve.extractIncomingChat(packet);
|
||||||
var matched = captureChatAction(chat, capture, run == null ? null : run.variables);
|
var matched = MacroChatObserve.captureChatAction(chat, capture, run == null ? null : run.variables);
|
||||||
if (matched.isEmpty()) return false;
|
if (matched.isEmpty()) return false;
|
||||||
if (run != null) run.stagedCaptures.put(chat, matched.get());
|
if (run != null) run.stagedCaptures.put(chat, matched.get());
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -4058,8 +4059,8 @@ public class MacroExecutor {
|
||||||
java.util.concurrent.atomic.AtomicReference<java.util.Map<String, MacroValue>> captures =
|
java.util.concurrent.atomic.AtomicReference<java.util.Map<String, MacroValue>> captures =
|
||||||
new java.util.concurrent.atomic.AtomicReference<>(java.util.Map.of());
|
new java.util.concurrent.atomic.AtomicReference<>(java.util.Map.of());
|
||||||
TimedOneShotPacketListener listener = awaitTimedReceive((packet, direction, now) -> {
|
TimedOneShotPacketListener listener = awaitTimedReceive((packet, direction, now) -> {
|
||||||
ChatCapture capture = extractIncomingChat(packet);
|
ChatCapture capture = MacroChatObserve.extractIncomingChat(packet);
|
||||||
var matched = captureChatAction(chat, capture, context);
|
var matched = MacroChatObserve.captureChatAction(chat, capture, context);
|
||||||
matched.ifPresent(captures::set);
|
matched.ifPresent(captures::set);
|
||||||
return matched.isPresent();
|
return matched.isPresent();
|
||||||
});
|
});
|
||||||
|
|
@ -4068,7 +4069,7 @@ public class MacroExecutor {
|
||||||
listener.future.whenComplete((event, e) -> {
|
listener.future.whenComplete((event, e) -> {
|
||||||
if (e != null) out.completeExceptionally(e);
|
if (e != null) out.completeExceptionally(e);
|
||||||
else out.complete(new MacroEvent(event.packet(), event.direction(), event.nanoTime(),
|
else out.complete(new MacroEvent(event.packet(), event.direction(), event.nanoTime(),
|
||||||
extractIncomingChat(event.packet()), captures.get()));
|
MacroChatObserve.extractIncomingChat(event.packet()), captures.get()));
|
||||||
});
|
});
|
||||||
out.whenComplete((v, e) -> listener.cancel());
|
out.whenComplete((v, e) -> listener.cancel());
|
||||||
return out;
|
return out;
|
||||||
|
|
@ -4235,8 +4236,8 @@ public class MacroExecutor {
|
||||||
if (trigger instanceof WaitForChatAction chat) {
|
if (trigger instanceof WaitForChatAction chat) {
|
||||||
MacroVariableContext context = currentVariableContext();
|
MacroVariableContext context = currentVariableContext();
|
||||||
OneShotPacketListener listener = awaitReceive(packet -> {
|
OneShotPacketListener listener = awaitReceive(packet -> {
|
||||||
ChatCapture capture = extractIncomingChat(packet);
|
ChatCapture capture = MacroChatObserve.extractIncomingChat(packet);
|
||||||
return capture != null && captureChatAction(chat, capture, context).isPresent();
|
return capture != null && MacroChatObserve.captureChatAction(chat, capture, context).isPresent();
|
||||||
});
|
});
|
||||||
listener.future.whenComplete((v, e) -> listener.cancel());
|
listener.future.whenComplete((v, e) -> listener.cancel());
|
||||||
return listener.future;
|
return listener.future;
|
||||||
|
|
@ -7923,7 +7924,7 @@ public class MacroExecutor {
|
||||||
|
|
||||||
public static void onPacketObserved(Packet<?> packet, String direction) {
|
public static void onPacketObserved(Packet<?> packet, String direction) {
|
||||||
if (PackHideState.isHardLocked()) return;
|
if (PackHideState.isHardLocked()) return;
|
||||||
syncRecentChatServerContext();
|
MacroChatObserve.syncRecentChatServerContext();
|
||||||
|
|
||||||
java.util.List<RunState> runs = activeRunSnapshot();
|
java.util.List<RunState> runs = activeRunSnapshot();
|
||||||
boolean s2c = "S2C".equals(direction);
|
boolean s2c = "S2C".equals(direction);
|
||||||
|
|
@ -7939,12 +7940,12 @@ public class MacroExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s2c && (chatWork || isWaitingForChat.get())) {
|
if (s2c && (chatWork || isWaitingForChat.get())) {
|
||||||
ChatCapture capture = extractIncomingChat(packet);
|
ChatCapture capture = MacroChatObserve.extractIncomingChat(packet);
|
||||||
if (capture != null && capture.message() != null && !capture.message().isBlank()) {
|
if (capture != null && capture.message() != null && !capture.message().isBlank()) {
|
||||||
|
|
||||||
for (RunState run : runs) {
|
for (RunState run : runs) {
|
||||||
var matched = run.running.get() && run.waitingForChat.get()
|
var matched = run.running.get() && run.waitingForChat.get()
|
||||||
? captureChatAction(run.waitingChatAction, capture, run.variables)
|
? MacroChatObserve.captureChatAction(run.waitingChatAction, capture, run.variables)
|
||||||
: java.util.Optional.<java.util.Map<String, MacroValue>>empty();
|
: java.util.Optional.<java.util.Map<String, MacroValue>>empty();
|
||||||
if (matched.isPresent()) {
|
if (matched.isPresent()) {
|
||||||
run.stagedCaptures.put(run.waitingChatAction, matched.get());
|
run.stagedCaptures.put(run.waitingChatAction, matched.get());
|
||||||
|
|
@ -7952,7 +7953,7 @@ public class MacroExecutor {
|
||||||
if (f != null) f.complete(null);
|
if (f != null) f.complete(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isCurrentRunActive() && isWaitingForChat.get() && matchesWaitingChat(capture)) {
|
if (isCurrentRunActive() && isWaitingForChat.get() && MacroChatObserve.matchesWaitingChat(capture)) {
|
||||||
lastMatchedChat.set(capture.displayText());
|
lastMatchedChat.set(capture.displayText());
|
||||||
CompletableFuture<Void> f = activeChatFuture;
|
CompletableFuture<Void> f = activeChatFuture;
|
||||||
if (f != null) f.complete(null);
|
if (f != null) f.complete(null);
|
||||||
|
|
@ -8001,219 +8002,7 @@ public class MacroExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void observeIncomingChat(Packet<?> packet) {
|
public static void observeIncomingChat(Packet<?> packet) {
|
||||||
if (PackHideState.isHardLocked()) return;
|
MacroChatObserve.observeIncomingChat(packet);
|
||||||
ChatCapture capture = extractIncomingChat(packet);
|
|
||||||
if (capture == null || capture.message() == null || capture.message().isBlank()) return;
|
|
||||||
syncRecentChatServerContext();
|
|
||||||
recordRecentChat(capture);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void recordRecentChat(ChatCapture capture) {
|
|
||||||
RecentChatMessage message = new RecentChatMessage(
|
|
||||||
capture.sender() == null ? "" : capture.sender(),
|
|
||||||
capture.message(),
|
|
||||||
capture.displayText(),
|
|
||||||
capture.displayComponent(),
|
|
||||||
capture.source(),
|
|
||||||
System.currentTimeMillis()
|
|
||||||
);
|
|
||||||
synchronized (recentChatLock) {
|
|
||||||
recentChatMessages.removeIf(existing -> existing.displayText().equals(message.displayText()));
|
|
||||||
recentChatMessages.addFirst(message);
|
|
||||||
while (recentChatMessages.size() > MAX_RECENT_CHAT_MESSAGES) {
|
|
||||||
recentChatMessages.removeLast();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static volatile String recentChatServerKeyMirror = "";
|
|
||||||
|
|
||||||
private static void syncRecentChatServerContext() {
|
|
||||||
String currentServerKey = getCurrentMultiplayerServerKey();
|
|
||||||
if (currentServerKey.isEmpty()) return;
|
|
||||||
|
|
||||||
if (currentServerKey.equals(recentChatServerKeyMirror)) return;
|
|
||||||
synchronized (recentChatLock) {
|
|
||||||
if (recentChatServerKey.isEmpty()) {
|
|
||||||
recentChatServerKey = currentServerKey;
|
|
||||||
recentChatServerKeyMirror = currentServerKey;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!recentChatServerKey.equals(currentServerKey)) {
|
|
||||||
recentChatMessages.clear();
|
|
||||||
recentChatServerKey = currentServerKey;
|
|
||||||
recentChatServerKeyMirror = currentServerKey;
|
|
||||||
synchronized (recentGuiLock) {
|
|
||||||
recentGuiTitles.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
recentChatServerKeyMirror = currentServerKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getCurrentMultiplayerServerKey() {
|
|
||||||
Minecraft mc = Minecraft.getInstance();
|
|
||||||
if (mc == null || mc.isLocalServer()) return "";
|
|
||||||
|
|
||||||
ServerData entry = mc.getCurrentServer();
|
|
||||||
if (entry != null && entry.ip != null && !entry.ip.isBlank()) {
|
|
||||||
return normalizeServerKey(entry.ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mc.getConnection() != null && mc.getConnection().getConnection() != null) {
|
|
||||||
SocketAddress address = mc.getConnection().getConnection().getRemoteAddress();
|
|
||||||
if (address instanceof InetSocketAddress inet) {
|
|
||||||
String host = inet.getHostString();
|
|
||||||
if ((host == null || host.isBlank()) && inet.getAddress() != null) {
|
|
||||||
host = inet.getAddress().getHostAddress();
|
|
||||||
}
|
|
||||||
if (host != null && !host.isBlank()) {
|
|
||||||
return normalizeServerKey(host + ":" + inet.getPort());
|
|
||||||
}
|
|
||||||
} else if (address != null) {
|
|
||||||
String raw = address.toString();
|
|
||||||
if (raw != null && !raw.isBlank()) {
|
|
||||||
return normalizeServerKey(raw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String normalizeServerKey(String address) {
|
|
||||||
if (address == null) return "";
|
|
||||||
String stripped = address.startsWith("/") ? address.substring(1) : address;
|
|
||||||
return stripped.trim().toLowerCase(Locale.ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean matchesWaitingChat(ChatCapture capture) {
|
|
||||||
String patternJson = waitingChatPatternJson.get();
|
|
||||||
if (!waitingChatIsRegex.get() && patternJson != null && !patternJson.isBlank() && capture.displayComponent() != null) {
|
|
||||||
String captureJson = MacroChatText.serializeTextComponent(capture.displayComponent());
|
|
||||||
if (patternJson.equals(captureJson)) return true;
|
|
||||||
}
|
|
||||||
String pattern = MacroChatText.normalizeManualText(waitingChatPattern.get());
|
|
||||||
if (pattern == null || pattern.isBlank()) return true;
|
|
||||||
if (waitingChatIsRegex.get()) {
|
|
||||||
try {
|
|
||||||
java.util.regex.Pattern compiled = MacroCapturePattern.compiled(pattern);
|
|
||||||
return compiled.matcher(capture.message()).matches() || compiled.matcher(capture.displayText()).matches();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int threshold = waitingChatFuzzyPercent.get();
|
|
||||||
return MacroChatText.fuzzyChatMatch(pattern, capture.message(), threshold)
|
|
||||||
|| MacroChatText.fuzzyChatMatch(pattern, capture.displayText(), threshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean matchesChatAction(WaitForChatAction action, ChatCapture capture) {
|
|
||||||
return captureChatAction(action, capture, currentVariableContext()).isPresent();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static java.util.Optional<java.util.Map<String, MacroValue>> captureChatAction(WaitForChatAction action, ChatCapture capture) {
|
|
||||||
return captureChatAction(action, capture, currentVariableContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static java.util.Optional<java.util.Map<String, MacroValue>> captureChatAction(WaitForChatAction action, ChatCapture capture,
|
|
||||||
MacroVariableContext context) {
|
|
||||||
if (action == null || capture == null) return java.util.Optional.empty();
|
|
||||||
MacroCapturePattern.Mode mode = action.effectiveMatchMode();
|
|
||||||
if (mode == MacroCapturePattern.Mode.MATCH && action.patternJson != null && !action.patternJson.isBlank() && capture.displayComponent() != null) {
|
|
||||||
String captureJson = MacroChatText.serializeTextComponent(capture.displayComponent());
|
|
||||||
if (action.patternJson.equals(captureJson)) return java.util.Optional.of(chatValues(action, capture, java.util.Map.of()));
|
|
||||||
}
|
|
||||||
String pattern = MacroChatText.normalizeManualText(action.pattern);
|
|
||||||
if (mode == MacroCapturePattern.Mode.MATCH) {
|
|
||||||
MacroTemplate.Resolution patternResolution = MacroTemplate.resolve(pattern, context, Minecraft.getInstance());
|
|
||||||
if (!patternResolution.success()) return java.util.Optional.empty();
|
|
||||||
pattern = patternResolution.value();
|
|
||||||
}
|
|
||||||
if (pattern == null || pattern.isBlank()) return java.util.Optional.of(chatValues(action, capture, java.util.Map.of()));
|
|
||||||
if (mode != MacroCapturePattern.Mode.MATCH) {
|
|
||||||
var messageResult = MacroCapturePattern.match(mode, pattern, capture.message());
|
|
||||||
var result = messageResult.isPresent() ? messageResult : MacroCapturePattern.match(mode, pattern, capture.displayText());
|
|
||||||
return result.map(value -> chatValues(action, capture, value.values()));
|
|
||||||
}
|
|
||||||
int threshold = WaitForChatAction.clampFuzzyPercent(action.fuzzyPercent);
|
|
||||||
boolean matched = MacroChatText.fuzzyChatMatch(pattern, capture.message(), threshold)
|
|
||||||
|| MacroChatText.fuzzyChatMatch(pattern, capture.displayText(), threshold);
|
|
||||||
return matched ? java.util.Optional.of(chatValues(action, capture, java.util.Map.of())) : java.util.Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static java.util.Map<String, MacroValue> chatValues(WaitForChatAction action, ChatCapture capture,
|
|
||||||
java.util.Map<String, MacroValue> captures) {
|
|
||||||
java.util.Map<String, MacroValue> values = new java.util.LinkedHashMap<>(captures);
|
|
||||||
if (action.saveAs != null && !action.saveAs.isBlank()) {
|
|
||||||
java.util.Map<String, MacroValue> properties = new java.util.LinkedHashMap<>();
|
|
||||||
properties.put("sender", MacroValue.text(capture.sender()));
|
|
||||||
properties.put("message", MacroValue.text(capture.message()));
|
|
||||||
properties.put("display", MacroValue.text(capture.displayText()));
|
|
||||||
properties.put("source", MacroValue.text(capture.source().name()));
|
|
||||||
values.put(action.saveAs, MacroValue.structured(MacroValue.Kind.TEXT, capture.displayText(), properties));
|
|
||||||
}
|
|
||||||
return java.util.Map.copyOf(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChatCapture extractIncomingChat(Packet<?> packet) {
|
|
||||||
|
|
||||||
if (packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket systemChat) {
|
|
||||||
Component content = systemChat.content();
|
|
||||||
if (content == null) return null;
|
|
||||||
String raw = content.getString();
|
|
||||||
if (raw == null || raw.isBlank()) return null;
|
|
||||||
return buildChatCapture("", raw, raw, content, ChatSource.PLAYER);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof net.minecraft.network.protocol.game.ClientboundDisguisedChatPacket disguisedChat) {
|
|
||||||
Component content = disguisedChat.message();
|
|
||||||
String text = content == null ? null : content.getString();
|
|
||||||
if (text == null || text.isBlank()) return null;
|
|
||||||
Component decorated = disguisedChat.chatType().decorate(content);
|
|
||||||
String sender = disguisedChat.chatType().name().getString();
|
|
||||||
return buildChatCapture(sender, text, null, decorated, ChatSource.PLAYER);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket playerChat) {
|
|
||||||
|
|
||||||
Component content = playerChat.unsignedContent() != null
|
|
||||||
? playerChat.unsignedContent()
|
|
||||||
: Component.literal(playerChat.body().content());
|
|
||||||
String message = content.getString();
|
|
||||||
if (message == null || message.isBlank()) message = playerChat.body().content();
|
|
||||||
if (message == null || message.isBlank()) return null;
|
|
||||||
|
|
||||||
Component decorated = playerChat.chatType().decorate(content);
|
|
||||||
String sender = playerChat.chatType().name().getString();
|
|
||||||
if (sender == null || sender.isBlank()) {
|
|
||||||
String byUuid = MacroChatText.resolvePlayerName(playerChat.sender());
|
|
||||||
if (byUuid != null && !byUuid.isBlank()) sender = byUuid;
|
|
||||||
}
|
|
||||||
return buildChatCapture(sender, message, null, decorated, ChatSource.PLAYER);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChatCapture buildChatCapture(String sender, String message, String displayText, Component displayComponent, ChatSource source) {
|
|
||||||
String cleanMessage = MacroChatText.sanitizeChatText(message);
|
|
||||||
if (cleanMessage.isBlank()) return null;
|
|
||||||
String cleanSender = MacroChatText.sanitizeChatText(sender);
|
|
||||||
String cleanDisplay = MacroChatText.sanitizeChatText(displayText);
|
|
||||||
Component rendered = MacroChatText.literalizeTextComponent(displayComponent);
|
|
||||||
String display = cleanDisplay;
|
|
||||||
if (rendered == null || rendered.getString().isBlank()) {
|
|
||||||
rendered = cleanSender.isBlank()
|
|
||||||
? Component.literal(cleanMessage)
|
|
||||||
: Component.literal("<" + cleanSender + "> ").append(Component.literal(cleanMessage));
|
|
||||||
}
|
|
||||||
if (display == null || display.isBlank()) {
|
|
||||||
display = MacroChatText.sanitizeChatText(rendered.getString());
|
|
||||||
}
|
|
||||||
ChatSource resolvedSource = source != null ? source : ChatSource.PLAYER;
|
|
||||||
return new ChatCapture(cleanSender, cleanMessage, MacroChatText.sanitizeChatText(display), rendered, resolvedSource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String normalizeManualText(String text) {
|
public static String normalizeManualText(String text) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue