mirror of
https://github.com/AutismDevelopment/Autism-Client.git
synced 2026-10-28 19:39:48 +00:00
Release the tag update
This commit is contained in:
commit
cb6910b424
1300 changed files with 317229 additions and 0 deletions
20
src/main/java/autismclient/AutismClientAddon.java
Normal file
20
src/main/java/autismclient/AutismClientAddon.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package autismclient;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public final class AutismClientAddon {
|
||||
public static final Logger LOG = LogUtils.getLogger();
|
||||
public static final String MOD_ID = "autism";
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
|
||||
public static final java.io.File FOLDER = FabricLoader.getInstance().getConfigDir().resolve("autism").toFile();
|
||||
|
||||
static {
|
||||
FOLDER.mkdirs();
|
||||
}
|
||||
|
||||
private AutismClientAddon() {}
|
||||
}
|
||||
242
src/main/java/autismclient/AutismClientMod.java
Normal file
242
src/main/java/autismclient/AutismClientMod.java
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
package autismclient;
|
||||
|
||||
import autismclient.gui.vanillaui.components.UiText;
|
||||
import autismclient.modules.PackAutoReconnectState;
|
||||
import autismclient.modules.AutismModule;
|
||||
import autismclient.render.AutismFemaleBodyRenderer;
|
||||
import autismclient.security.AutismItemNbtSanity;
|
||||
import autismclient.security.AutismProtector;
|
||||
import autismclient.security.AutismProtectorPackStrip;
|
||||
import autismclient.security.AutismProtectorServerPackFailureGuard;
|
||||
import autismclient.security.AutismProtectorTracker;
|
||||
import autismclient.security.AutismProtectorVanillaKeys;
|
||||
import autismclient.util.AutismInstaBreakRenderer;
|
||||
import autismclient.util.AutismFakeGamemode;
|
||||
import autismclient.util.AutismJoinMacroController;
|
||||
import autismclient.util.AutismLANSync;
|
||||
import autismclient.util.AutismOverlayManager;
|
||||
import autismclient.util.AutismSvgHudLogo;
|
||||
import autismclient.util.AutismWindowBranding;
|
||||
import autismclient.util.AutismSharedState;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
|
||||
public final class AutismClientMod implements ClientModInitializer {
|
||||
|
||||
private static void runSafe(String where, Runnable action) {
|
||||
try {
|
||||
action.run();
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[Autism] client-event '{}' failed; isolated to protect the client", where, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static final java.util.concurrent.ConcurrentHashMap<String, Long> LAST_TICK_ERROR_MS = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
|
||||
private static void logTickError(String where, Throwable t) {
|
||||
long now = System.currentTimeMillis();
|
||||
Long last = LAST_TICK_ERROR_MS.get(where);
|
||||
if (last != null && now - last < 5000L) return;
|
||||
LAST_TICK_ERROR_MS.put(where, now);
|
||||
AutismClientAddon.LOG.warn("[Autism] tick '{}' failed; isolated to protect the client", where, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onInitializeClient() {
|
||||
AutismClientAddon.FOLDER.mkdirs();
|
||||
|
||||
runSafe("initialize", () -> AutismModule.get().initialize());
|
||||
|
||||
AutismProtectorTracker.bootstrap();
|
||||
|
||||
if (!AutismProtector.isOverlapExternalProtectorPresent()) {
|
||||
AutismProtectorVanillaKeys.primeAsync();
|
||||
}
|
||||
if (AutismProtector.isFullExternalProtectorPresent()) {
|
||||
AutismClientAddon.LOG.info(
|
||||
"[AutismProtector] External protection mod detected; deferring all anti-fingerprint mixins to it.");
|
||||
} else if (AutismProtector.isExploitPreventerPresent()) {
|
||||
AutismClientAddon.LOG.info(
|
||||
"[AutismProtector] ExploitPreventer detected; deferring overlapping protections while keeping brand/channel hiding active.");
|
||||
} else {
|
||||
AutismClientAddon.LOG.info(
|
||||
"[AutismProtector] Built-in anti-fingerprint layer active.");
|
||||
}
|
||||
AutismInstaBreakRenderer.initialize();
|
||||
autismclient.util.AutismFreecamHighlightRenderer.initialize();
|
||||
autismclient.util.AutismSkeletonRenderer.initialize();
|
||||
autismclient.util.AutismWorldHighlightRenderer.initialize();
|
||||
autismclient.util.AutismScaffoldPlaceRenderer.initialize();
|
||||
autismclient.util.AutismKillAuraRenderer.initialize();
|
||||
|
||||
autismclient.modules.HoleEspModule.initialize();
|
||||
AutismFemaleBodyRenderer.initialize();
|
||||
autismclient.commands.AutismCommands.init();
|
||||
|
||||
autismclient.addons.AddonManager.init();
|
||||
|
||||
ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(new net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener() {
|
||||
@Override
|
||||
public net.minecraft.resources.Identifier getFabricId() {
|
||||
return net.minecraft.resources.Identifier.fromNamespaceAndPath("autismclient", "ui_assets");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(net.minecraft.server.packs.resources.ResourceManager manager) {
|
||||
UiText.onClientResourceReload();
|
||||
AutismSvgHudLogo.clear();
|
||||
}
|
||||
});
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
|
||||
try {
|
||||
if (autismclient.util.AutismBlockNbtCapture.hasTickWork()) {
|
||||
autismclient.util.AutismBlockNbtCapture.tick(client);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logTickError("blockNbtCapture", t);
|
||||
}
|
||||
try {
|
||||
autismclient.modules.ScaffoldModule.endMovementTick();
|
||||
} catch (Throwable t) {
|
||||
logTickError("scaffoldEndClick", t);
|
||||
}
|
||||
try {
|
||||
AutismWindowBranding.apply(client);
|
||||
} catch (Throwable ignored) { }
|
||||
try {
|
||||
autismclient.security.AutismPackResponseScheduler.tick();
|
||||
} catch (Throwable t) {
|
||||
logTickError("packResponses", t);
|
||||
}
|
||||
try {
|
||||
AutismModule.get().tick();
|
||||
} catch (Throwable t) {
|
||||
logTickError("module", t);
|
||||
}
|
||||
try {
|
||||
|
||||
autismclient.modules.ModuleOreSim.tickRetention();
|
||||
} catch (Throwable t) {
|
||||
logTickError("oreSimRetention", t);
|
||||
}
|
||||
try {
|
||||
autismclient.util.multi.PacketTeleportController.tick(client);
|
||||
} catch (Throwable t) {
|
||||
logTickError("pacedTp", t);
|
||||
}
|
||||
try {
|
||||
AutismJoinMacroController.onClientTick(client);
|
||||
} catch (Throwable t) {
|
||||
logTickError("joinMacro", t);
|
||||
}
|
||||
try {
|
||||
autismclient.util.AutismLagWatchdog.onClientTick(client);
|
||||
} catch (Throwable t) {
|
||||
logTickError("lagWatchdog", t);
|
||||
}
|
||||
try {
|
||||
|
||||
if (!autismclient.util.AutismLiteVariant.enabled()) {
|
||||
autismclient.util.multi.MultiTakeoverState.tick();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logTickError("multiTakeover", t);
|
||||
}
|
||||
try {
|
||||
autismclient.util.AutismRemoteView.tick();
|
||||
} catch (Throwable t) {
|
||||
logTickError("remoteView", t);
|
||||
}
|
||||
try {
|
||||
|
||||
if (!autismclient.util.AutismLiteVariant.enabled()
|
||||
&& autismclient.util.AutismDiscordLogin.hasSession() && autismclient.util.mm.MmPrefs.get().autoJoinPublic())
|
||||
autismclient.util.mm.MatchmakingManager.get().autoJoinNudge();
|
||||
} catch (Throwable t) {
|
||||
logTickError("mmAutoJoin", t);
|
||||
}
|
||||
});
|
||||
ClientTickEvents.END_LEVEL_TICK.register(level ->
|
||||
runSafe("lan.levelTick", () -> AutismLANSync.getInstance().onLevelTick(level.getGameTime())));
|
||||
ClientConfigurationConnectionEvents.INIT.register((listener, client) -> {
|
||||
runSafe("cfg.hideMenuOverlays", () -> AutismModule.get().hideMenuOverlays());
|
||||
runSafe("cfg.configStarted", () -> AutismModule.get().onConfigurationConnectionStarted());
|
||||
runSafe("cfg.joinMacro", () -> AutismJoinMacroController.onConfigurationInit(listener));
|
||||
});
|
||||
ClientConfigurationConnectionEvents.DISCONNECT.register((listener, client) -> {
|
||||
runSafe("cfg.remoteView", () -> autismclient.util.AutismRemoteView.stop(false));
|
||||
runSafe("cfg.persist", autismclient.util.AutismConfig::enqueuePendingSaveNow);
|
||||
runSafe("cfg.pacedTp", () -> autismclient.util.multi.PacketTeleportController.cancelAll("connection changed"));
|
||||
runSafe("cfg.fakeGamemode", AutismFakeGamemode::clear);
|
||||
runSafe("cfg.instaBreak", AutismInstaBreakRenderer::clear);
|
||||
runSafe("cfg.packFailureGuard", AutismProtectorServerPackFailureGuard::clear);
|
||||
runSafe("cfg.packResponses", autismclient.security.AutismPackResponseScheduler::clearAll);
|
||||
runSafe("cfg.hideOverlays", () -> AutismOverlayManager.get().hideAllInteractiveOverlays());
|
||||
runSafe("cfg.onGameLeft", () -> AutismModule.get().onGameLeft());
|
||||
runSafe("cfg.joinMacro", AutismJoinMacroController::onConfigurationDisconnect);
|
||||
});
|
||||
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
|
||||
runSafe("join.pacedTp", () -> autismclient.util.multi.PacketTeleportController.cancelAll("connection changed"));
|
||||
|
||||
runSafe("join.movementGrace", autismclient.modules.AutismJoinGrace::onJoin);
|
||||
runSafe("join.hideMenuOverlays", () -> AutismModule.get().hideMenuOverlays());
|
||||
runSafe("join.remember", () -> PackAutoReconnectState.remember(client.getCurrentServer()));
|
||||
runSafe("join.onGameJoin", () -> AutismModule.get().onGameJoin());
|
||||
runSafe("join.joinMacro", AutismJoinMacroController::onPlayJoin);
|
||||
runSafe("join.surfaceFailures", autismclient.addons.AddonManager::surfaceFailuresOnJoin);
|
||||
runSafe("join.lagWatchdog", autismclient.util.AutismLagWatchdog::reset);
|
||||
runSafe("join.macroEditor", autismclient.util.AutismMacroEditorOverlay::onPlayJoin);
|
||||
});
|
||||
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
|
||||
runSafe("leave.remoteView", () -> autismclient.util.AutismRemoteView.stop(false));
|
||||
runSafe("leave.persist", autismclient.util.AutismConfig::enqueuePendingSaveNow);
|
||||
runSafe("leave.stopMacro", () -> {
|
||||
if (autismclient.util.AutismConfig.getGlobal().stopMacroOnLeave) autismclient.util.macro.MacroExecutor.stop();
|
||||
});
|
||||
runSafe("leave.pacedTp", () -> autismclient.util.multi.PacketTeleportController.cancelAll("disconnected"));
|
||||
runSafe("leave.movementGrace", autismclient.modules.AutismJoinGrace::clear);
|
||||
runSafe("leave.fakeGamemode", AutismFakeGamemode::clear);
|
||||
runSafe("leave.instaBreak", AutismInstaBreakRenderer::clear);
|
||||
runSafe("leave.packStrip", AutismProtectorPackStrip::clearAll);
|
||||
runSafe("leave.packResponses", autismclient.security.AutismPackResponseScheduler::clearAll);
|
||||
runSafe("leave.packFailureGuard", AutismProtectorServerPackFailureGuard::clear);
|
||||
runSafe("leave.macroEditor", autismclient.util.AutismMacroEditorOverlay::onPlayDisconnect);
|
||||
runSafe("leave.lanSync", () -> AutismLANSync.getInstance().onGameDisconnected());
|
||||
runSafe("leave.hideOverlays", () -> AutismOverlayManager.get().hideAllInteractiveOverlays());
|
||||
runSafe("leave.joinMacro", AutismJoinMacroController::onGameLeft);
|
||||
runSafe("leave.onGameLeft", () -> AutismModule.get().onGameLeft());
|
||||
runSafe("leave.lagWatchdog", autismclient.util.AutismLagWatchdog::reset);
|
||||
});
|
||||
ItemTooltipCallback.EVENT.register((stack, tooltipContext, tooltipType, lines) -> {
|
||||
|
||||
AutismItemNbtSanity.scrubUnsafeTooltipLines(lines);
|
||||
AutismItemNbtSanity.trimTooltipLines(lines);
|
||||
AutismModule.get().appendTooltip(stack, lines);
|
||||
});
|
||||
|
||||
ClientLifecycleEvents.CLIENT_STOPPING.register(client -> {
|
||||
|
||||
if (!autismclient.util.AutismLiteVariant.enabled()) {
|
||||
autismclient.util.mm.MatchmakingManager.get().shutdownLeave();
|
||||
}
|
||||
|
||||
autismclient.util.AutismConfig.flushPendingSaves(2000L);
|
||||
});
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
|
||||
if (!autismclient.util.AutismLiteVariant.enabled()) {
|
||||
autismclient.util.mm.MatchmakingManager.get().shutdownLeave();
|
||||
}
|
||||
autismclient.util.AutismConfig.flushPendingSaves(2000L);
|
||||
}, "mm-shutdown-leave"));
|
||||
}
|
||||
}
|
||||
530
src/main/java/autismclient/addons/AddonManager.java
Normal file
530
src/main/java/autismclient/addons/AddonManager.java
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
package autismclient.addons;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.api.ApiVersion;
|
||||
import autismclient.api.AutismAddon;
|
||||
import autismclient.util.AutismConfig;
|
||||
import autismclient.util.AutismNotifications;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
|
||||
import net.fabricmc.loader.api.metadata.CustomValue;
|
||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||
import net.fabricmc.loader.api.metadata.Person;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class AddonManager {
|
||||
private static final int MAX_UI_ADDON_NAME_LENGTH = 22;
|
||||
private static final List<AutismAddon> LOADED = new ArrayList<>();
|
||||
private static final Map<String, AddonReport> REPORTS = new LinkedHashMap<>();
|
||||
|
||||
private record LoadingContext(String addonId, String addonName) {}
|
||||
private static volatile LoadingContext currentContext = null;
|
||||
|
||||
private static boolean failuresSurfaced = false;
|
||||
|
||||
private AddonManager() {}
|
||||
|
||||
public static void init() {
|
||||
LOADED.clear();
|
||||
REPORTS.clear();
|
||||
failuresSurfaced = false;
|
||||
|
||||
AutismConfig config = AutismConfig.getGlobal();
|
||||
List<LoadedAddon> accepted = new ArrayList<>();
|
||||
for (EntrypointContainer<AutismAddon> container : FabricLoader.getInstance()
|
||||
.getEntrypointContainers("autism", AutismAddon.class)) {
|
||||
ModContainer mod;
|
||||
try {
|
||||
mod = container.getProvider();
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.error("[Addons] Failed to inspect an addon entrypoint", t);
|
||||
continue;
|
||||
}
|
||||
|
||||
String modId = mod.getMetadata().getId();
|
||||
AddonReport report = ensureReport(mod.getMetadata());
|
||||
if (isDisabledOnRestart(modId)) {
|
||||
report.markDisabled("Disabled in AUTISM config. Enable it and restart to load.");
|
||||
continue;
|
||||
}
|
||||
|
||||
AutismAddon addon;
|
||||
try {
|
||||
addon = container.getEntrypoint();
|
||||
} catch (Throwable t) {
|
||||
report.markFailed("construct", "Failed to construct entrypoint: " + shortError(t));
|
||||
AutismClientAddon.LOG.error("[Addons] Failed to construct addon '{}'", modId, t);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
int declared = addon.apiVersion();
|
||||
report.setApiVersion(declared);
|
||||
if (declared <= 0) {
|
||||
report.markFailed("apiVersion", "Invalid API version " + declared + ".");
|
||||
AutismClientAddon.LOG.warn("[Addons] Addon '{}' declared invalid API v{}; skipping it.", modId, declared);
|
||||
continue;
|
||||
}
|
||||
if (declared > ApiVersion.CURRENT) {
|
||||
report.markNeedsNewerApi("Needs API v" + declared + ", host provides v" + ApiVersion.CURRENT + ".");
|
||||
AutismClientAddon.LOG.warn("[Addons] Addon '{}' needs API v{} but this client provides v{}; skipping it.",
|
||||
modId, declared, ApiVersion.CURRENT);
|
||||
continue;
|
||||
}
|
||||
if (declared < ApiVersion.CURRENT) {
|
||||
AutismClientAddon.LOG.info("[Addons] Addon '{}' built against API v{} (current v{}); loading.",
|
||||
modId, declared, ApiVersion.CURRENT);
|
||||
}
|
||||
applyMetadata(addon, mod.getMetadata());
|
||||
report.copyRuntimeMetadata(addon);
|
||||
accepted.add(new LoadedAddon(modId, addon));
|
||||
} catch (AbstractMethodError e) {
|
||||
report.markMissingApiVersion("Missing apiVersion() override.");
|
||||
AutismClientAddon.LOG.error("[Addons] Addon '{}' does not declare apiVersion(); skipping it.", modId, e);
|
||||
} catch (Throwable t) {
|
||||
report.markFailed("prepare", "Failed to prepare: " + shortError(t));
|
||||
AutismClientAddon.LOG.error("[Addons] Error preparing addon '{}'", modId, t);
|
||||
}
|
||||
}
|
||||
|
||||
List<LoadedAddon> categoryReady = new ArrayList<>();
|
||||
for (LoadedAddon la : accepted) {
|
||||
if (runPhase(la, "onRegisterCategories", la.addon::onRegisterCategories)) {
|
||||
categoryReady.add(la);
|
||||
} else {
|
||||
invokeUnload(la);
|
||||
rollbackRegistrations(la.modId);
|
||||
}
|
||||
}
|
||||
|
||||
for (LoadedAddon la : categoryReady) {
|
||||
if (runPhase(la, "onInitialize", la.addon::onInitialize)) {
|
||||
LOADED.add(la.addon);
|
||||
AddonReport report = REPORTS.get(la.modId);
|
||||
if (report != null) report.markLoaded();
|
||||
} else {
|
||||
invokeUnload(la);
|
||||
rollbackRegistrations(la.modId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!LOADED.isEmpty()) {
|
||||
AutismClientAddon.LOG.info("[Addons] Loaded {} addon(s).", LOADED.size());
|
||||
}
|
||||
if (config.disabledAddonIds != null) {
|
||||
for (String id : config.disabledAddonIds) {
|
||||
if (id == null || id.isBlank() || REPORTS.containsKey(id)) continue;
|
||||
AddonReport report = new AddonReport(id.trim(), id.trim(), "", "", 0xFFAA66FF);
|
||||
report.markDisabled("Disabled in AUTISM config, but no matching addon entrypoint was discovered.");
|
||||
REPORTS.put(report.modId(), report);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean runPhase(LoadedAddon la, String phase, Runnable body) {
|
||||
String name = la.addon.name == null || la.addon.name.isBlank() ? la.modId : la.addon.name;
|
||||
currentContext = new LoadingContext(la.modId, name);
|
||||
AddonReport report = REPORTS.get(la.modId);
|
||||
if (report != null) report.setPhase(phase);
|
||||
try {
|
||||
body.run();
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
if (report != null) report.markFailed(phase, "Threw during " + phase + ": " + shortError(t));
|
||||
AutismClientAddon.LOG.error("[Addons] Addon '{}' threw during {}", la.modId, phase, t);
|
||||
return false;
|
||||
} finally {
|
||||
currentContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void invokeUnload(LoadedAddon la) {
|
||||
if (la == null || la.addon == null) return;
|
||||
try {
|
||||
la.addon.onUnload();
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[Addons] Addon '{}' threw during onUnload", la.modId, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void rollbackRegistrations(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
AddonReport report = REPORTS.get(addonId);
|
||||
rollbackStep(addonId, report, "macro actions", () -> autismclient.api.macro.MacroActionRegistry.unregisterAddon(addonId));
|
||||
rollbackStep(addonId, report, "custom menu adapters", () -> autismclient.api.custommenu.CustomMenuAdapterRegistry.unregisterAddon(addonId));
|
||||
rollbackStep(addonId, report, "presets", () -> autismclient.api.macro.MacroPresetRegistry.unregisterAddon(addonId));
|
||||
rollbackStep(addonId, report, "events", () -> autismclient.api.event.AddonEvents.unregisterAddon(addonId));
|
||||
rollbackStep(addonId, report, "hud", () -> autismclient.api.hud.HudElements.unregisterAddon(addonId));
|
||||
rollbackStep(addonId, report, "modules", () -> autismclient.modules.ModuleRegistry.unregisterAddonModules(addonId));
|
||||
rollbackStep(addonId, report, "commands", () -> autismclient.commands.AutismCommands.unregisterAddonCommands(addonId));
|
||||
if (report != null) report.addRollback("Rolled back partial registrations after load failure.");
|
||||
}
|
||||
|
||||
private static void rollbackStep(String addonId, AddonReport report, String kind, Runnable body) {
|
||||
try {
|
||||
body.run();
|
||||
} catch (Throwable t) {
|
||||
String detail = "Failed to rollback " + kind + ": " + shortError(t);
|
||||
if (report != null) report.addRollback(detail);
|
||||
AutismClientAddon.LOG.warn("[Addons] Addon '{}' rollback failed for {}", addonId, kind, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static AddonReport ensureReport(ModMetadata meta) {
|
||||
String modId = meta.getId();
|
||||
AddonReport existing = REPORTS.get(modId);
|
||||
if (existing != null) return existing;
|
||||
AddonReport report = new AddonReport(
|
||||
modId,
|
||||
meta.getName() == null || meta.getName().isBlank() ? modId : meta.getName(),
|
||||
meta.getVersion().getFriendlyString(),
|
||||
meta.getAuthors().stream().map(Person::getName).collect(Collectors.joining(", ")),
|
||||
metadataColor(meta, 0xFFAA66FF)
|
||||
);
|
||||
REPORTS.put(modId, report);
|
||||
return report;
|
||||
}
|
||||
|
||||
private static void applyMetadata(AutismAddon addon, ModMetadata meta) {
|
||||
addon.name = meta.getName();
|
||||
addon.authors = meta.getAuthors().stream().map(Person::getName).collect(Collectors.joining(", "));
|
||||
addon.color = metadataColor(meta, addon.color);
|
||||
}
|
||||
|
||||
private static int metadataColor(ModMetadata meta, int fallback) {
|
||||
CustomValue color = meta.containsCustomValue("autism:color") ? meta.getCustomValue("autism:color") : null;
|
||||
if (color != null && color.getType() == CustomValue.CvType.STRING) {
|
||||
return parseColor(color.getAsString(), fallback);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static int parseColor(String raw, int fallback) {
|
||||
try {
|
||||
String[] parts = raw.split(",");
|
||||
if (parts.length != 3) return fallback;
|
||||
int r = clamp(Integer.parseInt(parts[0].trim()));
|
||||
int g = clamp(Integer.parseInt(parts[1].trim()));
|
||||
int b = clamp(Integer.parseInt(parts[2].trim()));
|
||||
return 0xFF000000 | (r << 16) | (g << 8) | b;
|
||||
} catch (Exception e) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clamp(int v) { return Math.max(0, Math.min(255, v)); }
|
||||
|
||||
private static String currentId() {
|
||||
LoadingContext ctx = currentContext;
|
||||
return ctx == null ? null : ctx.addonId();
|
||||
}
|
||||
|
||||
private static String currentName() {
|
||||
LoadingContext ctx = currentContext;
|
||||
return ctx == null ? null : ctx.addonName();
|
||||
}
|
||||
|
||||
public static String currentAddonId() {
|
||||
return currentId();
|
||||
}
|
||||
|
||||
public static boolean isLifecycleActive() {
|
||||
String id = currentId();
|
||||
return id != null && !id.isBlank();
|
||||
}
|
||||
|
||||
public static String currentAddonName() {
|
||||
return currentName();
|
||||
}
|
||||
|
||||
public static String normalizedAddonName(String name, String fallback) {
|
||||
String value = name == null ? "" : name.replaceAll("\\s+", " ").trim();
|
||||
if (value.isBlank()) value = fallback == null ? "" : fallback.replaceAll("\\s+", " ").trim();
|
||||
return value.isBlank() ? "Addon" : value;
|
||||
}
|
||||
|
||||
public static String compactAddonName(String name, String fallback) {
|
||||
String value = normalizedAddonName(name, fallback);
|
||||
if (value.length() <= MAX_UI_ADDON_NAME_LENGTH) return value;
|
||||
return value.substring(0, Math.max(1, MAX_UI_ADDON_NAME_LENGTH - 3)).trim() + "...";
|
||||
}
|
||||
|
||||
public static String scopedId(String rawId) {
|
||||
String raw = rawId == null ? "" : rawId.trim();
|
||||
if (raw.contains(":")) return raw;
|
||||
String owner = currentId();
|
||||
if (owner == null || owner.isBlank()) return raw;
|
||||
return owner + ":" + raw;
|
||||
}
|
||||
|
||||
public static String scopedCategoryLabel(String customLabel) {
|
||||
LoadingContext ctx = currentContext;
|
||||
String id = ctx == null ? null : ctx.addonId();
|
||||
String name = ctx == null ? null : ctx.addonName();
|
||||
if (name == null || name.isBlank()) name = id;
|
||||
return compactAddonName(name, id);
|
||||
}
|
||||
|
||||
public static List<AutismAddon> loaded() {
|
||||
return Collections.unmodifiableList(LOADED);
|
||||
}
|
||||
|
||||
public static List<AddonReport> reports() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(REPORTS.values()));
|
||||
}
|
||||
|
||||
public static AddonReport report(String addonId) {
|
||||
return addonId == null ? null : REPORTS.get(addonId);
|
||||
}
|
||||
|
||||
public static File modsFolder() {
|
||||
return FabricLoader.getInstance().getGameDir().resolve("mods").toFile();
|
||||
}
|
||||
|
||||
public static boolean isDisabledOnRestart(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return false;
|
||||
AutismConfig config = AutismConfig.getGlobal();
|
||||
if (config.disabledAddonIds == null) return false;
|
||||
String needle = addonId.trim().toLowerCase(Locale.ROOT);
|
||||
for (String id : config.disabledAddonIds) {
|
||||
if (id != null && needle.equals(id.trim().toLowerCase(Locale.ROOT))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setDisabledOnRestart(String addonId, boolean disabled) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
AutismConfig config = AutismConfig.getGlobal();
|
||||
if (config.disabledAddonIds == null) config.disabledAddonIds = new ArrayList<>();
|
||||
String normalized = addonId.trim();
|
||||
boolean currently = isDisabledOnRestart(normalized);
|
||||
if (disabled && !currently) {
|
||||
config.disabledAddonIds.add(normalized);
|
||||
} else if (!disabled && currently) {
|
||||
config.disabledAddonIds.removeIf(id -> id != null && normalized.equalsIgnoreCase(id.trim()));
|
||||
}
|
||||
config.save();
|
||||
}
|
||||
|
||||
public static void recordAcceptedRegistration(String kind, String id) {
|
||||
AddonReport report = REPORTS.get(currentId());
|
||||
if (report != null) report.recordAccepted(kind, id);
|
||||
}
|
||||
|
||||
public static void recordRejectedRegistration(String addonId, String kind, String id, String reason) {
|
||||
AddonReport report = REPORTS.get(addonId == null || addonId.isBlank() ? currentId() : addonId);
|
||||
if (report != null) report.recordRejected(kind, id, reason);
|
||||
}
|
||||
|
||||
public static void recordRuntimeError(String addonId, String detail) {
|
||||
AddonReport report = REPORTS.get(addonId);
|
||||
if (report != null) report.recordRuntimeError(detail);
|
||||
}
|
||||
|
||||
public static void surfaceFailuresOnJoin() {
|
||||
if (failuresSurfaced) return;
|
||||
int issues = issueCount();
|
||||
if (issues <= 0) return;
|
||||
failuresSurfaced = true;
|
||||
AutismNotifications.warning(issues + " addon issue(s). Open Addons for details.");
|
||||
}
|
||||
|
||||
private static int issueCount() {
|
||||
int count = 0;
|
||||
for (AddonReport report : REPORTS.values()) {
|
||||
if (report.status().isIssue()) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static String shortError(Throwable t) {
|
||||
if (t == null) return "Unknown error";
|
||||
String msg = t.getMessage();
|
||||
return msg == null || msg.isBlank() ? t.getClass().getSimpleName() : t.getClass().getSimpleName() + ": " + msg;
|
||||
}
|
||||
|
||||
private record LoadedAddon(String modId, AutismAddon addon) {}
|
||||
|
||||
public enum AddonLoadStatus {
|
||||
LOADED("Loaded", false),
|
||||
DISABLED("Disabled on restart", false),
|
||||
FAILED("Failed", true),
|
||||
NEEDS_NEWER_API("Needs newer API", true),
|
||||
MISSING_API_VERSION("Missing apiVersion", true);
|
||||
|
||||
private final String label;
|
||||
private final boolean issue;
|
||||
|
||||
AddonLoadStatus(String label, boolean issue) {
|
||||
this.label = label;
|
||||
this.issue = issue;
|
||||
}
|
||||
|
||||
public String label() { return label; }
|
||||
public boolean isIssue() { return issue; }
|
||||
}
|
||||
|
||||
public static final class AddonReport {
|
||||
private final String modId;
|
||||
private String name;
|
||||
private String version;
|
||||
private String authors;
|
||||
private int color;
|
||||
private int apiVersion = -1;
|
||||
private int hostApiVersion = ApiVersion.CURRENT;
|
||||
private AddonLoadStatus status = AddonLoadStatus.FAILED;
|
||||
private String phase = "discovered";
|
||||
private String failureReason = "";
|
||||
private final Map<String, Integer> accepted = new LinkedHashMap<>();
|
||||
private final Map<String, Integer> rejected = new LinkedHashMap<>();
|
||||
private final List<String> rejectionDetails = new ArrayList<>();
|
||||
private final List<String> rollbackDetails = new ArrayList<>();
|
||||
private int runtimeErrors;
|
||||
private String lastRuntimeError = "";
|
||||
|
||||
private AddonReport(String modId, String name, String version, String authors, int color) {
|
||||
this.modId = modId == null ? "" : modId;
|
||||
this.name = name == null || name.isBlank() ? this.modId : name;
|
||||
this.version = version == null ? "" : version;
|
||||
this.authors = authors == null ? "" : authors;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
private void copyRuntimeMetadata(AutismAddon addon) {
|
||||
if (addon == null) return;
|
||||
if (addon.name != null && !addon.name.isBlank()) this.name = addon.name;
|
||||
if (addon.authors != null && !addon.authors.isBlank()) this.authors = addon.authors;
|
||||
this.color = addon.color;
|
||||
}
|
||||
|
||||
private void setApiVersion(int apiVersion) {
|
||||
this.apiVersion = apiVersion;
|
||||
}
|
||||
|
||||
private void setPhase(String phase) {
|
||||
this.phase = phase == null || phase.isBlank() ? this.phase : phase;
|
||||
}
|
||||
|
||||
private void markLoaded() {
|
||||
this.status = AddonLoadStatus.LOADED;
|
||||
this.phase = "loaded";
|
||||
this.failureReason = "";
|
||||
}
|
||||
|
||||
private void markDisabled(String reason) {
|
||||
this.status = AddonLoadStatus.DISABLED;
|
||||
this.phase = "disabled";
|
||||
this.failureReason = reason == null ? "" : reason;
|
||||
}
|
||||
|
||||
private void markFailed(String phase, String reason) {
|
||||
this.status = AddonLoadStatus.FAILED;
|
||||
this.phase = phase == null || phase.isBlank() ? "failed" : phase;
|
||||
this.failureReason = reason == null ? "" : reason;
|
||||
}
|
||||
|
||||
private void markNeedsNewerApi(String reason) {
|
||||
this.status = AddonLoadStatus.NEEDS_NEWER_API;
|
||||
this.phase = "apiVersion";
|
||||
this.failureReason = reason == null ? "" : reason;
|
||||
}
|
||||
|
||||
private void markMissingApiVersion(String reason) {
|
||||
this.status = AddonLoadStatus.MISSING_API_VERSION;
|
||||
this.phase = "apiVersion";
|
||||
this.failureReason = reason == null ? "" : reason;
|
||||
}
|
||||
|
||||
private void recordAccepted(String kind, String id) {
|
||||
String key = kind == null || kind.isBlank() ? "item" : kind;
|
||||
accepted.merge(key, 1, Integer::sum);
|
||||
}
|
||||
|
||||
private void recordRejected(String kind, String id, String reason) {
|
||||
String key = kind == null || kind.isBlank() ? "item" : kind;
|
||||
rejected.merge(key, 1, Integer::sum);
|
||||
String detail = key + (id == null || id.isBlank() ? "" : " " + id) + ": "
|
||||
+ (reason == null || reason.isBlank() ? "rejected" : reason);
|
||||
rejectionDetails.add(detail);
|
||||
}
|
||||
|
||||
private void addRollback(String detail) {
|
||||
if (detail != null && !detail.isBlank()) rollbackDetails.add(detail);
|
||||
}
|
||||
|
||||
private void recordRuntimeError(String detail) {
|
||||
runtimeErrors++;
|
||||
lastRuntimeError = detail == null || detail.isBlank() ? "Runtime error" : detail;
|
||||
}
|
||||
|
||||
public String modId() { return modId; }
|
||||
public String name() { return name; }
|
||||
public String version() { return version; }
|
||||
public String authors() { return authors; }
|
||||
public int color() { return color; }
|
||||
public int apiVersion() { return apiVersion; }
|
||||
public int hostApiVersion() { return hostApiVersion; }
|
||||
public AddonLoadStatus status() { return status; }
|
||||
public String phase() { return phase; }
|
||||
public String failureReason() { return failureReason; }
|
||||
public int runtimeErrors() { return runtimeErrors; }
|
||||
public String lastRuntimeError() { return lastRuntimeError; }
|
||||
public Map<String, Integer> accepted() { return Collections.unmodifiableMap(accepted); }
|
||||
public Map<String, Integer> rejected() { return Collections.unmodifiableMap(rejected); }
|
||||
public List<String> rejectionDetails() { return Collections.unmodifiableList(rejectionDetails); }
|
||||
public List<String> rollbackDetails() { return Collections.unmodifiableList(rollbackDetails); }
|
||||
|
||||
public int acceptedTotal() {
|
||||
int total = 0;
|
||||
for (int value : accepted.values()) total += value;
|
||||
return total;
|
||||
}
|
||||
|
||||
public int rejectedTotal() {
|
||||
int total = 0;
|
||||
for (int value : rejected.values()) total += value;
|
||||
return total;
|
||||
}
|
||||
|
||||
public String summaryCounts() {
|
||||
if (accepted.isEmpty()) return "No registered extensions";
|
||||
List<String> parts = new ArrayList<>();
|
||||
for (Map.Entry<String, Integer> entry : accepted.entrySet()) {
|
||||
parts.add(entry.getValue() + " " + entry.getKey());
|
||||
}
|
||||
return String.join(", ", parts);
|
||||
}
|
||||
|
||||
public String copyReport() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Addon: ").append(name).append('\n');
|
||||
sb.append("ID: ").append(modId).append('\n');
|
||||
if (!version.isBlank()) sb.append("Version: ").append(version).append('\n');
|
||||
if (!authors.isBlank()) sb.append("Authors: ").append(authors).append('\n');
|
||||
sb.append("Status: ").append(status.label()).append('\n');
|
||||
sb.append("Phase: ").append(phase).append('\n');
|
||||
sb.append("API: addon v").append(apiVersion < 0 ? "unknown" : Integer.toString(apiVersion))
|
||||
.append(", host v").append(hostApiVersion).append('\n');
|
||||
sb.append("Disabled on restart: ").append(isDisabledOnRestart(modId)).append('\n');
|
||||
if (!failureReason.isBlank()) sb.append("Reason: ").append(failureReason).append('\n');
|
||||
sb.append("Accepted: ").append(accepted).append('\n');
|
||||
if (!rejected.isEmpty()) sb.append("Rejected: ").append(rejected).append('\n');
|
||||
for (String detail : rejectionDetails) sb.append("- ").append(detail).append('\n');
|
||||
for (String detail : rollbackDetails) sb.append("- ").append(detail).append('\n');
|
||||
if (runtimeErrors > 0) {
|
||||
sb.append("Runtime errors: ").append(runtimeErrors).append('\n');
|
||||
sb.append("Last runtime error: ").append(lastRuntimeError).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/java/autismclient/api/AddonRegistrationResult.java
Normal file
16
src/main/java/autismclient/api/AddonRegistrationResult.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package autismclient.api;
|
||||
|
||||
public record AddonRegistrationResult(boolean accepted, String kind, String id, String reason) {
|
||||
|
||||
public static AddonRegistrationResult accepted(String kind, String id) {
|
||||
return new AddonRegistrationResult(true, clean(kind), clean(id), "Accepted");
|
||||
}
|
||||
|
||||
public static AddonRegistrationResult rejected(String kind, String id, String reason) {
|
||||
return new AddonRegistrationResult(false, clean(kind), clean(id), clean(reason));
|
||||
}
|
||||
|
||||
private static String clean(String value) {
|
||||
return value == null || value.isBlank() ? "" : value.trim();
|
||||
}
|
||||
}
|
||||
8
src/main/java/autismclient/api/ApiVersion.java
Normal file
8
src/main/java/autismclient/api/ApiVersion.java
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package autismclient.api;
|
||||
|
||||
public final class ApiVersion {
|
||||
|
||||
public static final int CURRENT = 3;
|
||||
|
||||
private ApiVersion() {}
|
||||
}
|
||||
20
src/main/java/autismclient/api/AutismAddon.java
Normal file
20
src/main/java/autismclient/api/AutismAddon.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package autismclient.api;
|
||||
|
||||
public abstract class AutismAddon {
|
||||
|
||||
public String name = "";
|
||||
|
||||
public String authors = "";
|
||||
|
||||
public int color = 0xFFFFFFFF;
|
||||
|
||||
public abstract int apiVersion();
|
||||
|
||||
public void onRegisterCategories() {}
|
||||
|
||||
public abstract void onInitialize();
|
||||
|
||||
public void onUnload() {}
|
||||
|
||||
public abstract String getPackage();
|
||||
}
|
||||
162
src/main/java/autismclient/api/AutismAddons.java
Normal file
162
src/main/java/autismclient/api/AutismAddons.java
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
package autismclient.api;
|
||||
|
||||
import autismclient.addons.AddonManager;
|
||||
import autismclient.api.event.AddonEvents;
|
||||
import autismclient.api.custommenu.CustomMenuAdapter;
|
||||
import autismclient.api.custommenu.CustomMenuAdapterRegistry;
|
||||
import autismclient.api.hud.HudElementProvider;
|
||||
import autismclient.api.hud.HudElements;
|
||||
import autismclient.api.macro.MacroActionEntry;
|
||||
import autismclient.api.macro.MacroActionRegistry;
|
||||
import autismclient.api.macro.MacroPresetRegistry;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.modules.Module;
|
||||
import autismclient.modules.ModuleCategory;
|
||||
import autismclient.modules.ModuleRegistry;
|
||||
import autismclient.util.macro.MacroAction;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class AutismAddons {
|
||||
private AutismAddons() {}
|
||||
|
||||
public static Modules modules() { return Modules.INSTANCE; }
|
||||
|
||||
public static Commands commands() { return Commands.INSTANCE; }
|
||||
|
||||
public static MacroActions macroActions() { return MacroActions.INSTANCE; }
|
||||
|
||||
public static Presets presets() { return Presets.INSTANCE; }
|
||||
|
||||
public static Hud hud() { return Hud.INSTANCE; }
|
||||
|
||||
public static Events events() { return Events.INSTANCE; }
|
||||
|
||||
public static CustomMenus customMenus() { return CustomMenus.INSTANCE; }
|
||||
|
||||
public static List<AutismAddon> list() { return AddonManager.loaded(); }
|
||||
|
||||
public static String id(String localId) { return AddonManager.scopedId(localId); }
|
||||
|
||||
public static int apiVersion() { return ApiVersion.CURRENT; }
|
||||
|
||||
public static final class Modules {
|
||||
static final Modules INSTANCE = new Modules();
|
||||
private Modules() {}
|
||||
|
||||
public boolean register(Module module) {
|
||||
return ModuleRegistry.registerAddonModule(module, AddonManager.currentAddonId());
|
||||
}
|
||||
|
||||
public AddonRegistrationResult registerDetailed(Module module) {
|
||||
return ModuleRegistry.registerAddonModuleDetailed(module, AddonManager.currentAddonId());
|
||||
}
|
||||
|
||||
public ModuleCategory autoCategory() {
|
||||
return ModuleCategory.registerAddon(AddonManager.currentAddonId(),
|
||||
AddonManager.scopedCategoryLabel(null));
|
||||
}
|
||||
|
||||
public ModuleCategory registerCategory(String label) {
|
||||
return ModuleCategory.registerAddon(AddonManager.currentAddonId(),
|
||||
AddonManager.scopedCategoryLabel(label));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Commands {
|
||||
static final Commands INSTANCE = new Commands();
|
||||
private Commands() {}
|
||||
|
||||
public void register(Command command) {
|
||||
autismclient.commands.AutismCommands.registerAddonCommand(command, AddonManager.currentAddonId());
|
||||
}
|
||||
|
||||
public AddonRegistrationResult registerDetailed(Command command) {
|
||||
return autismclient.commands.AutismCommands.registerAddonCommandDetailed(command, AddonManager.currentAddonId());
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MacroActions {
|
||||
static final MacroActions INSTANCE = new MacroActions();
|
||||
private MacroActions() {}
|
||||
|
||||
public boolean register(MacroActionEntry entry) {
|
||||
return MacroActionRegistry.register(entry);
|
||||
}
|
||||
|
||||
public AddonRegistrationResult registerDetailed(MacroActionEntry entry) {
|
||||
return MacroActionRegistry.registerDetailed(entry);
|
||||
}
|
||||
|
||||
public void registerCategory(String id, String label, int color) {
|
||||
MacroActionRegistry.registerScopedCategory(id, label, color);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CustomMenus {
|
||||
static final CustomMenus INSTANCE = new CustomMenus();
|
||||
private CustomMenus() {}
|
||||
|
||||
public boolean register(CustomMenuAdapter adapter) {
|
||||
return CustomMenuAdapterRegistry.register(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Presets {
|
||||
static final Presets INSTANCE = new Presets();
|
||||
private Presets() {}
|
||||
|
||||
public void registerCategory(String id, String label) {
|
||||
MacroPresetRegistry.registerScopedCategory(id, label);
|
||||
}
|
||||
|
||||
public void register(String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
MacroPresetRegistry.register(MacroPresetRegistry.ensureScopedCategory(null), label, tip, builder);
|
||||
}
|
||||
|
||||
public AddonRegistrationResult registerDetailed(String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
return MacroPresetRegistry.registerDetailed(MacroPresetRegistry.ensureScopedCategory(null), label, tip, builder);
|
||||
}
|
||||
|
||||
public void register(String categoryId, String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
MacroPresetRegistry.register(MacroPresetRegistry.ensureScopedCategory(categoryId), label, tip, builder);
|
||||
}
|
||||
|
||||
public AddonRegistrationResult registerDetailed(String categoryId, String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
return MacroPresetRegistry.registerDetailed(MacroPresetRegistry.ensureScopedCategory(categoryId), label, tip, builder);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Hud {
|
||||
static final Hud INSTANCE = new Hud();
|
||||
private Hud() {}
|
||||
|
||||
public boolean register(HudElementProvider provider) {
|
||||
return HudElements.register(provider);
|
||||
}
|
||||
|
||||
public AddonRegistrationResult registerDetailed(HudElementProvider provider) {
|
||||
return HudElements.registerDetailed(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Events {
|
||||
static final Events INSTANCE = new Events();
|
||||
private Events() {}
|
||||
|
||||
public void onTick(Consumer<Minecraft> listener) { AddonEvents.onTick(listener); }
|
||||
|
||||
public void onPacketSend(Predicate<Packet<?>> listener) { AddonEvents.onPacketSend(listener); }
|
||||
|
||||
public void onPacketReceive(Consumer<Packet<?>> listener) { AddonEvents.onPacketReceive(listener); }
|
||||
|
||||
public void onGameJoin(Runnable listener) { AddonEvents.onGameJoin(listener); }
|
||||
|
||||
public void onGameLeft(Runnable listener) { AddonEvents.onGameLeft(listener); }
|
||||
}
|
||||
}
|
||||
94
src/main/java/autismclient/api/SimpleAddon.java
Normal file
94
src/main/java/autismclient/api/SimpleAddon.java
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package autismclient.api;
|
||||
|
||||
import autismclient.api.macro.MacroActionEntry;
|
||||
import autismclient.api.macro.MacroPresetRegistry;
|
||||
import autismclient.api.macro.SimpleAction;
|
||||
import autismclient.api.macro.SimpleCondition;
|
||||
import autismclient.modules.Module;
|
||||
import autismclient.util.macro.MacroAction;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class SimpleAddon extends AutismAddon {
|
||||
private final int apiVersion;
|
||||
private final String rootPackage;
|
||||
|
||||
protected SimpleAddon(int apiVersion, String rootPackage) {
|
||||
this.apiVersion = apiVersion;
|
||||
this.rootPackage = rootPackage == null ? "" : rootPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int apiVersion() {
|
||||
return apiVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getPackage() {
|
||||
return rootPackage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInitialize() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected abstract void initialize();
|
||||
|
||||
protected final String id(String localId) {
|
||||
return AutismAddons.id(localId);
|
||||
}
|
||||
|
||||
protected final AddonRegistrationResult registerModule(Module module) {
|
||||
return AutismAddons.modules().registerDetailed(module);
|
||||
}
|
||||
|
||||
protected final AddonRegistrationResult registerAction(MacroActionEntry entry) {
|
||||
return AutismAddons.macroActions().registerDetailed(entry);
|
||||
}
|
||||
|
||||
protected final AddonRegistrationResult registerPreset(String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
return AutismAddons.presets().registerDetailed(label, tip, builder);
|
||||
}
|
||||
|
||||
protected final AddonRegistrationResult registerSimpleAction(
|
||||
String localId, String label, String tip, String icon, Consumer<Minecraft> runner) {
|
||||
return registerAction(simpleAction(localId, label, tip, icon, runner));
|
||||
}
|
||||
|
||||
protected final AddonRegistrationResult registerSimpleCondition(
|
||||
String localId, String label, String tip, String status, String icon, Predicate<Minecraft> predicate) {
|
||||
return registerAction(simpleCondition(localId, label, tip, status, icon, predicate));
|
||||
}
|
||||
|
||||
protected final MacroActionEntry simpleAction(
|
||||
String localId,
|
||||
String label,
|
||||
String tip,
|
||||
String icon,
|
||||
Consumer<Minecraft> runner
|
||||
) {
|
||||
String typeId = id(localId);
|
||||
return MacroActionEntry.builder(typeId, () -> new SimpleAction(typeId, label, icon, runner))
|
||||
.picker(label, tip)
|
||||
.build();
|
||||
}
|
||||
|
||||
protected final MacroActionEntry simpleCondition(
|
||||
String localId,
|
||||
String label,
|
||||
String tip,
|
||||
String status,
|
||||
String icon,
|
||||
Predicate<Minecraft> predicate
|
||||
) {
|
||||
String typeId = id(localId);
|
||||
return MacroActionEntry.builder(typeId, () -> new SimpleCondition(typeId, label, status, icon, predicate))
|
||||
.condition(label, tip)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
public interface CustomMenuAdapter {
|
||||
String id();
|
||||
|
||||
default boolean acceptsInbound(Packet<?> packet) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CustomMenuEvent inspectInbound(Packet<?> packet, String phase);
|
||||
|
||||
CustomMenuSubmitResult submit(CustomMenuSnapshot snapshot, CustomMenuSubmission submission);
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.addons.AddonManager;
|
||||
import autismclient.util.custommenu.VanillaDialogAdapter;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class CustomMenuAdapterRegistry {
|
||||
private record Registered(CustomMenuAdapter adapter, String owner) {}
|
||||
private record RegistrySnapshot(Registered[] ordered, Map<String, Registered> byId, List<String> ids) {}
|
||||
private static final Map<String, Registered> ADAPTERS = new LinkedHashMap<>();
|
||||
private static volatile RegistrySnapshot SNAPSHOT;
|
||||
|
||||
static {
|
||||
ADAPTERS.put(VanillaDialogAdapter.ID, new Registered(new VanillaDialogAdapter(), "autismclient"));
|
||||
publishSnapshot();
|
||||
}
|
||||
|
||||
private CustomMenuAdapterRegistry() {}
|
||||
|
||||
public static synchronized boolean register(CustomMenuAdapter adapter) {
|
||||
if (adapter == null || adapter.id() == null || adapter.id().isBlank()) return false;
|
||||
String id = adapter.id().trim().toLowerCase(java.util.Locale.ROOT);
|
||||
if (ADAPTERS.containsKey(id)) return false;
|
||||
ADAPTERS.put(id, new Registered(adapter, AddonManager.currentAddonId()));
|
||||
publishSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static synchronized void unregisterAddon(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
if (ADAPTERS.entrySet().removeIf(entry -> addonId.equals(entry.getValue().owner()))) {
|
||||
publishSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean acceptsInbound(Packet<?> packet) {
|
||||
if (packet == null) return false;
|
||||
for (Registered registered : SNAPSHOT.ordered()) {
|
||||
try {
|
||||
if (registered.adapter().acceptsInbound(packet)) return true;
|
||||
} catch (Throwable error) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static CustomMenuEvent inspect(Packet<?> packet, String phase) {
|
||||
if (packet == null) return CustomMenuEvent.NONE;
|
||||
for (Registered registered : SNAPSHOT.ordered()) {
|
||||
try {
|
||||
if (!registered.adapter().acceptsInbound(packet)) continue;
|
||||
CustomMenuEvent event = registered.adapter().inspectInbound(packet, phase);
|
||||
if (event != null && event.type() != CustomMenuEvent.Type.NONE) return event;
|
||||
} catch (Throwable error) {
|
||||
AutismClientAddon.LOG.warn("[CustomMenus] Adapter '{}' failed while reading a packet", registered.adapter().id(), error);
|
||||
}
|
||||
}
|
||||
return CustomMenuEvent.NONE;
|
||||
}
|
||||
|
||||
public static CustomMenuSubmitResult submit(CustomMenuSnapshot snapshot, CustomMenuSubmission submission) {
|
||||
if (snapshot == null) return CustomMenuSubmitResult.failure("No custom menu is open");
|
||||
Registered registered = SNAPSHOT.byId().get(snapshot.adapterId().toLowerCase(java.util.Locale.ROOT));
|
||||
if (registered == null) return CustomMenuSubmitResult.failure("Custom menu adapter is unavailable");
|
||||
try {
|
||||
CustomMenuSubmitResult result = registered.adapter().submit(snapshot, submission);
|
||||
return result == null ? CustomMenuSubmitResult.failure("Custom menu adapter returned no result") : result;
|
||||
} catch (Throwable error) {
|
||||
|
||||
AutismClientAddon.LOG.warn("[CustomMenus] Adapter '{}' failed while submitting a form ({})",
|
||||
registered.adapter().id(), error.getClass().getSimpleName());
|
||||
return CustomMenuSubmitResult.failure("Custom menu adapter failed");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> ids() {
|
||||
return SNAPSHOT.ids();
|
||||
}
|
||||
|
||||
private static void publishSnapshot() {
|
||||
Registered[] ordered = ADAPTERS.values().toArray(Registered[]::new);
|
||||
SNAPSHOT = new RegistrySnapshot(ordered, Map.copyOf(ADAPTERS), List.copyOf(ADAPTERS.keySet()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
public record CustomMenuButton(int index, String label, String actionId, Kind kind, String labelColor) {
|
||||
public enum Kind { CUSTOM, COMMAND, DIALOG, URL, CLIPBOARD, OTHER, EMPTY }
|
||||
|
||||
public CustomMenuButton(int index, String label, String actionId, Kind kind) {
|
||||
this(index, label, actionId, kind, "");
|
||||
}
|
||||
|
||||
public CustomMenuButton {
|
||||
label = label == null ? "" : label;
|
||||
actionId = actionId == null ? "" : actionId;
|
||||
kind = kind == null ? Kind.OTHER : kind;
|
||||
labelColor = labelColor == null ? "" : labelColor;
|
||||
}
|
||||
|
||||
public boolean serverRelevant() {
|
||||
return kind == Kind.CUSTOM || kind == Kind.COMMAND || kind == Kind.DIALOG;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
public record CustomMenuEvent(Type type, CustomMenuSnapshot snapshot) {
|
||||
public enum Type { NONE, OPEN, CLEAR }
|
||||
|
||||
public static final CustomMenuEvent NONE = new CustomMenuEvent(Type.NONE, null);
|
||||
public static final CustomMenuEvent CLEAR = new CustomMenuEvent(Type.CLEAR, null);
|
||||
|
||||
public static CustomMenuEvent open(CustomMenuSnapshot snapshot) {
|
||||
return new CustomMenuEvent(Type.OPEN, snapshot);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record CustomMenuInput(
|
||||
int index,
|
||||
String key,
|
||||
String label,
|
||||
Kind kind,
|
||||
String initialValue,
|
||||
int maxLength,
|
||||
double min,
|
||||
double max,
|
||||
double step,
|
||||
List<String> options
|
||||
) {
|
||||
public enum Kind { TEXT, BOOLEAN, NUMBER, OPTION }
|
||||
|
||||
public CustomMenuInput {
|
||||
key = key == null ? "" : key;
|
||||
label = label == null ? "" : label;
|
||||
kind = kind == null ? Kind.TEXT : kind;
|
||||
initialValue = initialValue == null ? "" : initialValue;
|
||||
options = options == null ? List.of() : List.copyOf(options);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record CustomMenuSnapshot(
|
||||
String adapterId,
|
||||
String phase,
|
||||
long generation,
|
||||
String title,
|
||||
List<CustomMenuInput> inputs,
|
||||
List<CustomMenuButton> buttons,
|
||||
Object adapterState
|
||||
) {
|
||||
public CustomMenuSnapshot {
|
||||
adapterId = adapterId == null ? "" : adapterId;
|
||||
phase = phase == null ? "" : phase;
|
||||
title = title == null ? "" : title;
|
||||
inputs = inputs == null ? List.of() : List.copyOf(inputs);
|
||||
buttons = buttons == null ? List.of() : List.copyOf(buttons);
|
||||
}
|
||||
|
||||
public CustomMenuSnapshot withConnectionState(String newPhase, long newGeneration) {
|
||||
return new CustomMenuSnapshot(adapterId, newPhase, newGeneration, title, inputs, buttons, adapterState);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record CustomMenuSubmission(Map<String, String> values, CustomMenuButton button) {
|
||||
public CustomMenuSubmission {
|
||||
values = values == null ? Map.of() : Map.copyOf(values);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package autismclient.api.custommenu;
|
||||
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import java.util.List;
|
||||
|
||||
public record CustomMenuSubmitResult(
|
||||
boolean success,
|
||||
String error,
|
||||
List<Packet<?>> packets,
|
||||
CustomMenuSnapshot replacement,
|
||||
ClickEvent clientAction
|
||||
) {
|
||||
public CustomMenuSubmitResult {
|
||||
error = error == null ? "" : error;
|
||||
packets = packets == null ? List.of() : List.copyOf(packets);
|
||||
}
|
||||
|
||||
public static CustomMenuSubmitResult failure(String error) {
|
||||
return new CustomMenuSubmitResult(false, error, List.of(), null, null);
|
||||
}
|
||||
|
||||
public static CustomMenuSubmitResult packets(List<Packet<?>> packets) {
|
||||
return new CustomMenuSubmitResult(true, "", packets, null, null);
|
||||
}
|
||||
|
||||
public static CustomMenuSubmitResult replacement(CustomMenuSnapshot replacement, ClickEvent clientAction) {
|
||||
return new CustomMenuSubmitResult(true, "", List.of(), replacement, clientAction);
|
||||
}
|
||||
}
|
||||
153
src/main/java/autismclient/api/event/AddonEvents.java
Normal file
153
src/main/java/autismclient/api/event/AddonEvents.java
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
package autismclient.api.event;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.addons.AddonManager;
|
||||
import autismclient.modules.PackHideState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class AddonEvents {
|
||||
private static final List<OwnedConsumer<Minecraft>> TICK = new CopyOnWriteArrayList<>();
|
||||
private static final List<OwnedPredicate<Packet<?>>> PACKET_SEND = new CopyOnWriteArrayList<>();
|
||||
private static final List<OwnedConsumer<Packet<?>>> PACKET_RECEIVE = new CopyOnWriteArrayList<>();
|
||||
private static final List<OwnedRunnable> GAME_JOIN = new CopyOnWriteArrayList<>();
|
||||
private static final List<OwnedRunnable> GAME_LEFT = new CopyOnWriteArrayList<>();
|
||||
|
||||
private static final long ERROR_LOG_INTERVAL_MS = 5_000L;
|
||||
private static final Map<String, Long> LAST_ERROR_LOG_MS = new ConcurrentHashMap<>();
|
||||
|
||||
private AddonEvents() {}
|
||||
|
||||
public static void onTick(Consumer<Minecraft> listener) {
|
||||
String owner = ownerOrReject("tick");
|
||||
if (owner != null && listener != null) {
|
||||
TICK.add(new OwnedConsumer<>(owner, listener));
|
||||
AddonManager.recordAcceptedRegistration("event", "tick");
|
||||
}
|
||||
}
|
||||
|
||||
public static void onPacketSend(Predicate<Packet<?>> listener) {
|
||||
String owner = ownerOrReject("packetSend");
|
||||
if (owner != null && listener != null) {
|
||||
PACKET_SEND.add(new OwnedPredicate<>(owner, listener));
|
||||
AddonManager.recordAcceptedRegistration("event", "packetSend");
|
||||
}
|
||||
}
|
||||
|
||||
public static void onPacketReceive(Consumer<Packet<?>> listener) {
|
||||
String owner = ownerOrReject("packetReceive");
|
||||
if (owner != null && listener != null) {
|
||||
PACKET_RECEIVE.add(new OwnedConsumer<>(owner, listener));
|
||||
AddonManager.recordAcceptedRegistration("event", "packetReceive");
|
||||
}
|
||||
}
|
||||
|
||||
public static void onGameJoin(Runnable listener) {
|
||||
String owner = ownerOrReject("gameJoin");
|
||||
if (owner != null && listener != null) {
|
||||
GAME_JOIN.add(new OwnedRunnable(owner, listener));
|
||||
AddonManager.recordAcceptedRegistration("event", "gameJoin");
|
||||
}
|
||||
}
|
||||
|
||||
public static void onGameLeft(Runnable listener) {
|
||||
String owner = ownerOrReject("gameLeft");
|
||||
if (owner != null && listener != null) {
|
||||
GAME_LEFT.add(new OwnedRunnable(owner, listener));
|
||||
AddonManager.recordAcceptedRegistration("event", "gameLeft");
|
||||
}
|
||||
}
|
||||
|
||||
private static String ownerOrReject(String event) {
|
||||
String owner = AddonManager.currentAddonId();
|
||||
if (owner == null || owner.isBlank()) {
|
||||
AutismClientAddon.LOG.warn("[Addons] Rejecting {} listener outside an addon lifecycle", event);
|
||||
AddonManager.recordRejectedRegistration(owner, "event", event, "registration outside an addon lifecycle - register from onInitialize() or onRegisterCategories()");
|
||||
return null;
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
||||
public static void unregisterAddon(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
TICK.removeIf(listener -> addonId.equals(listener.owner()));
|
||||
PACKET_SEND.removeIf(listener -> addonId.equals(listener.owner()));
|
||||
PACKET_RECEIVE.removeIf(listener -> addonId.equals(listener.owner()));
|
||||
GAME_JOIN.removeIf(listener -> addonId.equals(listener.owner()));
|
||||
GAME_LEFT.removeIf(listener -> addonId.equals(listener.owner()));
|
||||
LAST_ERROR_LOG_MS.keySet().removeIf(key -> key.startsWith(addonId + ":"));
|
||||
}
|
||||
|
||||
public static void fireTick(Minecraft mc) {
|
||||
if (PackHideState.isHardLocked()) return;
|
||||
if (TICK.isEmpty()) return;
|
||||
for (OwnedConsumer<Minecraft> l : TICK) {
|
||||
try { l.listener().accept(mc); } catch (Throwable t) { logThrottled(l.owner(), "tick", t); }
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasTickListeners() {
|
||||
if (PackHideState.isHardLocked()) return false;
|
||||
return !TICK.isEmpty();
|
||||
}
|
||||
|
||||
public static boolean hasPacketListeners() {
|
||||
if (PackHideState.isHardLocked()) return false;
|
||||
return !PACKET_SEND.isEmpty() || !PACKET_RECEIVE.isEmpty();
|
||||
}
|
||||
|
||||
public static boolean firePacketSend(Packet<?> packet) {
|
||||
if (PackHideState.isHardLocked()) return false;
|
||||
if (PACKET_SEND.isEmpty()) return false;
|
||||
boolean cancel = false;
|
||||
for (OwnedPredicate<Packet<?>> l : PACKET_SEND) {
|
||||
try { cancel |= l.listener().test(packet); } catch (Throwable t) { logThrottled(l.owner(), "packetSend", t); }
|
||||
}
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public static void firePacketReceive(Packet<?> packet) {
|
||||
if (PackHideState.isHardLocked()) return;
|
||||
if (PACKET_RECEIVE.isEmpty()) return;
|
||||
for (OwnedConsumer<Packet<?>> l : PACKET_RECEIVE) {
|
||||
try { l.listener().accept(packet); } catch (Throwable t) { logThrottled(l.owner(), "packetReceive", t); }
|
||||
}
|
||||
}
|
||||
|
||||
public static void fireGameJoin() {
|
||||
if (PackHideState.isHardLocked()) return;
|
||||
if (GAME_JOIN.isEmpty()) return;
|
||||
for (OwnedRunnable l : GAME_JOIN) {
|
||||
try { l.listener().run(); } catch (Throwable t) { logThrottled(l.owner(), "gameJoin", t); }
|
||||
}
|
||||
}
|
||||
|
||||
public static void fireGameLeft() {
|
||||
if (PackHideState.isHardLocked()) return;
|
||||
if (GAME_LEFT.isEmpty()) return;
|
||||
for (OwnedRunnable l : GAME_LEFT) {
|
||||
try { l.listener().run(); } catch (Throwable t) { logThrottled(l.owner(), "gameLeft", t); }
|
||||
}
|
||||
}
|
||||
|
||||
private static void logThrottled(String owner, String event, Throwable t) {
|
||||
long now = System.currentTimeMillis();
|
||||
String key = owner + ":" + event + ":" + t.getClass().getName();
|
||||
Long last = LAST_ERROR_LOG_MS.get(key);
|
||||
if (last != null && now - last < ERROR_LOG_INTERVAL_MS) return;
|
||||
LAST_ERROR_LOG_MS.put(key, now);
|
||||
AddonManager.recordRuntimeError(owner, event + " listener threw " + t.getClass().getSimpleName());
|
||||
AutismClientAddon.LOG.warn("[Addons] Addon '{}' {} event listener threw", owner, event, t);
|
||||
}
|
||||
|
||||
private record OwnedConsumer<T>(String owner, Consumer<T> listener) {}
|
||||
private record OwnedPredicate<T>(String owner, Predicate<T> listener) {}
|
||||
private record OwnedRunnable(String owner, Runnable listener) {}
|
||||
}
|
||||
27
src/main/java/autismclient/api/hud/HudElementProvider.java
Normal file
27
src/main/java/autismclient/api/hud/HudElementProvider.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package autismclient.api.hud;
|
||||
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphicsExtractor;
|
||||
|
||||
public interface HudElementProvider {
|
||||
|
||||
String id();
|
||||
|
||||
String label();
|
||||
|
||||
default String description() { return ""; }
|
||||
|
||||
int width();
|
||||
|
||||
int height();
|
||||
|
||||
void render(GuiGraphicsExtractor ctx, Font font, int x, int y, float alpha);
|
||||
|
||||
default boolean defaultEnabled() { return false; }
|
||||
|
||||
default String defaultAnchor() { return "TOP_LEFT"; }
|
||||
|
||||
default int defaultX() { return 0; }
|
||||
|
||||
default int defaultY() { return 0; }
|
||||
}
|
||||
104
src/main/java/autismclient/api/hud/HudElements.java
Normal file
104
src/main/java/autismclient/api/hud/HudElements.java
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
package autismclient.api.hud;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.api.AddonRegistrationResult;
|
||||
import autismclient.addons.AddonManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class HudElements {
|
||||
private static final Map<String, HudElementProvider> PROVIDERS = new LinkedHashMap<>();
|
||||
private static final Map<String, String> OWNERS = new LinkedHashMap<>();
|
||||
private static int revision;
|
||||
|
||||
private HudElements() {}
|
||||
|
||||
public static int revision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public static boolean register(HudElementProvider provider) {
|
||||
return registerDetailed(provider).accepted();
|
||||
}
|
||||
|
||||
public static AddonRegistrationResult registerDetailed(HudElementProvider provider) {
|
||||
if (provider == null) return AddonRegistrationResult.rejected("hud", "", "provider was null");
|
||||
String ownerAddonId = AddonManager.currentAddonId();
|
||||
if (ownerAddonId == null || ownerAddonId.isBlank()) {
|
||||
return reject(ownerAddonId, "", "registration outside an addon lifecycle - register from onInitialize() or onRegisterCategories()");
|
||||
}
|
||||
String id = provider.id();
|
||||
if (id == null || !id.contains(":")) {
|
||||
return reject(ownerAddonId, id, "non-namespaced id - scope it with AutismAddons.id(...) so it becomes addonId:localId");
|
||||
}
|
||||
if (!id.startsWith(ownerAddonId + ":")) {
|
||||
return reject(ownerAddonId, id, "foreign namespace - the id must start with your addon id; scope it with AutismAddons.id(...)");
|
||||
}
|
||||
if (PROVIDERS.containsKey(id)) {
|
||||
return reject(ownerAddonId, id, "duplicate id");
|
||||
}
|
||||
AddonRegistrationResult sizeCheck = validateSize(ownerAddonId, id, provider);
|
||||
if (!sizeCheck.accepted()) return sizeCheck;
|
||||
PROVIDERS.put(id, provider);
|
||||
OWNERS.put(id, ownerAddonId);
|
||||
revision++;
|
||||
AddonManager.recordAcceptedRegistration("hud", id);
|
||||
return AddonRegistrationResult.accepted("hud", id);
|
||||
}
|
||||
|
||||
private static AddonRegistrationResult validateSize(String ownerAddonId, String id, HudElementProvider provider) {
|
||||
int width;
|
||||
int height;
|
||||
try {
|
||||
width = provider.width();
|
||||
height = provider.height();
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[Hud] Rejecting HUD element '{}': sizing threw", id, t);
|
||||
AddonManager.recordRejectedRegistration(ownerAddonId, "hud", id, "sizing threw " + t.getClass().getSimpleName());
|
||||
return AddonRegistrationResult.rejected("hud", id, "sizing threw " + t.getClass().getSimpleName());
|
||||
}
|
||||
if (width <= 0) return reject(ownerAddonId, id, "width must be positive");
|
||||
if (height <= 0) return reject(ownerAddonId, id, "height must be positive");
|
||||
return AddonRegistrationResult.accepted("hud", id);
|
||||
}
|
||||
|
||||
private static AddonRegistrationResult reject(String ownerAddonId, String id, String reason) {
|
||||
AutismClientAddon.LOG.warn("[Hud] Rejecting HUD element '{}': {}", id, reason);
|
||||
AddonManager.recordRejectedRegistration(ownerAddonId, "hud", id, reason);
|
||||
return AddonRegistrationResult.rejected("hud", id, reason);
|
||||
}
|
||||
|
||||
public static void unregisterAddon(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
List<String> remove = new ArrayList<>();
|
||||
for (Map.Entry<String, String> owner : OWNERS.entrySet()) {
|
||||
if (addonId.equals(owner.getValue())) remove.add(owner.getKey());
|
||||
}
|
||||
if (remove.isEmpty()) return;
|
||||
for (String id : remove) {
|
||||
PROVIDERS.remove(id);
|
||||
OWNERS.remove(id);
|
||||
}
|
||||
revision++;
|
||||
}
|
||||
|
||||
public static HudElementProvider get(String id) {
|
||||
return id == null ? null : PROVIDERS.get(id);
|
||||
}
|
||||
|
||||
public static boolean isAddon(String id) {
|
||||
return id != null && PROVIDERS.containsKey(id);
|
||||
}
|
||||
|
||||
public static List<String> ids() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(PROVIDERS.keySet()));
|
||||
}
|
||||
|
||||
public static boolean isEmpty() {
|
||||
return PROVIDERS.isEmpty();
|
||||
}
|
||||
}
|
||||
83
src/main/java/autismclient/api/macro/ActionSchema.java
Normal file
83
src/main/java/autismclient/api/macro/ActionSchema.java
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import autismclient.gui.macro.editor.ActionFieldSchema;
|
||||
|
||||
public final class ActionSchema {
|
||||
private final ActionFieldSchema internal;
|
||||
|
||||
private ActionSchema(ActionFieldSchema internal) {
|
||||
this.internal = internal;
|
||||
}
|
||||
|
||||
public ActionFieldSchema internal() {
|
||||
return internal;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final ActionFieldSchema.Builder delegate = ActionFieldSchema.builder();
|
||||
|
||||
private Builder() {}
|
||||
|
||||
public Builder toggle(String key, String label) { delegate.toggle(key, label); return this; }
|
||||
|
||||
public Builder number(String key, String label) { delegate.number(key, label); return this; }
|
||||
|
||||
public Builder decimal(String key, String label) { delegate.decimal(key, label); return this; }
|
||||
|
||||
public Builder text(String key, String label) { delegate.text(key, label); return this; }
|
||||
|
||||
public Builder macroSelect(String key, String label) { delegate.macroSelect(key, label); return this; }
|
||||
|
||||
public Builder enumField(String key, String label, String... options) { delegate.enumField(key, label, options); return this; }
|
||||
|
||||
public Builder stringList(String key, String label) { delegate.stringList(key, label); return this; }
|
||||
|
||||
public Builder slot(String key, String label) { delegate.slot(key, label); return this; }
|
||||
|
||||
public Builder targetSummary(String key, String label) { delegate.targetSummary(key, label); return this; }
|
||||
|
||||
public Builder capturePacketClick(String key, String label) { delegate.capturePacketClick(key, label); return this; }
|
||||
|
||||
public Builder blockPos(String key, String label) { delegate.blockPos(key, label); return this; }
|
||||
|
||||
public Builder range(int min, int max) { delegate.range(min, max); return this; }
|
||||
|
||||
public Builder decRange(double min, double max) { delegate.decRange(min, max); return this; }
|
||||
|
||||
public Builder showWhen(String key) { delegate.showWhen(key); return this; }
|
||||
|
||||
public Builder hideWhen(String key) { delegate.hideWhen(key); return this; }
|
||||
|
||||
public Builder showWhenEnum(String key, String value) { delegate.showWhenEnum(key, value); return this; }
|
||||
|
||||
public Builder hideWhenEnum(String key, String value) { delegate.hideWhenEnum(key, value); return this; }
|
||||
|
||||
public Builder addLabel(String label) { delegate.addLabel(label); return this; }
|
||||
|
||||
public Builder xyzKeys(String... keys) { delegate.xyzKeys(keys); return this; }
|
||||
|
||||
public Builder xyzDouble(boolean value) { delegate.xyzDouble(value); return this; }
|
||||
|
||||
public Builder captureBlock() { delegate.captureBlock(); return this; }
|
||||
|
||||
public Builder captureEntity() { delegate.captureEntity(); return this; }
|
||||
|
||||
public Builder captureCatalog() { delegate.captureCatalog(); return this; }
|
||||
|
||||
public Builder captureItemSlot() { delegate.captureItemSlot(); return this; }
|
||||
|
||||
public Builder capturePacketName() { delegate.capturePacketName(); return this; }
|
||||
|
||||
public Builder exclusiveWith(String... keys) { delegate.exclusiveWith(keys); return this; }
|
||||
|
||||
public Builder dynamic() { delegate.dynamic(); return this; }
|
||||
|
||||
public ActionSchema build() {
|
||||
return new ActionSchema(delegate.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
61
src/main/java/autismclient/api/macro/AddonAction.java
Normal file
61
src/main/java/autismclient/api/macro/AddonAction.java
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import autismclient.util.macro.MacroAction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public abstract class AddonAction implements MacroAction {
|
||||
private final String typeId;
|
||||
|
||||
protected AddonAction(String typeId) {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
protected abstract void run(net.minecraft.client.Minecraft mc);
|
||||
|
||||
protected void save(CompoundTag tag) {}
|
||||
|
||||
protected void load(CompoundTag tag) {}
|
||||
|
||||
protected ActionSchema schema() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void execute(net.minecraft.client.Minecraft mc) {
|
||||
run(mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getTypeId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CompoundTag toTag() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString("type", typeId);
|
||||
save(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void fromTag(CompoundTag tag) {
|
||||
load(tag);
|
||||
}
|
||||
|
||||
protected static void putString(CompoundTag tag, String key, String value) { tag.putString(key, value == null ? "" : value); }
|
||||
protected static void putInt(CompoundTag tag, String key, int value) { tag.putInt(key, value); }
|
||||
protected static void putDouble(CompoundTag tag, String key, double value) { tag.putDouble(key, value); }
|
||||
protected static void putBool(CompoundTag tag, String key, boolean value) { tag.putBoolean(key, value); }
|
||||
|
||||
protected static String getString(CompoundTag tag, String key, String def) { return tag.getStringOr(key, def); }
|
||||
protected static int getInt(CompoundTag tag, String key, int def) { return tag.getIntOr(key, def); }
|
||||
protected static double getDouble(CompoundTag tag, String key, double def) { return tag.getDoubleOr(key, def); }
|
||||
protected static boolean getBool(CompoundTag tag, String key, boolean def) { return tag.getBooleanOr(key, def); }
|
||||
|
||||
public static ActionSchema schemaOf(MacroAction action) {
|
||||
return action instanceof AddonAction a ? a.schema()
|
||||
: action instanceof AddonContextAction c ? c.schemaInternal()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
10
src/main/java/autismclient/api/macro/AddonCondition.java
Normal file
10
src/main/java/autismclient/api/macro/AddonCondition.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public abstract class AddonCondition {
|
||||
|
||||
public abstract boolean check(Minecraft mc);
|
||||
|
||||
public void onComplete() {}
|
||||
}
|
||||
51
src/main/java/autismclient/api/macro/AddonContextAction.java
Normal file
51
src/main/java/autismclient/api/macro/AddonContextAction.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public abstract class AddonContextAction implements ContextMacroAction {
|
||||
private final String typeId;
|
||||
|
||||
protected AddonContextAction(String typeId) {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
protected void save(CompoundTag tag) {}
|
||||
|
||||
protected void load(CompoundTag tag) {}
|
||||
|
||||
protected ActionSchema schema() {
|
||||
return null;
|
||||
}
|
||||
|
||||
ActionSchema schemaInternal() {
|
||||
return schema();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getTypeId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final CompoundTag toTag() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString("type", typeId);
|
||||
save(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void fromTag(CompoundTag tag) {
|
||||
load(tag);
|
||||
}
|
||||
|
||||
protected static void putString(CompoundTag tag, String key, String value) { tag.putString(key, value == null ? "" : value); }
|
||||
protected static void putInt(CompoundTag tag, String key, int value) { tag.putInt(key, value); }
|
||||
protected static void putDouble(CompoundTag tag, String key, double value) { tag.putDouble(key, value); }
|
||||
protected static void putBool(CompoundTag tag, String key, boolean value) { tag.putBoolean(key, value); }
|
||||
|
||||
protected static String getString(CompoundTag tag, String key, String def) { return tag.getStringOr(key, def); }
|
||||
protected static int getInt(CompoundTag tag, String key, int def) { return tag.getIntOr(key, def); }
|
||||
protected static double getDouble(CompoundTag tag, String key, double def) { return tag.getDoubleOr(key, def); }
|
||||
protected static boolean getBool(CompoundTag tag, String key, boolean def) { return tag.getBooleanOr(key, def); }
|
||||
}
|
||||
14
src/main/java/autismclient/api/macro/ContextMacroAction.java
Normal file
14
src/main/java/autismclient/api/macro/ContextMacroAction.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import autismclient.util.macro.MacroAction;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public interface ContextMacroAction extends MacroAction {
|
||||
|
||||
void run(MacroExecutionContext ctx) throws InterruptedException;
|
||||
|
||||
@Override
|
||||
default void execute(Minecraft mc) {
|
||||
|
||||
}
|
||||
}
|
||||
103
src/main/java/autismclient/api/macro/MacroActionEntry.java
Normal file
103
src/main/java/autismclient/api/macro/MacroActionEntry.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import autismclient.addons.AddonManager;
|
||||
import autismclient.gui.macro.editor.ActionFieldSchema;
|
||||
import autismclient.util.macro.MacroAction;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class MacroActionEntry {
|
||||
|
||||
public enum Kind { ACTION, CONDITION }
|
||||
|
||||
private final String typeId;
|
||||
private final Supplier<MacroAction> factory;
|
||||
private final ActionFieldSchema schema;
|
||||
private String pickerCategory;
|
||||
private final String pickerLabel;
|
||||
private final String pickerTip;
|
||||
private final Kind kind;
|
||||
|
||||
private MacroActionEntry(Builder b) {
|
||||
this.typeId = b.typeId;
|
||||
this.factory = b.factory;
|
||||
this.schema = b.schema;
|
||||
this.pickerCategory = b.pickerCategory;
|
||||
this.pickerLabel = b.pickerLabel;
|
||||
this.pickerTip = b.pickerTip;
|
||||
this.kind = b.kind;
|
||||
}
|
||||
|
||||
public String typeId() { return typeId; }
|
||||
public Supplier<MacroAction> factory() { return factory; }
|
||||
public ActionFieldSchema schema() { return schema; }
|
||||
|
||||
public boolean hasPicker() { return pickerCategory != null && pickerLabel != null; }
|
||||
|
||||
boolean wantsPicker() { return pickerLabel != null; }
|
||||
public String pickerCategory() { return pickerCategory; }
|
||||
public String pickerLabel() { return pickerLabel; }
|
||||
public String pickerTip() { return pickerTip == null ? "" : pickerTip; }
|
||||
public Kind kind() { return kind; }
|
||||
public boolean isCondition() { return kind == Kind.CONDITION; }
|
||||
|
||||
void setPickerCategory(String categoryId) {
|
||||
this.pickerCategory = categoryId;
|
||||
}
|
||||
|
||||
public static Builder builder(String typeId, Supplier<MacroAction> factory) {
|
||||
return new Builder(typeId, factory);
|
||||
}
|
||||
|
||||
public static Builder local(String localId, Supplier<MacroAction> factory) {
|
||||
return new Builder(AddonManager.scopedId(localId), factory);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final String typeId;
|
||||
private final Supplier<MacroAction> factory;
|
||||
private ActionFieldSchema schema;
|
||||
private String pickerCategory;
|
||||
private String pickerLabel;
|
||||
private String pickerTip;
|
||||
private Kind kind = Kind.ACTION;
|
||||
|
||||
private Builder(String typeId, Supplier<MacroAction> factory) {
|
||||
this.typeId = typeId;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public Builder schema(ActionSchema schema) {
|
||||
this.schema = schema == null ? null : schema.internal();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder picker(String label, String tip) {
|
||||
return picker(null, label, tip);
|
||||
}
|
||||
|
||||
public Builder picker(String categoryId, String label, String tip) {
|
||||
this.pickerCategory = categoryId;
|
||||
this.pickerLabel = label;
|
||||
this.pickerTip = tip;
|
||||
this.kind = Kind.ACTION;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder condition(String label, String tip) {
|
||||
return condition(null, label, tip);
|
||||
}
|
||||
|
||||
public Builder condition(String categoryId, String label, String tip) {
|
||||
this.pickerCategory = categoryId;
|
||||
this.pickerLabel = label;
|
||||
this.pickerTip = tip;
|
||||
this.kind = Kind.CONDITION;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MacroActionEntry build() {
|
||||
return new MacroActionEntry(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
230
src/main/java/autismclient/api/macro/MacroActionRegistry.java
Normal file
230
src/main/java/autismclient/api/macro/MacroActionRegistry.java
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.api.AddonRegistrationResult;
|
||||
import autismclient.addons.AddonManager;
|
||||
import autismclient.gui.macro.editor.ActionFieldSchema;
|
||||
import autismclient.util.macro.MacroAction;
|
||||
import autismclient.util.macro.MacroActionType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class MacroActionRegistry {
|
||||
private static final Map<String, MacroActionEntry> ENTRIES = new LinkedHashMap<>();
|
||||
private static final Map<String, ActionCategory> CATEGORIES = new LinkedHashMap<>();
|
||||
private static final Map<String, String> ENTRY_OWNERS = new LinkedHashMap<>();
|
||||
private static final Map<String, String> CATEGORY_OWNERS = new LinkedHashMap<>();
|
||||
|
||||
private static final Map<String, ActionFieldSchema> RESOLVED_SCHEMAS = new LinkedHashMap<>();
|
||||
|
||||
public record ActionCategory(String id, String label, int color) {}
|
||||
|
||||
public static final int ADDON_AUTO_CATEGORY_COLOR = 0xFFAA66FF;
|
||||
|
||||
private static final String ADDON_CAT_PREFIX = "addon:";
|
||||
|
||||
private MacroActionRegistry() {}
|
||||
|
||||
public static String scopeCategoryId(String rawId) {
|
||||
if (AddonManager.isLifecycleActive()) {
|
||||
String addonId = AddonManager.currentAddonId();
|
||||
return ADDON_CAT_PREFIX + (addonId == null || addonId.isBlank() ? "shared" : addonId);
|
||||
}
|
||||
if (rawId != null && rawId.startsWith(ADDON_CAT_PREFIX)) return rawId;
|
||||
String addonId = AddonManager.currentAddonId();
|
||||
String base = addonId == null || addonId.isBlank() ? "shared" : addonId;
|
||||
return rawId == null || rawId.isBlank()
|
||||
? ADDON_CAT_PREFIX + base
|
||||
: ADDON_CAT_PREFIX + base + "/" + rawId;
|
||||
}
|
||||
|
||||
public static String ensureScopedCategory(String rawId) {
|
||||
String scopedId = scopeCategoryId(rawId);
|
||||
if (!CATEGORIES.containsKey(scopedId)) {
|
||||
registerCategoryInternal(scopedId, AddonManager.scopedCategoryLabel(null), ADDON_AUTO_CATEGORY_COLOR,
|
||||
AddonManager.currentAddonId());
|
||||
}
|
||||
return scopedId;
|
||||
}
|
||||
|
||||
public static void registerScopedCategory(String rawId, String label, int color) {
|
||||
String visible = AddonManager.isLifecycleActive() ? AddonManager.scopedCategoryLabel(null) : label;
|
||||
registerCategoryInternal(scopeCategoryId(rawId), visible, color,
|
||||
AddonManager.currentAddonId());
|
||||
}
|
||||
|
||||
public static void registerCategory(String id, String label, int color) {
|
||||
registerCategoryInternal(id, label, color, null);
|
||||
}
|
||||
|
||||
private static void registerCategoryInternal(String id, String label, int color, String ownerAddonId) {
|
||||
if (id == null || id.isBlank()) return;
|
||||
CATEGORIES.put(id, new ActionCategory(id, label == null || label.isBlank() ? id : label, color));
|
||||
if (ownerAddonId != null && !ownerAddonId.isBlank()) CATEGORY_OWNERS.put(id, ownerAddonId);
|
||||
}
|
||||
|
||||
public static ActionCategory category(String id) {
|
||||
return id == null ? null : CATEGORIES.get(id);
|
||||
}
|
||||
|
||||
public static boolean register(MacroActionEntry entry) {
|
||||
return registerDetailed(entry).accepted();
|
||||
}
|
||||
|
||||
public static AddonRegistrationResult registerDetailed(MacroActionEntry entry) {
|
||||
if (entry == null) return AddonRegistrationResult.rejected("macro action", "", "entry was null");
|
||||
String ownerAddonId = AddonManager.currentAddonId();
|
||||
if (ownerAddonId == null || ownerAddonId.isBlank()) {
|
||||
return reject(ownerAddonId, "macro action", "", "registration outside an addon lifecycle - register from onInitialize() or onRegisterCategories()");
|
||||
}
|
||||
|
||||
String typeId = entry.typeId();
|
||||
if (typeId == null || !typeId.contains(":")) {
|
||||
return reject(ownerAddonId, "macro action", typeId, "non-namespaced type id - scope it with AutismAddons.id(...) so it becomes addonId:localId");
|
||||
}
|
||||
if (!typeId.startsWith(ownerAddonId + ":")) {
|
||||
return reject(ownerAddonId, "macro action", typeId, "foreign namespace - the id must start with your addon id; scope it with AutismAddons.id(...)");
|
||||
}
|
||||
if (entry.factory() == null) {
|
||||
return reject(ownerAddonId, "macro action", typeId, "missing factory");
|
||||
}
|
||||
if (isBuiltinName(typeId)) {
|
||||
return reject(ownerAddonId, "macro action", typeId, "collides with a built-in type");
|
||||
}
|
||||
if (ENTRIES.containsKey(typeId)) {
|
||||
return reject(ownerAddonId, "macro action", typeId, "duplicate type id");
|
||||
}
|
||||
|
||||
if (entry.wantsPicker()) {
|
||||
entry.setPickerCategory(ensureScopedCategory(entry.pickerCategory()));
|
||||
}
|
||||
|
||||
ActionFieldSchema effectiveSchema = entry.schema();
|
||||
MacroAction probe;
|
||||
try {
|
||||
probe = entry.factory().get();
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[MacroActions] Rejecting action '{}' because its factory threw during validation", typeId, t);
|
||||
return reject(ownerAddonId, "macro action", typeId, "factory threw: " + errorName(t));
|
||||
}
|
||||
if (probe == null) {
|
||||
return reject(ownerAddonId, "macro action", typeId, "factory returned null");
|
||||
}
|
||||
if (effectiveSchema == null) {
|
||||
ActionSchema declared = AddonAction.schemaOf(probe);
|
||||
if (declared != null) effectiveSchema = declared.internal();
|
||||
}
|
||||
String validationError = validateProbe(typeId, entry, probe, effectiveSchema);
|
||||
if (validationError != null) return reject(ownerAddonId, "macro action", typeId, validationError);
|
||||
|
||||
ENTRIES.put(typeId, entry);
|
||||
ENTRY_OWNERS.put(typeId, ownerAddonId);
|
||||
if (effectiveSchema != null) RESOLVED_SCHEMAS.put(typeId, effectiveSchema);
|
||||
AddonManager.recordAcceptedRegistration("macro action", typeId);
|
||||
return AddonRegistrationResult.accepted("macro action", typeId);
|
||||
}
|
||||
|
||||
private static AddonRegistrationResult reject(String ownerAddonId, String kind, String id, String reason) {
|
||||
AutismClientAddon.LOG.warn("[MacroActions] Rejecting {} '{}': {}", kind, id, reason);
|
||||
AddonManager.recordRejectedRegistration(ownerAddonId, kind, id, reason);
|
||||
return AddonRegistrationResult.rejected(kind, id, reason);
|
||||
}
|
||||
|
||||
private static String validateProbe(String typeId, MacroActionEntry entry, MacroAction probe, ActionFieldSchema schema) {
|
||||
String writtenType;
|
||||
net.minecraft.nbt.CompoundTag tag;
|
||||
try {
|
||||
tag = probe.toTag();
|
||||
writtenType = tag.getStringOr("type", "");
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[MacroActions] Rejecting '{}' because toTag() threw", typeId, t);
|
||||
return "toTag() threw: " + errorName(t);
|
||||
}
|
||||
if (!typeId.equals(writtenType)) {
|
||||
return "writes type=\"" + writtenType + "\" instead of \"" + typeId + "\"";
|
||||
}
|
||||
if (schema != null) {
|
||||
for (autismclient.gui.macro.editor.FieldDef field : schema.fields()) {
|
||||
String key = field.key();
|
||||
if (field.type() == autismclient.gui.macro.editor.FieldType.BLOCK_POS) {
|
||||
for (String axisKey : field.xyzKeys()) {
|
||||
if (axisKey != null && !axisKey.isEmpty() && !tag.contains(axisKey)) {
|
||||
return "schema field '" + axisKey + "' is not written by toTag()/save()";
|
||||
}
|
||||
}
|
||||
} else if (key != null && !key.isEmpty() && !tag.contains(key)) {
|
||||
return "schema field '" + key + "' is not written by toTag()/save()";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entry.isCondition() && !(probe instanceof ContextMacroAction)) {
|
||||
return "conditions must implement ContextMacroAction";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void unregisterAddon(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
List<String> removeTypes = new ArrayList<>();
|
||||
for (Map.Entry<String, String> owner : ENTRY_OWNERS.entrySet()) {
|
||||
if (addonId.equals(owner.getValue())) removeTypes.add(owner.getKey());
|
||||
}
|
||||
for (String typeId : removeTypes) {
|
||||
ENTRIES.remove(typeId);
|
||||
RESOLVED_SCHEMAS.remove(typeId);
|
||||
ENTRY_OWNERS.remove(typeId);
|
||||
}
|
||||
|
||||
List<String> removeCategories = new ArrayList<>();
|
||||
for (Map.Entry<String, String> owner : CATEGORY_OWNERS.entrySet()) {
|
||||
if (addonId.equals(owner.getValue())) removeCategories.add(owner.getKey());
|
||||
}
|
||||
for (String categoryId : removeCategories) {
|
||||
CATEGORIES.remove(categoryId);
|
||||
CATEGORY_OWNERS.remove(categoryId);
|
||||
}
|
||||
}
|
||||
|
||||
public static Supplier<MacroAction> factory(String typeId) {
|
||||
MacroActionEntry e = typeId == null ? null : ENTRIES.get(typeId);
|
||||
return e == null ? null : e.factory();
|
||||
}
|
||||
|
||||
public static ActionFieldSchema schema(String typeId) {
|
||||
return typeId == null ? null : RESOLVED_SCHEMAS.get(typeId);
|
||||
}
|
||||
|
||||
public static boolean isCondition(String typeId) {
|
||||
MacroActionEntry e = typeId == null ? null : ENTRIES.get(typeId);
|
||||
return e != null && e.isCondition();
|
||||
}
|
||||
|
||||
public static String ownerOf(String typeId) {
|
||||
return typeId == null ? null : ENTRY_OWNERS.get(typeId);
|
||||
}
|
||||
|
||||
public static List<MacroActionEntry> entries() {
|
||||
return Collections.unmodifiableList(new ArrayList<>(ENTRIES.values()));
|
||||
}
|
||||
|
||||
private static boolean isBuiltinName(String typeId) {
|
||||
try {
|
||||
MacroActionType.valueOf(typeId.toUpperCase(Locale.ROOT));
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String errorName(Throwable t) {
|
||||
if (t == null) return "unknown";
|
||||
String message = t.getMessage();
|
||||
return message == null || message.isBlank() ? t.getClass().getSimpleName() : t.getClass().getSimpleName() + ": " + message;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import autismclient.util.macro.MacroTemplate;
|
||||
import autismclient.util.macro.MacroValue;
|
||||
|
||||
public interface MacroExecutionContext {
|
||||
|
||||
Minecraft mc();
|
||||
|
||||
void runOnClientThread(Runnable r);
|
||||
|
||||
<T> T callOnClientThread(Supplier<T> s);
|
||||
|
||||
void waitTicks(int ticks);
|
||||
|
||||
void awaitCondition(CompletableFuture<Void> future);
|
||||
|
||||
void awaitCondition(AddonCondition condition);
|
||||
|
||||
boolean isActive();
|
||||
|
||||
void setStatus(String status);
|
||||
|
||||
void sendPacket(Packet<?> packet);
|
||||
|
||||
Optional<MacroValue> variable(String name);
|
||||
|
||||
void setVariable(String name, MacroValue value);
|
||||
|
||||
default void setVariable(String name, Object value) {
|
||||
setVariable(name, MacroValue.text(value));
|
||||
}
|
||||
|
||||
void removeVariable(String name);
|
||||
|
||||
Map<String, MacroValue> variables();
|
||||
|
||||
MacroTemplate.Resolution resolveTemplate(String template);
|
||||
|
||||
default void captureResult(String name, MacroValue value) {
|
||||
setVariable(name, value);
|
||||
}
|
||||
|
||||
default void captureResults(Map<String, MacroValue> values) {
|
||||
if (values != null) values.forEach(this::setVariable);
|
||||
}
|
||||
|
||||
default Optional<Integer> resolveInt(String template) {
|
||||
MacroTemplate.Resolution resolved = resolveTemplate(template);
|
||||
if (!resolved.success()) return Optional.empty();
|
||||
try { return Optional.of(Integer.parseInt(resolved.value().trim())); }
|
||||
catch (NumberFormatException ignored) { return Optional.empty(); }
|
||||
}
|
||||
|
||||
default Optional<Double> resolveDouble(String template) {
|
||||
MacroTemplate.Resolution resolved = resolveTemplate(template);
|
||||
if (!resolved.success()) return Optional.empty();
|
||||
try { return Optional.of(Double.parseDouble(resolved.value().trim())); }
|
||||
catch (NumberFormatException ignored) { return Optional.empty(); }
|
||||
}
|
||||
|
||||
default <E extends Enum<E>> Optional<E> resolveEnum(String template, Class<E> type) {
|
||||
if (type == null) return Optional.empty();
|
||||
MacroTemplate.Resolution resolved = resolveTemplate(template);
|
||||
if (!resolved.success()) return Optional.empty();
|
||||
for (E value : type.getEnumConstants()) {
|
||||
if (value.name().equalsIgnoreCase(resolved.value().trim())) return Optional.of(value);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
115
src/main/java/autismclient/api/macro/MacroPresetRegistry.java
Normal file
115
src/main/java/autismclient/api/macro/MacroPresetRegistry.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.api.AddonRegistrationResult;
|
||||
import autismclient.addons.AddonManager;
|
||||
import autismclient.util.macro.MacroAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class MacroPresetRegistry {
|
||||
|
||||
public record PresetEntry(String categoryId, String label, String tip, Supplier<List<MacroAction>> builder) {}
|
||||
|
||||
private static final List<PresetEntry> ENTRIES = new ArrayList<>();
|
||||
private static final Map<PresetEntry, String> ENTRY_OWNERS = new IdentityHashMap<>();
|
||||
private static final Map<String, String> CATEGORY_LABELS = new LinkedHashMap<>();
|
||||
private static final Map<String, String> CATEGORY_OWNERS = new LinkedHashMap<>();
|
||||
|
||||
private MacroPresetRegistry() {}
|
||||
|
||||
public static void registerCategory(String id, String label) {
|
||||
registerCategory(id, label, AddonManager.currentAddonId());
|
||||
}
|
||||
|
||||
public static String ensureScopedCategory(String rawId) {
|
||||
String scopedId = MacroActionRegistry.scopeCategoryId(rawId);
|
||||
if (!CATEGORY_LABELS.containsKey(scopedId)) {
|
||||
registerCategory(scopedId, AddonManager.scopedCategoryLabel(null), AddonManager.currentAddonId());
|
||||
}
|
||||
return scopedId;
|
||||
}
|
||||
|
||||
public static void registerScopedCategory(String rawId, String label) {
|
||||
String visible = AddonManager.isLifecycleActive() ? AddonManager.scopedCategoryLabel(null) : label;
|
||||
registerCategory(MacroActionRegistry.scopeCategoryId(rawId), visible,
|
||||
AddonManager.currentAddonId());
|
||||
}
|
||||
|
||||
private static void registerCategory(String id, String label, String ownerAddonId) {
|
||||
if (id == null || id.isBlank()) return;
|
||||
CATEGORY_LABELS.put(id, label == null || label.isBlank() ? id : label);
|
||||
if (ownerAddonId != null && !ownerAddonId.isBlank()) CATEGORY_OWNERS.put(id, ownerAddonId);
|
||||
}
|
||||
|
||||
public static void register(String categoryId, String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
registerDetailed(categoryId, label, tip, builder);
|
||||
}
|
||||
|
||||
public static AddonRegistrationResult registerDetailed(String categoryId, String label, String tip, Supplier<List<MacroAction>> builder) {
|
||||
String ownerAddonId = AddonManager.currentAddonId();
|
||||
if (ownerAddonId == null || ownerAddonId.isBlank()) {
|
||||
return reject(ownerAddonId, label, "registration outside an addon lifecycle - register from onInitialize() or onRegisterCategories()");
|
||||
}
|
||||
if (categoryId == null || categoryId.isBlank()) return reject(ownerAddonId, label, "missing category");
|
||||
if (label == null || label.isBlank()) return reject(ownerAddonId, label, "missing label");
|
||||
if (builder == null) return reject(ownerAddonId, label, "missing builder");
|
||||
AddonRegistrationResult validation = validateBuilder(ownerAddonId, label, builder);
|
||||
if (!validation.accepted()) return validation;
|
||||
PresetEntry entry = new PresetEntry(categoryId, label, tip == null ? "" : tip, builder);
|
||||
ENTRIES.add(entry);
|
||||
ENTRY_OWNERS.put(entry, ownerAddonId);
|
||||
AddonManager.recordAcceptedRegistration("preset", label);
|
||||
return AddonRegistrationResult.accepted("preset", label);
|
||||
}
|
||||
|
||||
private static AddonRegistrationResult validateBuilder(String ownerAddonId, String label, Supplier<List<MacroAction>> builder) {
|
||||
List<MacroAction> actions;
|
||||
try {
|
||||
actions = builder.get();
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[MacroPresets] Rejecting preset '{}': builder threw", label, t);
|
||||
AddonManager.recordRejectedRegistration(ownerAddonId, "preset", label, "builder threw " + t.getClass().getSimpleName());
|
||||
return AddonRegistrationResult.rejected("preset", label, "builder threw " + t.getClass().getSimpleName());
|
||||
}
|
||||
if (actions == null) return reject(ownerAddonId, label, "builder returned null");
|
||||
for (int i = 0; i < actions.size(); i++) {
|
||||
if (actions.get(i) == null) {
|
||||
return reject(ownerAddonId, label, "builder returned null action at index " + i);
|
||||
}
|
||||
}
|
||||
return AddonRegistrationResult.accepted("preset", label);
|
||||
}
|
||||
|
||||
private static AddonRegistrationResult reject(String ownerAddonId, String label, String reason) {
|
||||
AutismClientAddon.LOG.warn("[MacroPresets] Rejecting preset '{}': {}", label, reason);
|
||||
AddonManager.recordRejectedRegistration(ownerAddonId, "preset", label, reason);
|
||||
return AddonRegistrationResult.rejected("preset", label, reason);
|
||||
}
|
||||
|
||||
public static void unregisterAddon(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
List<PresetEntry> remove = new ArrayList<>();
|
||||
for (PresetEntry entry : ENTRIES) {
|
||||
if (addonId.equals(ENTRY_OWNERS.get(entry))) remove.add(entry);
|
||||
}
|
||||
ENTRIES.removeAll(remove);
|
||||
for (PresetEntry entry : remove) ENTRY_OWNERS.remove(entry);
|
||||
CATEGORY_LABELS.entrySet().removeIf(entry -> addonId.equals(CATEGORY_OWNERS.get(entry.getKey())));
|
||||
CATEGORY_OWNERS.entrySet().removeIf(entry -> addonId.equals(entry.getValue()));
|
||||
}
|
||||
|
||||
public static List<PresetEntry> entries() {
|
||||
return Collections.unmodifiableList(ENTRIES);
|
||||
}
|
||||
|
||||
public static String categoryLabel(String id) {
|
||||
return CATEGORY_LABELS.get(id);
|
||||
}
|
||||
}
|
||||
33
src/main/java/autismclient/api/macro/SimpleAction.java
Normal file
33
src/main/java/autismclient/api/macro/SimpleAction.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class SimpleAction extends AddonAction {
|
||||
private final String displayName;
|
||||
private final String icon;
|
||||
private final Consumer<Minecraft> runner;
|
||||
|
||||
public SimpleAction(String typeId, String displayName, String icon, Consumer<Minecraft> runner) {
|
||||
super(typeId);
|
||||
this.displayName = displayName == null || displayName.isBlank() ? typeId : displayName;
|
||||
this.icon = icon == null || icon.isBlank() ? "*" : icon;
|
||||
this.runner = runner;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(Minecraft mc) {
|
||||
if (runner != null) runner.accept(mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
41
src/main/java/autismclient/api/macro/SimpleCondition.java
Normal file
41
src/main/java/autismclient/api/macro/SimpleCondition.java
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package autismclient.api.macro;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public final class SimpleCondition extends AddonContextAction {
|
||||
private final String displayName;
|
||||
private final String status;
|
||||
private final String icon;
|
||||
private final Predicate<Minecraft> predicate;
|
||||
|
||||
public SimpleCondition(String typeId, String displayName, String status, String icon, Predicate<Minecraft> predicate) {
|
||||
super(typeId);
|
||||
this.displayName = displayName == null || displayName.isBlank() ? typeId : displayName;
|
||||
this.status = status == null || status.isBlank() ? this.displayName : status;
|
||||
this.icon = icon == null || icon.isBlank() ? "?" : icon;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(MacroExecutionContext ctx) {
|
||||
ctx.setStatus(status);
|
||||
ctx.awaitCondition(new AddonCondition() {
|
||||
@Override
|
||||
public boolean check(Minecraft mc) {
|
||||
return predicate != null && predicate.test(mc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
29
src/main/java/autismclient/api/module/ActionSetting.java
Normal file
29
src/main/java/autismclient/api/module/ActionSetting.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class ActionSetting extends Setting<Void, ActionSetting> {
|
||||
private String buttonLabel = "Run";
|
||||
|
||||
public ActionSetting(String name, String title, Runnable action) {
|
||||
super(Kind.ACTION, name, title, null);
|
||||
setAction(action);
|
||||
}
|
||||
|
||||
public ActionSetting buttonLabel(String buttonLabel) {
|
||||
this.buttonLabel = buttonLabel == null || buttonLabel.isBlank() ? "Run" : buttonLabel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String buttonLabel() {
|
||||
return buttonLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void decode(String raw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Void value) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
10
src/main/java/autismclient/api/module/Availability.java
Normal file
10
src/main/java/autismclient/api/module/Availability.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public enum Availability {
|
||||
|
||||
ALWAYS,
|
||||
|
||||
IN_WORLD,
|
||||
|
||||
IN_CONTAINER
|
||||
}
|
||||
17
src/main/java/autismclient/api/module/BoolSetting.java
Normal file
17
src/main/java/autismclient/api/module/BoolSetting.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class BoolSetting extends Setting<Boolean, BoolSetting> {
|
||||
public BoolSetting(String name, String title, boolean defaultValue) {
|
||||
super(Kind.BOOLEAN, name, title, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean decode(String raw) {
|
||||
return raw != null && Boolean.parseBoolean(raw.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Boolean value) {
|
||||
return Boolean.toString(value != null && value);
|
||||
}
|
||||
}
|
||||
39
src/main/java/autismclient/api/module/ChoiceSetting.java
Normal file
39
src/main/java/autismclient/api/module/ChoiceSetting.java
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class ChoiceSetting extends Setting<String, ChoiceSetting> {
|
||||
public ChoiceSetting(String name, String title, String defaultValue, String... choices) {
|
||||
super(Kind.ENUM, name, title, defaultValue == null ? "" : defaultValue);
|
||||
setChoices(choices == null ? List.of() : Arrays.asList(choices));
|
||||
}
|
||||
|
||||
public ChoiceSetting(String name, String title, String defaultValue, List<String> choices) {
|
||||
super(Kind.ENUM, name, title, defaultValue == null ? "" : defaultValue);
|
||||
setChoices(choices);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String decode(String raw) {
|
||||
if (raw == null) return defaultValueTyped();
|
||||
String needle = raw.trim();
|
||||
List<String> options = choices();
|
||||
if (options.isEmpty()) return needle;
|
||||
for (String choice : options) {
|
||||
if (choice.equalsIgnoreCase(needle)) return choice;
|
||||
}
|
||||
return defaultValueTyped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(String value) {
|
||||
if (value == null) return defaultValueTyped();
|
||||
List<String> options = choices();
|
||||
if (options.isEmpty()) return value;
|
||||
for (String choice : options) {
|
||||
if (choice.equalsIgnoreCase(value)) return choice;
|
||||
}
|
||||
return defaultValueTyped();
|
||||
}
|
||||
}
|
||||
36
src/main/java/autismclient/api/module/ColorSetting.java
Normal file
36
src/main/java/autismclient/api/module/ColorSetting.java
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class ColorSetting extends Setting<Integer, ColorSetting> {
|
||||
public ColorSetting(String name, String title, int defaultArgb) {
|
||||
super(Kind.COLOR, name, title, defaultArgb);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer decode(String raw) {
|
||||
Integer parsed = parseColor(raw);
|
||||
return parsed == null ? defaultValueTyped() : parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Integer value) {
|
||||
int argb = value == null ? defaultValueTyped() : value;
|
||||
return String.format(Locale.ROOT, "%08X", argb);
|
||||
}
|
||||
|
||||
static Integer parseColor(String value) {
|
||||
if (value == null) return null;
|
||||
String text = value.trim();
|
||||
if (text.isEmpty()) return null;
|
||||
if (text.startsWith("#")) text = text.substring(1);
|
||||
else if (text.startsWith("0x") || text.startsWith("0X")) text = text.substring(2);
|
||||
try {
|
||||
long parsed = Long.parseUnsignedLong(text, 16);
|
||||
if (text.length() <= 6) parsed |= 0xFF000000L;
|
||||
return (int) parsed;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/main/java/autismclient/api/module/DisplayMode.java
Normal file
24
src/main/java/autismclient/api/module/DisplayMode.java
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public enum DisplayMode {
|
||||
|
||||
DEFAULT,
|
||||
|
||||
READONLY_SUMMARY,
|
||||
|
||||
NUMERIC_TEXT_FIELD,
|
||||
|
||||
RANGE_SLIDER,
|
||||
|
||||
FILE_PICKER,
|
||||
|
||||
PLAYER_NAME_LIST,
|
||||
|
||||
RANK_TAG_LIST,
|
||||
|
||||
PLAYER_RANK_PICKER,
|
||||
|
||||
MACRO_PICKER,
|
||||
|
||||
CONDITIONAL_MACRO_PICKER
|
||||
}
|
||||
42
src/main/java/autismclient/api/module/DoubleSetting.java
Normal file
42
src/main/java/autismclient/api/module/DoubleSetting.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class DoubleSetting extends Setting<Double, DoubleSetting> {
|
||||
public DoubleSetting(String name, String title, double defaultValue, double min, double max, double step) {
|
||||
super(Kind.DOUBLE, name, title, clamp(defaultValue, min, Math.max(min, max)));
|
||||
setRange(min, max);
|
||||
setSliderRange(min, max);
|
||||
setStep(step <= 0 ? 0.1 : step);
|
||||
}
|
||||
|
||||
public DoubleSetting sliderRange(double min, double max) {
|
||||
setSliderRange(min, max);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double decode(String raw) {
|
||||
if (raw == null) return defaultValueTyped();
|
||||
try {
|
||||
return clamp(Double.parseDouble(raw.trim()), min(), max());
|
||||
} catch (Exception e) {
|
||||
return defaultValueTyped();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Double value) {
|
||||
double v = value == null ? defaultValueTyped() : value;
|
||||
return Double.toString(clamp(v, min(), max()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double sanitizeTyped(Double value) {
|
||||
double v = value == null ? defaultValueTyped() : value;
|
||||
return clamp(v, min(), max());
|
||||
}
|
||||
|
||||
private static double clamp(double value, double min, double max) {
|
||||
if (Double.isNaN(value)) return min;
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
}
|
||||
70
src/main/java/autismclient/api/module/EnumSetting.java
Normal file
70
src/main/java/autismclient/api/module/EnumSetting.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class EnumSetting<E extends Enum<E>> extends Setting<E, EnumSetting<E>> {
|
||||
private final E[] values;
|
||||
private final Function<E, String> tokenizer;
|
||||
|
||||
public EnumSetting(String name, String title, E defaultValue, E[] values, Function<E, String> tokenizer) {
|
||||
super(Kind.ENUM, name, title, defaultValue);
|
||||
this.values = values == null ? newArray(defaultValue) : values;
|
||||
this.tokenizer = tokenizer == null ? Enum::name : tokenizer;
|
||||
List<String> labels = new ArrayList<>();
|
||||
for (E value : this.values) labels.add(token(value));
|
||||
setChoices(labels);
|
||||
}
|
||||
|
||||
public EnumSetting(String name, String title, E defaultValue, E[] values) {
|
||||
this(name, title, defaultValue, values, EnumSetting::titleCase);
|
||||
}
|
||||
|
||||
private String token(E value) {
|
||||
if (value == null) return "";
|
||||
try {
|
||||
String label = tokenizer.apply(value);
|
||||
return label == null || label.isBlank() ? value.name() : label;
|
||||
} catch (Throwable t) {
|
||||
return value.name();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected E decode(String raw) {
|
||||
if (raw == null) return defaultValueTyped();
|
||||
String needle = raw.trim();
|
||||
if (needle.isEmpty()) return defaultValueTyped();
|
||||
for (E value : values) {
|
||||
if (token(value).equalsIgnoreCase(needle)) return value;
|
||||
}
|
||||
for (E value : values) {
|
||||
if (value.name().equalsIgnoreCase(needle)) return value;
|
||||
}
|
||||
return defaultValueTyped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(E value) {
|
||||
return token(value == null ? defaultValueTyped() : value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Enum<E>> E[] newArray(E single) {
|
||||
E[] array = (E[]) java.lang.reflect.Array.newInstance(single.getClass(), 1);
|
||||
array[0] = single;
|
||||
return array;
|
||||
}
|
||||
|
||||
public static String titleCase(Enum<?> value) {
|
||||
String[] words = value.name().toLowerCase(java.util.Locale.ROOT).split("_");
|
||||
StringBuilder out = new StringBuilder();
|
||||
for (String word : words) {
|
||||
if (word.isEmpty()) continue;
|
||||
if (out.length() > 0) out.append(' ');
|
||||
out.append(Character.toUpperCase(word.charAt(0))).append(word.substring(1));
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
45
src/main/java/autismclient/api/module/IntSetting.java
Normal file
45
src/main/java/autismclient/api/module/IntSetting.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class IntSetting extends Setting<Integer, IntSetting> {
|
||||
public IntSetting(String name, String title, int defaultValue, int min, int max, int step) {
|
||||
super(Kind.INTEGER, name, title, clampStatic(defaultValue, min, Math.max(min, max)));
|
||||
setRange(min, max);
|
||||
setSliderRange(min, max);
|
||||
setStep(Math.max(1, step));
|
||||
}
|
||||
|
||||
public IntSetting sliderRange(double min, double max) {
|
||||
setSliderRange(min, max);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer decode(String raw) {
|
||||
if (raw == null) return defaultValueTyped();
|
||||
try {
|
||||
return (int) clampStatic((long) Double.parseDouble(raw.trim()), (long) min(), (long) max());
|
||||
} catch (Exception e) {
|
||||
return defaultValueTyped();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Integer value) {
|
||||
int v = value == null ? defaultValueTyped() : value;
|
||||
return Integer.toString((int) clampStatic(v, (long) min(), (long) max()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer sanitizeTyped(Integer value) {
|
||||
int v = value == null ? defaultValueTyped() : value;
|
||||
return (int) clampStatic(v, (long) min(), (long) max());
|
||||
}
|
||||
|
||||
private static int clampStatic(int value, int min, int max) {
|
||||
return (int) clampStatic((long) value, (long) min, (long) max);
|
||||
}
|
||||
|
||||
private static long clampStatic(long value, long min, long max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
}
|
||||
22
src/main/java/autismclient/api/module/KeybindSetting.java
Normal file
22
src/main/java/autismclient/api/module/KeybindSetting.java
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class KeybindSetting extends Setting<Integer, KeybindSetting> {
|
||||
public KeybindSetting(String name, String title, int defaultKey) {
|
||||
super(Kind.KEYBIND, name, title, defaultKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer decode(String raw) {
|
||||
if (raw == null) return defaultValueTyped();
|
||||
try {
|
||||
return Integer.parseInt(raw.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValueTyped();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(Integer value) {
|
||||
return Integer.toString(value == null ? defaultValueTyped() : value);
|
||||
}
|
||||
}
|
||||
19
src/main/java/autismclient/api/module/Kind.java
Normal file
19
src/main/java/autismclient/api/module/Kind.java
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public enum Kind {
|
||||
BOOLEAN,
|
||||
INTEGER,
|
||||
DOUBLE,
|
||||
ENUM,
|
||||
STRING,
|
||||
STRING_LIST,
|
||||
PACKET_LIST,
|
||||
ITEM_LIST,
|
||||
BLOCK_LIST,
|
||||
ENTITY_TYPE_LIST,
|
||||
SOUND_EVENT_LIST,
|
||||
STORAGE_LIST,
|
||||
COLOR,
|
||||
KEYBIND,
|
||||
ACTION
|
||||
}
|
||||
52
src/main/java/autismclient/api/module/ListSetting.java
Normal file
52
src/main/java/autismclient/api/module/ListSetting.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
abstract class ListSetting<S extends ListSetting<S>> extends Setting<List<String>, S> {
|
||||
protected ListSetting(Kind kind, String name, String title, List<String> defaultValue) {
|
||||
super(kind, name, title, defaultValue == null ? List.of() : List.copyOf(defaultValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isListSelection() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ListSetting(Kind kind, String name, String title, String defaultRaw) {
|
||||
super(kind, name, title, parseRaw(defaultRaw));
|
||||
}
|
||||
|
||||
static List<String> parseRaw(String raw) {
|
||||
if (raw == null || raw.isBlank()) return List.of();
|
||||
List<String> out = new ArrayList<>();
|
||||
for (String part : raw.split("\\|")) {
|
||||
String trimmed = part.trim();
|
||||
if (!trimmed.isEmpty()) out.add(trimmed);
|
||||
}
|
||||
return List.copyOf(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> decode(String raw) {
|
||||
if (raw == null || raw.isBlank()) return List.of();
|
||||
List<String> out = new ArrayList<>();
|
||||
for (String part : raw.split("\\|")) {
|
||||
String item = part.trim();
|
||||
if (!item.isEmpty()) out.add(item);
|
||||
}
|
||||
return List.copyOf(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(List<String> value) {
|
||||
if (value == null || value.isEmpty()) return "";
|
||||
List<String> out = new ArrayList<>();
|
||||
for (String item : value) {
|
||||
if (item == null) continue;
|
||||
String trimmed = item.trim();
|
||||
if (!trimmed.isEmpty()) out.add(trimmed);
|
||||
}
|
||||
return String.join("|", out);
|
||||
}
|
||||
}
|
||||
17
src/main/java/autismclient/api/module/PacketListSetting.java
Normal file
17
src/main/java/autismclient/api/module/PacketListSetting.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class PacketListSetting extends ListSetting<PacketListSetting> {
|
||||
public PacketListSetting(String name, String title) {
|
||||
super(Kind.PACKET_LIST, name, title, List.of());
|
||||
}
|
||||
|
||||
public PacketListSetting(String name, String title, List<String> defaultValue) {
|
||||
super(Kind.PACKET_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
public PacketListSetting(String name, String title, String defaultValue) {
|
||||
super(Kind.PACKET_LIST, name, title, defaultValue);
|
||||
}
|
||||
}
|
||||
44
src/main/java/autismclient/api/module/RangeSetting.java
Normal file
44
src/main/java/autismclient/api/module/RangeSetting.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class RangeSetting extends Setting<ValueRange, RangeSetting> {
|
||||
private double minSeparation;
|
||||
|
||||
public RangeSetting(String name, String title, ValueRange defaultValue, double min, double max, double step) {
|
||||
super(Kind.STRING, name, title,
|
||||
(defaultValue == null ? new ValueRange(min, max) : defaultValue).clamp(min, Math.max(min, max)));
|
||||
setRange(min, max);
|
||||
setSliderRange(min, max);
|
||||
setStep(step);
|
||||
displayMode(DisplayMode.RANGE_SLIDER);
|
||||
}
|
||||
|
||||
public RangeSetting(String name, String title, double defaultMin, double defaultMax,
|
||||
double min, double max, double step) {
|
||||
this(name, title, new ValueRange(defaultMin, defaultMax), min, max, step);
|
||||
}
|
||||
|
||||
public RangeSetting minSeparation(double separation) {
|
||||
this.minSeparation = Math.max(0.0, separation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public double minSeparation() {
|
||||
return minSeparation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueRange decode(String raw) {
|
||||
return sanitizeTyped(ValueRange.parse(raw, defaultValueTyped()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(ValueRange value) {
|
||||
return sanitizeTyped(value).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ValueRange sanitizeTyped(ValueRange value) {
|
||||
ValueRange range = value == null ? defaultValueTyped() : value;
|
||||
return range.withMinSeparation(minSeparation, min(), max(), false);
|
||||
}
|
||||
}
|
||||
115
src/main/java/autismclient/api/module/RegistryListSetting.java
Normal file
115
src/main/java/autismclient/api/module/RegistryListSetting.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class RegistryListSetting extends ListSetting<RegistryListSetting> {
|
||||
|
||||
public enum BlockFilter {
|
||||
|
||||
NONE,
|
||||
|
||||
PLACEABLE,
|
||||
|
||||
ORE_SIM,
|
||||
|
||||
CROPS
|
||||
}
|
||||
|
||||
private final BlockFilter blockFilter;
|
||||
|
||||
private RegistryListSetting(Kind kind, String name, String title, String defaultValue) {
|
||||
this(kind, name, title, defaultValue, BlockFilter.NONE);
|
||||
}
|
||||
|
||||
private RegistryListSetting(Kind kind, String name, String title, String defaultValue,
|
||||
BlockFilter blockFilter) {
|
||||
super(registryKindOrDefault(kind), name, title, parseDefault(defaultValue));
|
||||
this.blockFilter = kind == Kind.BLOCK_LIST && blockFilter != null ? blockFilter : BlockFilter.NONE;
|
||||
}
|
||||
|
||||
public static RegistryListSetting items(String name, String title) {
|
||||
return items(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting items(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.ITEM_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
public static RegistryListSetting blocks(String name, String title) {
|
||||
return blocks(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting blocks(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.BLOCK_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
public static RegistryListSetting placeableBlocks(String name, String title) {
|
||||
return placeableBlocks(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting placeableBlocks(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.BLOCK_LIST, name, title, defaultValue, BlockFilter.PLACEABLE);
|
||||
}
|
||||
|
||||
public static RegistryListSetting oreSimOres(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.BLOCK_LIST, name, title, defaultValue, BlockFilter.ORE_SIM);
|
||||
}
|
||||
|
||||
public static RegistryListSetting crops(String name, String title) {
|
||||
return crops(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting crops(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.BLOCK_LIST, name, title, defaultValue, BlockFilter.CROPS);
|
||||
}
|
||||
|
||||
public BlockFilter blockFilter() {
|
||||
return blockFilter;
|
||||
}
|
||||
|
||||
public boolean placeableBlocksOnly() {
|
||||
return blockFilter == BlockFilter.PLACEABLE;
|
||||
}
|
||||
|
||||
public static RegistryListSetting entityTypes(String name, String title) {
|
||||
return entityTypes(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting entityTypes(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.ENTITY_TYPE_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
public static RegistryListSetting soundEvents(String name, String title) {
|
||||
return soundEvents(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting soundEvents(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.SOUND_EVENT_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
public static RegistryListSetting storages(String name, String title) {
|
||||
return storages(name, title, "");
|
||||
}
|
||||
|
||||
public static RegistryListSetting storages(String name, String title, String defaultValue) {
|
||||
return new RegistryListSetting(Kind.STORAGE_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
private static List<String> parseDefault(String raw) {
|
||||
if (raw == null || raw.isBlank()) return List.of();
|
||||
List<String> out = new ArrayList<>();
|
||||
for (String part : raw.split("\\|")) {
|
||||
String trimmed = part.trim();
|
||||
if (!trimmed.isEmpty()) out.add(trimmed);
|
||||
}
|
||||
return List.copyOf(out);
|
||||
}
|
||||
|
||||
private static Kind registryKindOrDefault(Kind kind) {
|
||||
return switch (kind == null ? Kind.ITEM_LIST : kind) {
|
||||
case ITEM_LIST, BLOCK_LIST, ENTITY_TYPE_LIST, SOUND_EVENT_LIST, STORAGE_LIST -> kind;
|
||||
default -> Kind.ITEM_LIST;
|
||||
};
|
||||
}
|
||||
}
|
||||
333
src/main/java/autismclient/api/module/Setting.java
Normal file
333
src/main/java/autismclient/api/module/Setting.java
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class Setting<T, S extends Setting<T, S>> {
|
||||
private final Kind kind;
|
||||
private final String name;
|
||||
private final T def;
|
||||
private String title;
|
||||
private String description = "";
|
||||
private String group = "General";
|
||||
private String unit = "";
|
||||
private DisplayMode displayMode = DisplayMode.DEFAULT;
|
||||
private Availability availability;
|
||||
private BooleanSupplier visible;
|
||||
private Function<String, String> formatter;
|
||||
private Runnable action;
|
||||
private String linkedActionId = "";
|
||||
private boolean keepOnReset;
|
||||
private double min;
|
||||
private double max;
|
||||
private double sliderMin;
|
||||
private double sliderMax;
|
||||
private double step = 1.0;
|
||||
private List<String> choices = List.of();
|
||||
|
||||
private SettingOwner owner;
|
||||
private transient boolean cached;
|
||||
private transient String cachedRaw;
|
||||
private transient T cachedValue;
|
||||
|
||||
protected Setting(Kind kind, String name, String title, T def) {
|
||||
this.kind = kind == null ? Kind.STRING : kind;
|
||||
this.name = name == null ? "" : name;
|
||||
this.title = title == null || title.isBlank() ? this.name : title;
|
||||
this.def = def;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final S self() {
|
||||
return (S) this;
|
||||
}
|
||||
|
||||
protected abstract T decode(String raw);
|
||||
|
||||
protected abstract String encode(T value);
|
||||
|
||||
protected T sanitizeTyped(T value) {
|
||||
return value == null ? def : value;
|
||||
}
|
||||
|
||||
public final void attach(SettingOwner owner) {
|
||||
this.owner = owner;
|
||||
this.cached = false;
|
||||
}
|
||||
|
||||
private String stored() {
|
||||
if (owner == null) return defaultValue();
|
||||
String raw = owner.settingValue(name);
|
||||
return raw == null ? defaultValue() : raw;
|
||||
}
|
||||
|
||||
public final T get() {
|
||||
String raw = stored();
|
||||
if (cached && Objects.equals(raw, cachedRaw)) return cachedValue;
|
||||
T value = decode(raw);
|
||||
cachedRaw = raw;
|
||||
cachedValue = value;
|
||||
cached = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
public final void set(T value) {
|
||||
if (owner != null) owner.putSettingValue(name, encode(sanitizeTyped(value)));
|
||||
}
|
||||
|
||||
public final T defaultValueTyped() {
|
||||
return def;
|
||||
}
|
||||
|
||||
public final S description(String description) {
|
||||
this.description = description == null ? "" : description;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S group(String group) {
|
||||
this.group = group == null || group.isBlank() ? "General" : group;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S unit(String unit) {
|
||||
this.unit = unit == null ? "" : unit;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S visibleWhen(BooleanSupplier visible) {
|
||||
this.visible = visible;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S formatter(Function<String, String> formatter) {
|
||||
this.formatter = formatter;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S displayMode(DisplayMode displayMode) {
|
||||
this.displayMode = displayMode == null ? DisplayMode.DEFAULT : displayMode;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S readonlySummary() {
|
||||
return displayMode(DisplayMode.READONLY_SUMMARY);
|
||||
}
|
||||
|
||||
public final S numericTextField() {
|
||||
return displayMode(DisplayMode.NUMERIC_TEXT_FIELD);
|
||||
}
|
||||
|
||||
public final S playerNameList() {
|
||||
return displayMode(DisplayMode.PLAYER_NAME_LIST);
|
||||
}
|
||||
|
||||
public final S rankTagList() {
|
||||
return displayMode(DisplayMode.RANK_TAG_LIST);
|
||||
}
|
||||
|
||||
public final S playerRankPicker() {
|
||||
return displayMode(DisplayMode.PLAYER_RANK_PICKER);
|
||||
}
|
||||
|
||||
public final S macroPicker() {
|
||||
return displayMode(DisplayMode.MACRO_PICKER);
|
||||
}
|
||||
|
||||
public final S conditionalMacroPicker() {
|
||||
return displayMode(DisplayMode.CONDITIONAL_MACRO_PICKER);
|
||||
}
|
||||
|
||||
public final S filePicker(String linkedActionId) {
|
||||
this.linkedActionId = linkedActionId == null ? "" : linkedActionId;
|
||||
return displayMode(DisplayMode.FILE_PICKER);
|
||||
}
|
||||
|
||||
public final S linkedActionId(String linkedActionId) {
|
||||
this.linkedActionId = linkedActionId == null ? "" : linkedActionId;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S availableOffline() {
|
||||
this.availability = Availability.ALWAYS;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S requiresWorld() {
|
||||
this.availability = Availability.IN_WORLD;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S requiresContainer() {
|
||||
this.availability = Availability.IN_CONTAINER;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final S keepOnReset() {
|
||||
this.keepOnReset = true;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final boolean isKeptOnReset() {
|
||||
return keepOnReset;
|
||||
}
|
||||
|
||||
public final S build() {
|
||||
return self();
|
||||
}
|
||||
|
||||
protected final void setRange(double min, double max) {
|
||||
this.min = min;
|
||||
this.max = Math.max(min, max);
|
||||
}
|
||||
|
||||
protected final void setSliderRange(double min, double max) {
|
||||
this.sliderMin = min;
|
||||
this.sliderMax = Math.max(min, max);
|
||||
}
|
||||
|
||||
protected final void setStep(double step) {
|
||||
this.step = step <= 0 ? 1.0 : step;
|
||||
}
|
||||
|
||||
protected final void setChoices(List<String> choices) {
|
||||
this.choices = choices == null ? List.of() : List.copyOf(choices);
|
||||
}
|
||||
|
||||
protected final void setAction(Runnable action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public final String id() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public final String label() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public final String description() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public final String group() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public boolean isListSelection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean isListModeSelector() {
|
||||
List<String> options = choices();
|
||||
if (options.size() < 2) return false;
|
||||
boolean whitelist = false;
|
||||
boolean blacklist = false;
|
||||
for (String option : options) {
|
||||
if ("whitelist".equalsIgnoreCase(option)) whitelist = true;
|
||||
else if ("blacklist".equalsIgnoreCase(option)) blacklist = true;
|
||||
}
|
||||
return whitelist && blacklist;
|
||||
}
|
||||
|
||||
public final Kind kind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public final DisplayMode displayMode() {
|
||||
return displayMode;
|
||||
}
|
||||
|
||||
public final Availability availability() {
|
||||
return availability == null
|
||||
? (kind == Kind.ACTION ? Availability.IN_WORLD : Availability.ALWAYS)
|
||||
: availability;
|
||||
}
|
||||
|
||||
public final String unit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public final List<String> choices() {
|
||||
return choices;
|
||||
}
|
||||
|
||||
public final double min() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public final double max() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public final double sliderMin() {
|
||||
return sliderMin;
|
||||
}
|
||||
|
||||
public final double sliderMax() {
|
||||
return sliderMax;
|
||||
}
|
||||
|
||||
public final double step() {
|
||||
return step;
|
||||
}
|
||||
|
||||
public final Runnable action() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public final String linkedActionId() {
|
||||
return linkedActionId;
|
||||
}
|
||||
|
||||
public final String defaultValue() {
|
||||
return encode(def);
|
||||
}
|
||||
|
||||
public final String serialize() {
|
||||
return stored();
|
||||
}
|
||||
|
||||
public final String displayString() {
|
||||
return format(serialize());
|
||||
}
|
||||
|
||||
public final String format(String value) {
|
||||
if (formatter == null) return value == null ? "" : value;
|
||||
try {
|
||||
return formatter.apply(value);
|
||||
} catch (Throwable t) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
}
|
||||
|
||||
public final String sanitizeUiString(String raw) {
|
||||
return encode(decode(raw));
|
||||
}
|
||||
|
||||
public final String deserialize(String raw) {
|
||||
return sanitizeUiString(raw);
|
||||
}
|
||||
|
||||
public final boolean isVisible() {
|
||||
if (visible == null) return true;
|
||||
try {
|
||||
return visible.getAsBoolean();
|
||||
} catch (Throwable t) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isAvailable(boolean inWorld, boolean inContainer) {
|
||||
return switch (availability()) {
|
||||
case ALWAYS -> true;
|
||||
case IN_WORLD -> inWorld;
|
||||
case IN_CONTAINER -> inContainer;
|
||||
};
|
||||
}
|
||||
|
||||
public final boolean isModified() {
|
||||
return !Objects.equals(serialize(), defaultValue());
|
||||
}
|
||||
}
|
||||
18
src/main/java/autismclient/api/module/SettingGroup.java
Normal file
18
src/main/java/autismclient/api/module/SettingGroup.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class SettingGroup {
|
||||
private final String name;
|
||||
|
||||
public SettingGroup(String name) {
|
||||
this.name = name == null || name.isBlank() ? "General" : name;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public <T, S extends Setting<T, S>> S apply(S setting) {
|
||||
if (setting != null) setting.group(name);
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
8
src/main/java/autismclient/api/module/SettingOwner.java
Normal file
8
src/main/java/autismclient/api/module/SettingOwner.java
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public interface SettingOwner {
|
||||
|
||||
String settingValue(String name);
|
||||
|
||||
void putSettingValue(String name, String value);
|
||||
}
|
||||
101
src/main/java/autismclient/api/module/SimpleModule.java
Normal file
101
src/main/java/autismclient/api/module/SimpleModule.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import autismclient.api.AutismAddons;
|
||||
import autismclient.modules.Module;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SimpleModule extends Module {
|
||||
private Consumer<SimpleModule> enableHandler;
|
||||
private Consumer<SimpleModule> disableHandler;
|
||||
private Consumer<SimpleModule> tickHandler;
|
||||
|
||||
public SimpleModule(String localId, String name, String description) {
|
||||
super(AutismAddons.id(localId), name, description);
|
||||
}
|
||||
|
||||
public final SimpleModule onEnabled(Consumer<SimpleModule> handler) {
|
||||
this.enableHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule onDisabled(Consumer<SimpleModule> handler) {
|
||||
this.disableHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule onTick(Consumer<SimpleModule> handler) {
|
||||
this.tickHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule addBool(String id, String label, boolean defaultValue) {
|
||||
add(new BoolSetting(id, label, defaultValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule addInt(String id, String label, int defaultValue, int min, int max, int step) {
|
||||
add(new IntSetting(id, label, defaultValue, min, max, step));
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule addDouble(String id, String label, double defaultValue, double min, double max, double step) {
|
||||
add(new DoubleSetting(id, label, defaultValue, min, max, step));
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule addChoice(String id, String label, String defaultValue, String... choices) {
|
||||
add(new ChoiceSetting(id, label, defaultValue, choices == null ? new String[0] : choices));
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule addText(String id, String label, String defaultValue) {
|
||||
add(new StringSetting(id, label, defaultValue));
|
||||
return this;
|
||||
}
|
||||
|
||||
public final SimpleModule addColor(String id, String label, int defaultArgb) {
|
||||
add(new ColorSetting(id, label, defaultArgb));
|
||||
return this;
|
||||
}
|
||||
|
||||
public final boolean getBool(String id) {
|
||||
return bool(id);
|
||||
}
|
||||
|
||||
public final int getInt(String id) {
|
||||
return integer(id);
|
||||
}
|
||||
|
||||
public final double getDouble(String id) {
|
||||
return decimal(id);
|
||||
}
|
||||
|
||||
public final String getText(String id) {
|
||||
return text(id);
|
||||
}
|
||||
|
||||
public final String getChoice(String id) {
|
||||
return choice(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (enableHandler != null) enableHandler.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (disableHandler != null) disableHandler.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (tickHandler != null) tickHandler.accept(this);
|
||||
}
|
||||
|
||||
protected final String upper(String value) {
|
||||
return value == null ? "" : value.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
17
src/main/java/autismclient/api/module/StringListSetting.java
Normal file
17
src/main/java/autismclient/api/module/StringListSetting.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class StringListSetting extends ListSetting<StringListSetting> {
|
||||
public StringListSetting(String name, String title) {
|
||||
super(Kind.STRING_LIST, name, title, List.of());
|
||||
}
|
||||
|
||||
public StringListSetting(String name, String title, List<String> defaultValue) {
|
||||
super(Kind.STRING_LIST, name, title, defaultValue);
|
||||
}
|
||||
|
||||
public StringListSetting(String name, String title, String defaultValue) {
|
||||
super(Kind.STRING_LIST, name, title, defaultValue);
|
||||
}
|
||||
}
|
||||
17
src/main/java/autismclient/api/module/StringSetting.java
Normal file
17
src/main/java/autismclient/api/module/StringSetting.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
public final class StringSetting extends Setting<String, StringSetting> {
|
||||
public StringSetting(String name, String title, String defaultValue) {
|
||||
super(Kind.STRING, name, title, defaultValue == null ? "" : defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String decode(String raw) {
|
||||
return raw == null ? "" : raw;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String encode(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
}
|
||||
76
src/main/java/autismclient/api/module/ValueRange.java
Normal file
76
src/main/java/autismclient/api/module/ValueRange.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package autismclient.api.module;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public record ValueRange(double min, double max) {
|
||||
public ValueRange {
|
||||
if (max < min) {
|
||||
double swap = min;
|
||||
min = max;
|
||||
max = swap;
|
||||
}
|
||||
}
|
||||
|
||||
public ValueRange(int min, int max) {
|
||||
this((double) min, (double) max);
|
||||
}
|
||||
|
||||
public ValueRange clamp(double lower, double upper) {
|
||||
double high = Math.max(lower, upper);
|
||||
return new ValueRange(
|
||||
Math.max(lower, Math.min(high, min)),
|
||||
Math.max(lower, Math.min(high, max)));
|
||||
}
|
||||
|
||||
public ValueRange withMinSeparation(double separation, double lower, double upper, boolean anchorMin) {
|
||||
double high = Math.max(lower, upper);
|
||||
if (separation <= 0 || high - lower < separation) return clamp(lower, high);
|
||||
ValueRange bounded = clamp(lower, high);
|
||||
if (bounded.max - bounded.min >= separation) return bounded;
|
||||
if (anchorMin) {
|
||||
double pushed = bounded.min + separation;
|
||||
return pushed <= high
|
||||
? new ValueRange(bounded.min, pushed)
|
||||
: new ValueRange(high - separation, high);
|
||||
}
|
||||
double pushed = bounded.max - separation;
|
||||
return pushed >= lower
|
||||
? new ValueRange(pushed, bounded.max)
|
||||
: new ValueRange(lower, lower + separation);
|
||||
}
|
||||
|
||||
public double random(Random random) {
|
||||
if (random == null || max <= min) return min;
|
||||
return min + random.nextDouble() * (max - min);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return number(min) + "," + number(max);
|
||||
}
|
||||
|
||||
public String format(double step) {
|
||||
boolean whole = step >= 1.0 && step == Math.rint(step);
|
||||
return whole
|
||||
? Math.round(min) + " - " + Math.round(max)
|
||||
: number(min) + " - " + number(max);
|
||||
}
|
||||
|
||||
private static String number(double value) {
|
||||
if (value == Math.rint(value) && !Double.isInfinite(value)) return Long.toString((long) value);
|
||||
return String.valueOf(Math.round(value * 100.0) / 100.0);
|
||||
}
|
||||
|
||||
public static ValueRange parse(String raw, ValueRange fallback) {
|
||||
if (raw == null) return fallback;
|
||||
int comma = raw.indexOf(',');
|
||||
if (comma < 0) return fallback;
|
||||
try {
|
||||
return new ValueRange(
|
||||
Double.parseDouble(raw.substring(0, comma).trim()),
|
||||
Double.parseDouble(raw.substring(comma + 1).trim()));
|
||||
} catch (RuntimeException e) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/main/java/autismclient/commands/AutismCommandSource.java
Normal file
18
src/main/java/autismclient/commands/AutismCommandSource.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package autismclient.commands;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
|
||||
public final class AutismCommandSource {
|
||||
public static final AutismCommandSource INSTANCE = new AutismCommandSource();
|
||||
|
||||
private AutismCommandSource() {}
|
||||
|
||||
public Minecraft mc() { return Minecraft.getInstance(); }
|
||||
public LocalPlayer player() { return Minecraft.getInstance().player; }
|
||||
public ClientPacketListener connection() { return Minecraft.getInstance().getConnection(); }
|
||||
|
||||
public boolean hasPlayer() { return Minecraft.getInstance().player != null; }
|
||||
public boolean hasConnection() { return Minecraft.getInstance().getConnection() != null; }
|
||||
}
|
||||
284
src/main/java/autismclient/commands/AutismCommands.java
Normal file
284
src/main/java/autismclient/commands/AutismCommands.java
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
package autismclient.commands;
|
||||
|
||||
import autismclient.AutismClientAddon;
|
||||
import autismclient.api.AddonRegistrationResult;
|
||||
import autismclient.modules.PackHideState;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismCompatManager;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public final class AutismCommands {
|
||||
private static final CommandDispatcher<AutismCommandSource> DISPATCHER = new CommandDispatcher<>();
|
||||
private static final List<Command> ALL = new ArrayList<>();
|
||||
private static final Map<String, Command> BY_NAME = new LinkedHashMap<>();
|
||||
private static final Map<String, String> ADDON_COMMAND_OWNERS = new LinkedHashMap<>();
|
||||
private static final Map<Command, String> ADDON_COMMAND_OBJECT_OWNERS = new IdentityHashMap<>();
|
||||
private static final Set<String> DISABLED_ADDON_COMMAND_NAMES = new HashSet<>();
|
||||
private static final List<String> PANIC_PREFIX_FALLBACKS = List.of(".", ",", ";", ":", "'", "\"", "\\", "|", "-", "_", "+", "=", "*", "#", "@", "!", "$", "%", "&", "~");
|
||||
private static boolean initialized = false;
|
||||
private static int revision;
|
||||
|
||||
private AutismCommands() {}
|
||||
|
||||
public static synchronized void init() {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
register(new autismclient.commands.impl.MacroCommand());
|
||||
register(new autismclient.commands.impl.SyncCommand());
|
||||
register(new autismclient.commands.impl.DelayCommand());
|
||||
register(new autismclient.commands.impl.SendCommand());
|
||||
register(new autismclient.commands.impl.VClipCommand());
|
||||
register(new autismclient.commands.impl.HClipCommand());
|
||||
register(new autismclient.commands.impl.TpCommand());
|
||||
register(new autismclient.commands.impl.CopyPosCommand());
|
||||
register(new autismclient.commands.impl.NbtCommand());
|
||||
register(new autismclient.commands.impl.ServerCommand());
|
||||
register(new autismclient.commands.impl.PluginsCommand());
|
||||
register(new autismclient.commands.impl.PrefixCommand());
|
||||
register(new autismclient.commands.impl.CommandsCommand());
|
||||
register(new autismclient.commands.impl.HelpCommand());
|
||||
register(new autismclient.commands.impl.BindsCommand());
|
||||
register(new autismclient.commands.impl.BindCommand());
|
||||
register(new autismclient.commands.impl.ClearCommand());
|
||||
register(new autismclient.commands.impl.DismountCommand());
|
||||
register(new autismclient.commands.impl.DisconnectCommand());
|
||||
register(new autismclient.commands.impl.SayCommand());
|
||||
register(new autismclient.commands.impl.DropCommand());
|
||||
register(new autismclient.commands.impl.ChangeSlotCommand());
|
||||
register(new autismclient.commands.impl.ClickSlotCommand());
|
||||
register(new autismclient.commands.impl.ClickItemCommand());
|
||||
register(new autismclient.commands.impl.GiveCommand());
|
||||
register(new autismclient.commands.impl.DamageCommand());
|
||||
|
||||
if (!autismclient.util.AutismLiteVariant.enabled()) {
|
||||
register(new autismclient.commands.impl.IrcCommand());
|
||||
register(new autismclient.commands.impl.MatchmakingCommand());
|
||||
register(new autismclient.commands.impl.MultiCommand());
|
||||
register(new autismclient.commands.impl.ToggleCommand());
|
||||
register(new autismclient.commands.impl.ModulesCommand());
|
||||
register(new autismclient.commands.impl.OreSimCommand());
|
||||
register(new autismclient.commands.impl.GhostBlockCommand());
|
||||
register(new autismclient.commands.impl.FriendCommand());
|
||||
register(new autismclient.commands.impl.WaypointsCommand());
|
||||
register(new autismclient.commands.impl.GamemodeCommand());
|
||||
register(new autismclient.commands.impl.FakeGmCommand());
|
||||
register(new autismclient.commands.impl.XCarryCommand());
|
||||
register(new autismclient.commands.impl.RemoteViewCommand());
|
||||
register(new autismclient.commands.impl.NameScrapeCommand());
|
||||
}
|
||||
}
|
||||
|
||||
private static void register(Command command) {
|
||||
registerWithName(command, command.name(), true);
|
||||
}
|
||||
|
||||
private static List<String> registerWithName(Command command, String primaryName, boolean registerAliases) {
|
||||
List<String> registeredNames = new ArrayList<>();
|
||||
revision++;
|
||||
ALL.add(command);
|
||||
LiteralArgumentBuilder<AutismCommandSource> primary = LiteralArgumentBuilder.literal(primaryName);
|
||||
command.build(primary);
|
||||
DISPATCHER.register(primary);
|
||||
BY_NAME.put(primaryName.toLowerCase(Locale.ROOT), command);
|
||||
DISABLED_ADDON_COMMAND_NAMES.remove(primaryName.toLowerCase(Locale.ROOT));
|
||||
registeredNames.add(primaryName.toLowerCase(Locale.ROOT));
|
||||
|
||||
if (!registerAliases) return registeredNames;
|
||||
for (String alias : command.aliases()) {
|
||||
if (alias == null || alias.isBlank()) continue;
|
||||
if (BY_NAME.containsKey(alias.toLowerCase(Locale.ROOT))) continue;
|
||||
LiteralArgumentBuilder<AutismCommandSource> aliasBuilder = LiteralArgumentBuilder.literal(alias);
|
||||
command.build(aliasBuilder);
|
||||
DISPATCHER.register(aliasBuilder);
|
||||
BY_NAME.put(alias.toLowerCase(Locale.ROOT), command);
|
||||
DISABLED_ADDON_COMMAND_NAMES.remove(alias.toLowerCase(Locale.ROOT));
|
||||
registeredNames.add(alias.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return registeredNames;
|
||||
}
|
||||
|
||||
public static synchronized void registerAddonCommand(Command command, String addonId) {
|
||||
registerAddonCommandDetailed(command, addonId);
|
||||
}
|
||||
|
||||
public static synchronized AddonRegistrationResult registerAddonCommandDetailed(Command command, String addonId) {
|
||||
if (command == null) return AddonRegistrationResult.rejected("command", "", "command was null");
|
||||
if (addonId == null || addonId.isBlank()) {
|
||||
return rejectAddonCommand(addonId, "", "registration outside an addon lifecycle - register from onInitialize() or onRegisterCategories()");
|
||||
}
|
||||
String name = command.name();
|
||||
if (name == null || name.isBlank()) {
|
||||
return rejectAddonCommand(addonId, "", "blank command name");
|
||||
}
|
||||
List<String> registered;
|
||||
boolean collision = BY_NAME.containsKey(name.toLowerCase(Locale.ROOT));
|
||||
if (collision) {
|
||||
String namespaced = (addonId == null || addonId.isBlank() ? "addon" : addonId) + ":" + name;
|
||||
AutismClientAddon.LOG.warn("[Commands] Command name '{}' from addon '{}' collides with an existing "
|
||||
+ "command; registering it as '{}' instead", name, addonId, namespaced);
|
||||
registered = registerWithName(command, namespaced, false);
|
||||
} else {
|
||||
registered = registerWithName(command, name, true);
|
||||
}
|
||||
ADDON_COMMAND_OBJECT_OWNERS.put(command, addonId);
|
||||
for (String registeredName : registered) {
|
||||
ADDON_COMMAND_OWNERS.put(registeredName, addonId);
|
||||
}
|
||||
String id = registered.isEmpty() ? name : registered.get(0);
|
||||
autismclient.addons.AddonManager.recordAcceptedRegistration("command", id);
|
||||
return AddonRegistrationResult.accepted("command", id);
|
||||
}
|
||||
|
||||
private static AddonRegistrationResult rejectAddonCommand(String addonId, String id, String reason) {
|
||||
AutismClientAddon.LOG.warn("[Commands] Rejecting addon command '{}': {}", id, reason);
|
||||
autismclient.addons.AddonManager.recordRejectedRegistration(addonId, "command", id, reason);
|
||||
return AddonRegistrationResult.rejected("command", id, reason);
|
||||
}
|
||||
|
||||
public static synchronized void unregisterAddonCommands(String addonId) {
|
||||
if (addonId == null || addonId.isBlank()) return;
|
||||
List<String> removeNames = new ArrayList<>();
|
||||
for (Map.Entry<String, String> owner : ADDON_COMMAND_OWNERS.entrySet()) {
|
||||
if (addonId.equals(owner.getValue())) removeNames.add(owner.getKey());
|
||||
}
|
||||
for (String name : removeNames) {
|
||||
BY_NAME.remove(name);
|
||||
ADDON_COMMAND_OWNERS.remove(name);
|
||||
DISABLED_ADDON_COMMAND_NAMES.add(name);
|
||||
}
|
||||
ALL.removeIf(command -> addonId.equals(ADDON_COMMAND_OBJECT_OWNERS.get(command)));
|
||||
ADDON_COMMAND_OBJECT_OWNERS.entrySet().removeIf(entry -> addonId.equals(entry.getValue()));
|
||||
if (!removeNames.isEmpty()) revision++;
|
||||
}
|
||||
|
||||
public static CommandDispatcher<AutismCommandSource> dispatcher() { return DISPATCHER; }
|
||||
|
||||
public static int revision() { return revision; }
|
||||
|
||||
public static List<Command> all() { return Collections.unmodifiableList(ALL); }
|
||||
|
||||
public static Command find(String nameOrAlias) {
|
||||
if (nameOrAlias == null) return null;
|
||||
return BY_NAME.get(nameOrAlias.trim().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
public static String effectivePrefix() { return AutismCompatManager.effectiveCommandPrefix(); }
|
||||
|
||||
public static boolean isAutismCommandMessage(String message) {
|
||||
if (message == null || message.isBlank()) return false;
|
||||
String trimmed = message.trim();
|
||||
String prefix = effectivePrefix();
|
||||
return !prefix.isEmpty() && trimmed.startsWith(prefix);
|
||||
}
|
||||
|
||||
public static boolean commandsBlockedByPanic() {
|
||||
return PackHideState.isHardLocked();
|
||||
}
|
||||
|
||||
public static boolean isBlockedPanicCommandMessage(String message) {
|
||||
return commandsBlockedByPanic() && isPanicBlockedCommandMessage(message);
|
||||
}
|
||||
|
||||
private static boolean isPanicBlockedCommandMessage(String message) {
|
||||
if (message == null || message.isBlank()) return false;
|
||||
String trimmed = message.trim();
|
||||
if ("^toggleautism".equalsIgnoreCase(trimmed)) return true;
|
||||
|
||||
String prefix = effectivePrefix();
|
||||
if (!prefix.isEmpty() && trimmed.startsWith(prefix)) return true;
|
||||
|
||||
for (String fallback : PANIC_PREFIX_FALLBACKS) {
|
||||
if (fallback == null || fallback.isEmpty() || fallback.equals(prefix)) continue;
|
||||
if (!trimmed.startsWith(fallback)) continue;
|
||||
String body = trimmed.substring(fallback.length()).trim();
|
||||
if (body.isEmpty()) continue;
|
||||
String first = firstToken(body).toLowerCase(Locale.ROOT);
|
||||
if (BY_NAME.containsKey(first) || DISABLED_ADDON_COMMAND_NAMES.contains(first)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String commandBody(String message) {
|
||||
if (!isAutismCommandMessage(message)) return "";
|
||||
String trimmed = message.trim();
|
||||
int prefixLength = effectivePrefix().length();
|
||||
return trimmed.length() <= prefixLength ? "" : trimmed.substring(prefixLength).trim();
|
||||
}
|
||||
|
||||
private static final ThreadLocal<Boolean> PLAIN_CHAT_BYPASS = ThreadLocal.withInitial(() -> false);
|
||||
|
||||
public static boolean plainChatBypass() {
|
||||
return PLAIN_CHAT_BYPASS.get();
|
||||
}
|
||||
|
||||
public static void sendPlainChat(net.minecraft.client.multiplayer.ClientPacketListener conn, String message) {
|
||||
PLAIN_CHAT_BYPASS.set(true);
|
||||
try {
|
||||
conn.sendChat(message);
|
||||
} finally {
|
||||
PLAIN_CHAT_BYPASS.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean dispatch(String body) {
|
||||
if (commandsBlockedByPanic()) return true;
|
||||
if (body == null) return false;
|
||||
String trimmed = body.trim();
|
||||
if (trimmed.isEmpty()) return false;
|
||||
if (DISABLED_ADDON_COMMAND_NAMES.contains(firstToken(trimmed).toLowerCase(Locale.ROOT))) {
|
||||
sendSyntaxError(trimmed, CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().create());
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
|
||||
if (!autismclient.util.AutismLiteVariant.enabled()
|
||||
&& autismclient.util.multi.MultiPovCommandRouter.route(trimmed)) return true;
|
||||
DISPATCHER.execute(trimmed, AutismCommandSource.INSTANCE);
|
||||
return true;
|
||||
} catch (CommandSyntaxException e) {
|
||||
sendSyntaxError(trimmed, e);
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
AutismClientAddon.LOG.warn("[Commands] dispatch failed for '{}'", trimmed, t);
|
||||
AutismClientMessaging.sendPrefixed("§cCommand error: "
|
||||
+ (t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendSyntaxError(String body, CommandSyntaxException e) {
|
||||
String first = firstToken(body);
|
||||
Command command = find(first);
|
||||
String prefix = effectivePrefix();
|
||||
if (command == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown AUTISM command: §f" + first);
|
||||
AutismClientMessaging.sendPrefixed("§7Use §f" + prefix + "commands §7or §f" + prefix + "help§7.");
|
||||
return;
|
||||
}
|
||||
|
||||
String message = e.getMessage();
|
||||
if (message == null || message.isBlank()) message = "Incomplete or invalid command.";
|
||||
AutismClientMessaging.sendPrefixed("§c" + message);
|
||||
AutismClientMessaging.sendPrefixed("§7Use §f" + prefix + "help " + command.name() + "§7.");
|
||||
}
|
||||
|
||||
private static String firstToken(String body) {
|
||||
if (body == null) return "";
|
||||
String trimmed = body.trim();
|
||||
if (trimmed.isEmpty()) return "";
|
||||
int space = trimmed.indexOf(' ');
|
||||
return space < 0 ? trimmed : trimmed.substring(0, space);
|
||||
}
|
||||
}
|
||||
33
src/main/java/autismclient/commands/Command.java
Normal file
33
src/main/java/autismclient/commands/Command.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package autismclient.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
||||
public abstract class Command {
|
||||
public static final int SUCCESS = 1;
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final String[] aliases;
|
||||
|
||||
protected Command(String name, String description, String... aliases) {
|
||||
this.name = name;
|
||||
this.description = description == null ? "" : description;
|
||||
this.aliases = aliases == null ? new String[0] : aliases.clone();
|
||||
}
|
||||
|
||||
public final String name() { return name; }
|
||||
public final String description() { return description; }
|
||||
public final String[] aliases() { return aliases.clone(); }
|
||||
|
||||
public abstract void build(LiteralArgumentBuilder<AutismCommandSource> builder);
|
||||
|
||||
public int run(CommandContext<AutismCommandSource> ctx) {
|
||||
autismclient.util.AutismClientMessaging.sendPrefixed(
|
||||
"§eUsage: " + AutismCommands.effectivePrefix() + name + (aliases.length > 0 ? " (alias: " + aliases[0] + ")" : ""));
|
||||
if (!description.isEmpty()) {
|
||||
autismclient.util.AutismClientMessaging.sendPrefixed("§7" + description);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
92
src/main/java/autismclient/commands/CommandSuggest.java
Normal file
92
src/main/java/autismclient/commands/CommandSuggest.java
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package autismclient.commands;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public final class CommandSuggest {
|
||||
private CommandSuggest() {}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> literals(SuggestionsBuilder builder, String... values) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (String value : values) {
|
||||
if (value != null && value.toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(value);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> state(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "on", "off", "toggle", "enable", "disable");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> offsets(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "1", "5", "10", "50", "100", "-1", "-5", "-10", "-50", "-100");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> vclipSegments(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "1", "5", "10", "20", "50");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> vclipPacketLimits(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "1", "5", "10", "20", "40", "100");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> damage(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "1", "2", "4", "8", "20");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> counts(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "1", "16", "32", "64", "1000", "10000", "100000");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> gamemodes(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "survival", "creative", "adventure", "spectator", "reset", "0", "1", "2", "3");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> realGamemodes(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, "survival", "creative", "adventure", "spectator", "0", "1", "2", "3");
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> prefixes(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
return literals(builder, ".", "%", "-", "_", "*", "#", "@", "&", "=");
|
||||
}
|
||||
|
||||
private static volatile String[][] itemIdCache;
|
||||
private static volatile String[][] blockIdCache;
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> itemIds(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
String[][] cache = itemIdCache;
|
||||
if (cache == null) cache = itemIdCache = buildIdCache(BuiltInRegistries.ITEM.keySet());
|
||||
for (String[] entry : cache) {
|
||||
if (entry[1].startsWith(remaining)) builder.suggest(entry[0]);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
public static <S> CompletableFuture<Suggestions> blockIds(CommandContext<S> ignored, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
String[][] cache = blockIdCache;
|
||||
if (cache == null) cache = blockIdCache = buildIdCache(BuiltInRegistries.BLOCK.keySet());
|
||||
for (String[] entry : cache) {
|
||||
if (entry[1].startsWith(remaining)) builder.suggest(entry[0]);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static String[][] buildIdCache(java.util.Set<Identifier> ids) {
|
||||
java.util.List<String[]> entries = new java.util.ArrayList<>(ids.size() * 2);
|
||||
for (Identifier id : ids) {
|
||||
String full = id.toString();
|
||||
String shortId = id.getNamespace().equals("minecraft") ? id.getPath() : full;
|
||||
entries.add(new String[]{full, full.toLowerCase(Locale.ROOT)});
|
||||
if (!shortId.equals(full)) entries.add(new String[]{shortId, shortId.toLowerCase(Locale.ROOT)});
|
||||
}
|
||||
return entries.toArray(new String[0][]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class KeyArgumentType implements ArgumentType<Integer> {
|
||||
private static final Map<String, Integer> NAMES = new LinkedHashMap<>();
|
||||
private static final SimpleCommandExceptionType UNKNOWN = new SimpleCommandExceptionType(Component.literal("Unknown key"));
|
||||
|
||||
static {
|
||||
for (Field f : GLFW.class.getDeclaredFields()) {
|
||||
String n = f.getName();
|
||||
if (!n.startsWith("GLFW_KEY_")) continue;
|
||||
try {
|
||||
int code = f.getInt(null);
|
||||
NAMES.put(n.substring("GLFW_KEY_".length()).toUpperCase(Locale.ROOT), code);
|
||||
} catch (Throwable ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyArgumentType key() { return new KeyArgumentType(); }
|
||||
|
||||
public static int get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, Integer.class);
|
||||
}
|
||||
|
||||
public static String keyName(int code) {
|
||||
if (code < 0) return "NONE";
|
||||
for (Map.Entry<String, Integer> e : NAMES.entrySet()) {
|
||||
if (e.getValue() == code) return e.getKey();
|
||||
}
|
||||
return "KEY_" + code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer parse(StringReader reader) throws CommandSyntaxException {
|
||||
String raw = reader.readUnquotedString();
|
||||
String upper = raw.toUpperCase(Locale.ROOT);
|
||||
Integer code = NAMES.get(upper);
|
||||
if (code != null) return code;
|
||||
|
||||
if (upper.length() == 1) {
|
||||
char c = upper.charAt(0);
|
||||
if (c >= 'A' && c <= 'Z') return GLFW.GLFW_KEY_A + (c - 'A');
|
||||
if (c >= '0' && c <= '9') return GLFW.GLFW_KEY_0 + (c - '0');
|
||||
}
|
||||
throw UNKNOWN.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toUpperCase(Locale.ROOT);
|
||||
for (String n : NAMES.keySet()) {
|
||||
if (n.startsWith(remaining)) builder.suggest(n);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.AutismMacroManager;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MacroArgumentType implements ArgumentType<String> {
|
||||
private static volatile long cachedRevision = Long.MIN_VALUE;
|
||||
private static volatile List<Candidate> cachedCandidates = List.of();
|
||||
|
||||
private record Candidate(String value, String lower) {}
|
||||
|
||||
public static MacroArgumentType macroName() { return new MacroArgumentType(); }
|
||||
|
||||
public static String get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
|
||||
if (reader.canRead() && reader.peek() == '"') return reader.readQuotedString();
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (Candidate candidate : candidates()) {
|
||||
if (candidate.lower().startsWith(remaining)) builder.suggest(candidate.value());
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static List<Candidate> candidates() {
|
||||
AutismMacroManager manager = AutismMacroManager.get();
|
||||
long revision = manager.getRevision();
|
||||
List<Candidate> hit = cachedCandidates;
|
||||
if (cachedRevision == revision) return hit;
|
||||
List<Candidate> rebuilt = new ArrayList<>();
|
||||
for (autismclient.util.AutismMacro macro : manager.getAll()) {
|
||||
if (macro == null || macro.name == null) continue;
|
||||
String name = macro.name;
|
||||
String value = name.contains(" ") ? "\"" + name + "\"" : name;
|
||||
rebuilt.add(new Candidate(value, name.toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
hit = List.copyOf(rebuilt);
|
||||
cachedCandidates = hit;
|
||||
cachedRevision = revision;
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.AutismInventoryHelper;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public final class MenuSlotArgumentType implements ArgumentType<Integer> {
|
||||
private static final SimpleCommandExceptionType INVALID_SLOT = new SimpleCommandExceptionType(
|
||||
Component.literal("Unknown slot. Use gui1, hotbar1, inventory1, helmet, chestplate, leggings, boots, offhand, or a visible numeric slot id."));
|
||||
private static AbstractContainerMenu cachedMenu;
|
||||
private static int cachedSlotCount = -1;
|
||||
private static List<String> cachedValues = List.of();
|
||||
|
||||
private MenuSlotArgumentType() {
|
||||
}
|
||||
|
||||
public static MenuSlotArgumentType slot() {
|
||||
return new MenuSlotArgumentType();
|
||||
}
|
||||
|
||||
public static int get(CommandContext<AutismCommandSource> context, String name) {
|
||||
return context.getArgument(name, Integer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer parse(StringReader reader) throws CommandSyntaxException {
|
||||
String token = reader.readUnquotedString().trim().toLowerCase(Locale.ROOT);
|
||||
if (token.isEmpty()) throw INVALID_SLOT.create();
|
||||
|
||||
Integer named = parseNamed(token);
|
||||
if (named != null) return named;
|
||||
try {
|
||||
int numeric = Integer.parseInt(token);
|
||||
if (numeric >= 0) return numeric;
|
||||
} catch (NumberFormatException ignored) { }
|
||||
throw INVALID_SLOT.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
for (String value : values(mc)) {
|
||||
if (value.toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(value);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static List<String> values(Minecraft mc) {
|
||||
AbstractContainerMenu menu = mc.player == null ? null : mc.player.containerMenu;
|
||||
int slotCount = menu == null ? 0 : menu.slots.size();
|
||||
if (menu == cachedMenu && slotCount == cachedSlotCount) return cachedValues;
|
||||
Set<String> values = new LinkedHashSet<>();
|
||||
if (menu != null) {
|
||||
for (int handlerSlot = 0; handlerSlot < menu.slots.size(); handlerSlot++) {
|
||||
int visible = AutismInventoryHelper.toUserVisibleSlot(mc, handlerSlot);
|
||||
values.add(displayToken(visible));
|
||||
values.add(String.valueOf(visible));
|
||||
}
|
||||
} else {
|
||||
for (int slot = 1; slot <= 9; slot++) values.add("hotbar" + slot);
|
||||
}
|
||||
cachedMenu = menu;
|
||||
cachedSlotCount = slotCount;
|
||||
cachedValues = List.copyOf(values);
|
||||
return cachedValues;
|
||||
}
|
||||
|
||||
public static String displayToken(int visibleSlot) {
|
||||
if (visibleSlot >= AutismInventoryHelper.FIRST_GUI_SLOT) {
|
||||
return "gui" + (visibleSlot - AutismInventoryHelper.FIRST_GUI_SLOT + 1);
|
||||
}
|
||||
if (visibleSlot >= 0 && visibleSlot <= 8) return "hotbar" + (visibleSlot + 1);
|
||||
if (visibleSlot >= 9 && visibleSlot <= 35) return "inventory" + (visibleSlot - 8);
|
||||
return switch (visibleSlot) {
|
||||
case 36 -> "boots";
|
||||
case 37 -> "leggings";
|
||||
case 38 -> "chestplate";
|
||||
case 39 -> "helmet";
|
||||
case 40 -> "offhand";
|
||||
default -> String.valueOf(visibleSlot);
|
||||
};
|
||||
}
|
||||
|
||||
private static Integer parseNamed(String token) {
|
||||
return switch (token) {
|
||||
case "boots" -> 36;
|
||||
case "leggings" -> 37;
|
||||
case "chestplate" -> 38;
|
||||
case "helmet" -> 39;
|
||||
case "offhand" -> 40;
|
||||
default -> {
|
||||
Integer gui = parseIndexed(token, "gui", AutismInventoryHelper.FIRST_GUI_SLOT, Integer.MAX_VALUE);
|
||||
if (gui != null) yield gui;
|
||||
Integer hotbar = parseIndexed(token, "hotbar", 0, 9);
|
||||
if (hotbar != null) yield hotbar;
|
||||
yield parseIndexed(token, "inventory", 9, 27);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Integer parseIndexed(String token, String prefix, int offset, int maxIndex) {
|
||||
if (!token.startsWith(prefix)) return null;
|
||||
String suffix = token.substring(prefix.length());
|
||||
if (suffix.startsWith(":")) suffix = suffix.substring(1);
|
||||
try {
|
||||
int oneBased = Integer.parseInt(suffix);
|
||||
if (oneBased < 1 || oneBased > maxIndex) return null;
|
||||
return offset + oneBased - 1;
|
||||
} catch (NumberFormatException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.modules.ModuleRegistry;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ModuleArgumentType implements ArgumentType<String> {
|
||||
private static volatile int cachedRevision = Integer.MIN_VALUE;
|
||||
private static volatile List<Candidate> cachedCandidates = List.of();
|
||||
|
||||
private record Candidate(String value, String lower) {}
|
||||
|
||||
public static ModuleArgumentType moduleName() { return new ModuleArgumentType(); }
|
||||
|
||||
public static String get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (Candidate candidate : candidates()) {
|
||||
if (candidate.lower().startsWith(remaining)) builder.suggest(candidate.value());
|
||||
}
|
||||
|
||||
if ("all".startsWith(remaining)) builder.suggest("all");
|
||||
if ("hud".startsWith(remaining)) builder.suggest("hud");
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static List<Candidate> candidates() {
|
||||
int revision = ModuleRegistry.revision();
|
||||
List<Candidate> hit = cachedCandidates;
|
||||
if (cachedRevision == revision) return hit;
|
||||
List<Candidate> rebuilt = new ArrayList<>();
|
||||
for (String name : ModuleRegistry.names()) {
|
||||
if (name == null) continue;
|
||||
|
||||
String commandName = name.replace(' ', '-');
|
||||
rebuilt.add(new Candidate(commandName, commandName.toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
hit = List.copyOf(rebuilt);
|
||||
cachedCandidates = hit;
|
||||
cachedRevision = revision;
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.multi.MultiManager;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MultiBotArgumentType implements ArgumentType<String> {
|
||||
public static MultiBotArgumentType botName() {
|
||||
return new MultiBotArgumentType();
|
||||
}
|
||||
|
||||
public static String get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
if (reader.canRead() && reader.peek() == '"') return reader.readQuotedString();
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (MultiManager.BotHandle bot : MultiManager.get().liveBots()) {
|
||||
String name = bot.username();
|
||||
if (name == null || name.isBlank()) continue;
|
||||
if (name.toLowerCase(Locale.ROOT).startsWith(remaining)) {
|
||||
builder.suggest(name.contains(" ") ? "\"" + name + "\"" : name);
|
||||
}
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.multi.MultiProfile;
|
||||
import autismclient.util.multi.MultiProfileManager;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MultiProfileArgumentType implements ArgumentType<String> {
|
||||
private static volatile long cachedRevision = Long.MIN_VALUE;
|
||||
private static volatile List<Candidate> cachedCandidates = List.of();
|
||||
|
||||
private record Candidate(String value, String lower) {}
|
||||
|
||||
public static MultiProfileArgumentType profileName() {
|
||||
return new MultiProfileArgumentType();
|
||||
}
|
||||
|
||||
public static String get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
if (reader.canRead() && reader.peek() == '"') return reader.readQuotedString();
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (Candidate candidate : candidates()) {
|
||||
if (candidate.lower().startsWith(remaining)) builder.suggest(candidate.value());
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static List<Candidate> candidates() {
|
||||
MultiProfileManager manager = MultiProfileManager.get();
|
||||
long revision = manager.revision();
|
||||
List<Candidate> hit = cachedCandidates;
|
||||
if (cachedRevision == revision) return hit;
|
||||
List<Candidate> rebuilt = new ArrayList<>();
|
||||
for (MultiProfile profile : manager) {
|
||||
if (profile == null || profile.name == null) continue;
|
||||
String name = profile.name;
|
||||
String value = name.contains(" ") ? "\"" + name + "\"" : name;
|
||||
rebuilt.add(new Candidate(value, name.toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
hit = List.copyOf(rebuilt);
|
||||
cachedCandidates = hit;
|
||||
cachedRevision = revision;
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.AutismPacketRegistry;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class PacketClassArgumentType implements ArgumentType<String> {
|
||||
private static volatile List<Candidate> cachedCandidates;
|
||||
|
||||
private record Candidate(String value, String lower) {}
|
||||
|
||||
public static PacketClassArgumentType packetClass() { return new PacketClassArgumentType(); }
|
||||
|
||||
public static String get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
return reader.readUnquotedString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
|
||||
for (Candidate candidate : candidates()) {
|
||||
if (candidate.lower().contains(remaining)) builder.suggest(candidate.value());
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static List<Candidate> candidates() {
|
||||
List<Candidate> hit = cachedCandidates;
|
||||
if (hit != null) return hit;
|
||||
List<Candidate> rebuilt = new ArrayList<>();
|
||||
for (Class<? extends Packet<?>> cls : AutismPacketRegistry.getC2SPackets()) {
|
||||
String name = AutismPacketRegistry.getName(cls);
|
||||
if (name != null) rebuilt.add(new Candidate(name, name.toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
hit = List.copyOf(rebuilt);
|
||||
cachedCandidates = hit;
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public final class VisibleItemNameArgumentType implements ArgumentType<String> {
|
||||
private static AbstractContainerMenu cachedMenu;
|
||||
private static int cachedMenuRevision = Integer.MIN_VALUE;
|
||||
private static List<Candidate> cachedNames = List.of();
|
||||
|
||||
private record Candidate(String value, String lower) {}
|
||||
|
||||
private VisibleItemNameArgumentType() {
|
||||
}
|
||||
|
||||
public static VisibleItemNameArgumentType itemName() {
|
||||
return new VisibleItemNameArgumentType();
|
||||
}
|
||||
|
||||
public static String get(CommandContext<AutismCommandSource> context, String name) {
|
||||
return context.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(StringReader reader) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
|
||||
return reader.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
if (remaining.startsWith("\"")) remaining = remaining.substring(1);
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
for (Candidate candidate : names(mc)) {
|
||||
if (candidate.lower().startsWith(remaining)) {
|
||||
builder.suggest(StringArgumentType.escapeIfRequired(candidate.value()));
|
||||
}
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static List<Candidate> names(Minecraft mc) {
|
||||
AbstractContainerMenu menu = mc.player == null ? null : mc.player.containerMenu;
|
||||
int revision = menu == null ? -1 : menu.getStateId();
|
||||
if (menu == cachedMenu && revision == cachedMenuRevision) return cachedNames;
|
||||
|
||||
Set<String> names = new LinkedHashSet<>();
|
||||
if (menu != null) {
|
||||
for (int slot = 0; slot < menu.slots.size(); slot++) {
|
||||
ItemStack stack = menu.slots.get(slot).getItem();
|
||||
if (stack.isEmpty()) continue;
|
||||
String displayName = collapseWhitespace(stack.getHoverName().getString());
|
||||
if (!displayName.isEmpty()) names.add(displayName);
|
||||
}
|
||||
}
|
||||
List<Candidate> rebuilt = new java.util.ArrayList<>(names.size());
|
||||
for (String name : names) rebuilt.add(new Candidate(name, name.toLowerCase(Locale.ROOT)));
|
||||
cachedMenu = menu;
|
||||
cachedMenuRevision = revision;
|
||||
cachedNames = List.copyOf(rebuilt);
|
||||
return cachedNames;
|
||||
}
|
||||
|
||||
private static String collapseWhitespace(String input) {
|
||||
if (input == null || input.isBlank()) return "";
|
||||
StringBuilder out = new StringBuilder(input.length());
|
||||
boolean pendingSpace = false;
|
||||
for (int index = 0; index < input.length(); index++) {
|
||||
char chr = input.charAt(index);
|
||||
if (Character.isWhitespace(chr)) {
|
||||
pendingSpace = out.length() > 0;
|
||||
} else {
|
||||
if (pendingSpace) out.append(' ');
|
||||
out.append(chr);
|
||||
pendingSpace = false;
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package autismclient.commands.args;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class XCarrySlotArgumentType implements ArgumentType<Integer> {
|
||||
public static final int CURSOR = Integer.MIN_VALUE;
|
||||
|
||||
private static final Map<String, Integer> TOKENS = new LinkedHashMap<>();
|
||||
static {
|
||||
TOKENS.put("craft1", 1);
|
||||
TOKENS.put("craft2", 2);
|
||||
TOKENS.put("craft3", 3);
|
||||
TOKENS.put("craft4", 4);
|
||||
TOKENS.put("craft5", 0);
|
||||
TOKENS.put("crafting5", 0);
|
||||
TOKENS.put("result", 0);
|
||||
TOKENS.put("helmet", 5);
|
||||
TOKENS.put("chestplate", 6);
|
||||
TOKENS.put("leggings", 7);
|
||||
TOKENS.put("boots", 8);
|
||||
TOKENS.put("offhand", 45);
|
||||
TOKENS.put("cursor", CURSOR);
|
||||
}
|
||||
|
||||
private static final SimpleCommandExceptionType UNKNOWN =
|
||||
new SimpleCommandExceptionType(Component.literal("Unknown XCarry slot — use craft1..craft5 / helmet / chestplate / leggings / boots / offhand / cursor."));
|
||||
|
||||
public static XCarrySlotArgumentType slot() { return new XCarrySlotArgumentType(); }
|
||||
|
||||
public static int get(CommandContext<AutismCommandSource> ctx, String name) {
|
||||
return ctx.getArgument(name, Integer.class);
|
||||
}
|
||||
|
||||
public static String displayName(int slotId) {
|
||||
for (Map.Entry<String, Integer> e : TOKENS.entrySet()) if (e.getValue() == slotId) return e.getKey();
|
||||
return "slot " + slotId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer parse(StringReader reader) throws CommandSyntaxException {
|
||||
String raw = reader.readUnquotedString().toLowerCase(Locale.ROOT);
|
||||
Integer id = TOKENS.get(raw);
|
||||
if (id == null) throw UNKNOWN.create();
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (String name : TOKENS.keySet()) {
|
||||
if (name.startsWith(remaining)) builder.suggest(name);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
42
src/main/java/autismclient/commands/impl/BindCommand.java
Normal file
42
src/main/java/autismclient/commands/impl/BindCommand.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.args.KeyArgumentType;
|
||||
import autismclient.modules.AutismModule;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
public class BindCommand extends Command {
|
||||
public BindCommand() { super("bind", "Bind a key to a command. Usage: bind <key> <command…> | bind clear <key>"); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + prefix + "bind <key> <command>");
|
||||
AutismClientMessaging.sendPrefixed("§7Example: §f" + prefix + "bind G " + prefix + "macro myMacro");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("clear")
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("key", KeyArgumentType.key())
|
||||
.executes(ctx -> {
|
||||
int key = KeyArgumentType.get(ctx, "key");
|
||||
AutismModule.get().clearCommandBind(key);
|
||||
AutismClientMessaging.sendPrefixed("§eCleared bind for §f" + KeyArgumentType.keyName(key));
|
||||
return SUCCESS;
|
||||
})));
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("key", KeyArgumentType.key())
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("command", StringArgumentType.greedyString())
|
||||
.executes(ctx -> {
|
||||
int key = KeyArgumentType.get(ctx, "key");
|
||||
String cmd = StringArgumentType.getString(ctx, "command");
|
||||
AutismModule.get().setCommandBind(key, cmd);
|
||||
AutismClientMessaging.sendPrefixed("§aBound §f" + KeyArgumentType.keyName(key) + "§a → §f" + cmd);
|
||||
return SUCCESS;
|
||||
})));
|
||||
}
|
||||
}
|
||||
27
src/main/java/autismclient/commands/impl/BindsCommand.java
Normal file
27
src/main/java/autismclient/commands/impl/BindsCommand.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.args.KeyArgumentType;
|
||||
import autismclient.modules.AutismModule;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class BindsCommand extends Command {
|
||||
public BindsCommand() { super("binds", "List command keybinds (set with `bind`)."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
Map<Integer, String> binds = AutismModule.get().getCommandBinds();
|
||||
if (binds.isEmpty()) { AutismClientMessaging.sendPrefixed("§eNo command keybinds set."); return SUCCESS; }
|
||||
AutismClientMessaging.sendPrefixed("§e" + binds.size() + " command keybinds:");
|
||||
for (Map.Entry<Integer, String> e : binds.entrySet()) {
|
||||
AutismClientMessaging.sendPrefixed("§b" + KeyArgumentType.keyName(e.getKey()) + "§7 → §f" + e.getValue());
|
||||
}
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismInventoryHelper;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public final class ChangeSlotCommand extends Command {
|
||||
public ChangeSlotCommand() {
|
||||
super("change-slot", "Select hotbar slot 1-9.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(context -> {
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§eUsage: §f" + AutismCommands.effectivePrefix() + "change-slot <1-9>");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument(
|
||||
"hotbar-slot", IntegerArgumentType.integer(1, 9))
|
||||
.suggests((context, builder) -> CommandSuggest.literals(
|
||||
builder, "1", "2", "3", "4", "5", "6", "7", "8", "9"))
|
||||
.executes(context -> changeSlot(IntegerArgumentType.getInteger(context, "hotbar-slot"))));
|
||||
}
|
||||
|
||||
private static int changeSlot(int oneBasedSlot) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player == null || mc.getConnection() == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNot in a world.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
AutismInventoryHelper.selectHotbarSlot(mc, oneBasedSlot - 1);
|
||||
AutismClientMessaging.sendPrefixed("§aSelected hotbar slot §f" + oneBasedSlot + "§a.");
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
31
src/main/java/autismclient/commands/impl/ClearCommand.java
Normal file
31
src/main/java/autismclient/commands/impl/ClearCommand.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class ClearCommand extends Command {
|
||||
public ClearCommand() { super("clear", "Clear every message from the chat.", "cls"); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.gui == null || mc.gui.hud == null || mc.gui.hud.getChat() == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cChat unavailable.");
|
||||
return SUCCESS;
|
||||
}
|
||||
try {
|
||||
|
||||
mc.gui.hud.getChat().clearMessages(false);
|
||||
mc.gui.hud.getChat().resetChatScroll();
|
||||
} catch (Throwable t) {
|
||||
AutismClientMessaging.sendPrefixed("§cClear failed: " + t.getMessage());
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.args.VisibleItemNameArgumentType;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.macro.ItemTarget;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ContainerInput;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public final class ClickItemCommand extends Command {
|
||||
public ClickItemCommand() {
|
||||
super("click-item", "Click an item by its visible in-game name.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(context -> {
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§eUsage: §f" + AutismCommands.effectivePrefix()
|
||||
+ "click-item \"<item name>\" [click] [times]");
|
||||
return SUCCESS;
|
||||
});
|
||||
|
||||
RequiredArgumentBuilder<AutismCommandSource, String> item = RequiredArgumentBuilder
|
||||
.<AutismCommandSource, String>argument("item-name", VisibleItemNameArgumentType.itemName())
|
||||
.executes(context -> click(context, ItemClickCommandSupport.ClickSpec.click(
|
||||
"Left", ContainerInput.PICKUP, 0), 1));
|
||||
ItemClickCommandSupport.attachModes(item, ClickItemCommand::click);
|
||||
root.then(item);
|
||||
}
|
||||
|
||||
private static int click(
|
||||
com.mojang.brigadier.context.CommandContext<AutismCommandSource> context,
|
||||
ItemClickCommandSupport.ClickSpec spec,
|
||||
int times
|
||||
) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player == null || mc.player.containerMenu == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo active inventory or GUI.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
String requestedName = VisibleItemNameArgumentType.get(context, "item-name");
|
||||
int handlerSlot = findByVisibleName(mc.player.containerMenu, requestedName);
|
||||
if (handlerSlot < 0) {
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§cNo visible item named §f" + requestedName + "§c exists in the current GUI.");
|
||||
return SUCCESS;
|
||||
}
|
||||
return ItemClickCommandSupport.clickHandlerSlot(
|
||||
handlerSlot, spec, times, "\"" + requestedName + "\"");
|
||||
}
|
||||
|
||||
private static int findByVisibleName(AbstractContainerMenu menu, String requestedName) {
|
||||
String normalizedTarget = ItemTarget.normalize(requestedName);
|
||||
if (normalizedTarget.isEmpty()) return -1;
|
||||
for (int handlerSlot = 0; handlerSlot < menu.slots.size(); handlerSlot++) {
|
||||
ItemStack stack = menu.slots.get(handlerSlot).getItem();
|
||||
if (stack.isEmpty()) continue;
|
||||
if (normalizedTarget.equals(ItemTarget.normalize(stack.getHoverName().getString()))) {
|
||||
return handlerSlot;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.args.MenuSlotArgumentType;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismInventoryHelper;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.inventory.ContainerInput;
|
||||
|
||||
public final class ClickSlotCommand extends Command {
|
||||
public ClickSlotCommand() {
|
||||
super("click-slot", "Click a GUI/player slot with Item Click actions.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(context -> {
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§eUsage: §f" + AutismCommands.effectivePrefix() + "click-slot <slot> [click] [times]");
|
||||
return SUCCESS;
|
||||
});
|
||||
|
||||
RequiredArgumentBuilder<AutismCommandSource, Integer> slot = RequiredArgumentBuilder
|
||||
.<AutismCommandSource, Integer>argument("slot", MenuSlotArgumentType.slot())
|
||||
.executes(context -> click(context, ItemClickCommandSupport.ClickSpec.click(
|
||||
"Left", ContainerInput.PICKUP, 0), 1));
|
||||
ItemClickCommandSupport.attachModes(slot, ClickSlotCommand::click);
|
||||
root.then(slot);
|
||||
}
|
||||
|
||||
private static int click(
|
||||
com.mojang.brigadier.context.CommandContext<AutismCommandSource> context,
|
||||
ItemClickCommandSupport.ClickSpec spec,
|
||||
int times
|
||||
) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player == null || mc.player.containerMenu == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo active inventory or GUI.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int visibleSlot = MenuSlotArgumentType.get(context, "slot");
|
||||
int handlerSlot = AutismInventoryHelper.toHandlerSlot(mc, visibleSlot);
|
||||
return ItemClickCommandSupport.clickHandlerSlot(
|
||||
handlerSlot, spec, times, MenuSlotArgumentType.displayToken(visibleSlot));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
public class CommandsCommand extends Command {
|
||||
public CommandsCommand() { super("commands", "List all available commands.", "cmds"); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("§e" + AutismCommands.all().size() + " commands (prefix §f" + prefix + "§e):");
|
||||
StringBuilder line = new StringBuilder();
|
||||
for (Command c : AutismCommands.all()) {
|
||||
if (line.length() > 0) line.append("§7, ");
|
||||
line.append("§f").append(c.name());
|
||||
if (line.length() > 200) { AutismClientMessaging.sendPrefixed(line.toString()); line.setLength(0); }
|
||||
}
|
||||
if (line.length() > 0) AutismClientMessaging.sendPrefixed(line.toString());
|
||||
AutismClientMessaging.sendPrefixed("§7Type §f" + prefix + "help <command>§7 for details.");
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
56
src/main/java/autismclient/commands/impl/CopyPosCommand.java
Normal file
56
src/main/java/autismclient/commands/impl/CopyPosCommand.java
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.modules.PackFreecamState;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.multi.MultiPilot;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class CopyPosCommand extends Command {
|
||||
public CopyPosCommand() {
|
||||
super("copypos", "Copies current coordinates.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
if (PackFreecamState.isActive()) {
|
||||
String coordinates = format(PackFreecamState.footPosition(1.0f));
|
||||
if (mc != null && mc.keyboardHandler != null) mc.keyboardHandler.setClipboard(coordinates);
|
||||
AutismClientMessaging.sendPrefixed("§aPosition copied (freecam): §f" + coordinates);
|
||||
return SUCCESS;
|
||||
}
|
||||
Player controlled = MultiPilot.commandPlayer();
|
||||
if (mc == null || controlled == null || mc.keyboardHandler == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo controlled position available.");
|
||||
return SUCCESS;
|
||||
}
|
||||
Entity positionOwner = !MultiPilot.isActive() && controlled.getVehicle() != null
|
||||
? controlled.getVehicle() : controlled;
|
||||
String coordinates = format(positionOwner.position());
|
||||
mc.keyboardHandler.setClipboard(coordinates);
|
||||
AutismClientMessaging.sendPrefixed("§aPosition copied: §f" + coordinates);
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
static String format(Vec3 position) {
|
||||
if (position == null) return "0 0 0";
|
||||
return floorCoordinate(position.x) + " " + safeFeetY(position.y) + " " + floorCoordinate(position.z);
|
||||
}
|
||||
|
||||
private static int floorCoordinate(double value) {
|
||||
return Double.isFinite(value) ? (int) Math.floor(value) : 0;
|
||||
}
|
||||
|
||||
private static int safeFeetY(double value) {
|
||||
return Double.isFinite(value) ? (int) Math.ceil(value - 1.0E-7D) : 0;
|
||||
}
|
||||
}
|
||||
32
src/main/java/autismclient/commands/impl/DamageCommand.java
Normal file
32
src/main/java/autismclient/commands/impl/DamageCommand.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class DamageCommand extends Command {
|
||||
public DamageCommand() { super("damage", "Take damage to self (vanilla server allows via /damage if op)."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + AutismCommands.effectivePrefix() + "damage <amount>");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, Double>argument("amount", DoubleArgumentType.doubleArg(0.1, 1024.0))
|
||||
.suggests(CommandSuggest::damage)
|
||||
.executes(ctx -> {
|
||||
double amt = DoubleArgumentType.getDouble(ctx, "amount");
|
||||
var conn = Minecraft.getInstance().getConnection();
|
||||
if (conn == null) { AutismClientMessaging.sendPrefixed("§cNot connected."); return SUCCESS; }
|
||||
conn.sendCommand("damage @s " + amt);
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
50
src/main/java/autismclient/commands/impl/DelayCommand.java
Normal file
50
src/main/java/autismclient/commands/impl/DelayCommand.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.modules.AutismModule;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismSharedState;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
public class DelayCommand extends Command {
|
||||
public DelayCommand() { super("delay", "Toggle the GUI packet delay queue."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> apply(null));
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("state", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::state)
|
||||
.executes(ctx -> apply(StringArgumentType.getString(ctx, "state"))));
|
||||
}
|
||||
|
||||
private static int apply(String state) {
|
||||
boolean current = AutismSharedState.get().shouldDelayGuiPackets();
|
||||
if (state != null) {
|
||||
String lower = state.toLowerCase();
|
||||
if (!lower.equals("on") && !lower.equals("enable") && !lower.equals("true") && !lower.equals("1")
|
||||
&& !lower.equals("off") && !lower.equals("disable") && !lower.equals("false") && !lower.equals("0")
|
||||
&& !lower.equals("toggle")) {
|
||||
AutismClientMessaging.sendPrefixed("Unknown delay state: " + state);
|
||||
AutismClientMessaging.sendPrefixed("Use on, off, or toggle.");
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
boolean target = state == null ? !current
|
||||
: switch (state.toLowerCase()) {
|
||||
case "on", "enable", "true", "1" -> true;
|
||||
case "off", "disable", "false", "0" -> false;
|
||||
case "toggle" -> !current;
|
||||
default -> !current;
|
||||
};
|
||||
|
||||
AutismModule module = AutismModule.get();
|
||||
int flushed = module.applyDelayGuiPacketsUiBehavior(target);
|
||||
module.notifyDelayPacketsUiResult(target, flushed);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
public class DisconnectCommand extends Command {
|
||||
public DisconnectCommand() { super("disconnect", "Disconnect from the current server."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.getConnection() == null) { AutismClientMessaging.sendPrefixed("§cNot connected."); return SUCCESS; }
|
||||
try {
|
||||
mc.getConnection().getConnection().disconnect(Component.literal("Disconnected via .disconnect"));
|
||||
} catch (Throwable t) {
|
||||
AutismClientMessaging.sendPrefixed("§cDisconnect failed: " + t.getMessage());
|
||||
}
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerInputPacket;
|
||||
|
||||
public class DismountCommand extends Command {
|
||||
public DismountCommand() { super("dismount", "Force-dismount from any vehicle."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player == null || mc.getConnection() == null) { AutismClientMessaging.sendPrefixed("§cNot in a world."); return SUCCESS; }
|
||||
if (mc.player.getVehicle() == null) { AutismClientMessaging.sendPrefixed("§eNot riding anything."); return SUCCESS; }
|
||||
try {
|
||||
mc.player.removeVehicle();
|
||||
AutismClientMessaging.sendPrefixed("§aDismounted.");
|
||||
} catch (Throwable t) {
|
||||
AutismClientMessaging.sendPrefixed("§cDismount failed: " + t.getMessage());
|
||||
}
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
206
src/main/java/autismclient/commands/impl/DropCommand.java
Normal file
206
src/main/java/autismclient/commands/impl/DropCommand.java
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismDropHelper;
|
||||
import autismclient.util.AutismInventoryHelper;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DropCommand extends Command {
|
||||
public DropCommand() {
|
||||
super("drop", "Drop hand, full inventory, or an amount of a held/specific item.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
showUsage();
|
||||
return SUCCESS;
|
||||
});
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("hand")
|
||||
.executes(ctx -> dropHand()));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("fullinventory")
|
||||
.executes(ctx -> dropFullInventory()));
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument(
|
||||
"amount", IntegerArgumentType.integer(1))
|
||||
.suggests(CommandSuggest::counts)
|
||||
.executes(ctx -> dropHeldAmount(IntegerArgumentType.getInteger(ctx, "amount")))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument(
|
||||
"item", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::itemIds)
|
||||
.executes(ctx -> dropItemAmount(
|
||||
IntegerArgumentType.getInteger(ctx, "amount"),
|
||||
StringArgumentType.getString(ctx, "item")))));
|
||||
}
|
||||
|
||||
private static void showUsage() {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + prefix + "drop hand");
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + prefix + "drop fullinventory");
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + prefix + "drop <amount> [item_id]");
|
||||
}
|
||||
|
||||
private static int dropHand() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!ready(mc)) return SUCCESS;
|
||||
|
||||
ItemStack held = mc.player.getMainHandItem();
|
||||
if (held.isEmpty()) {
|
||||
AutismClientMessaging.sendPrefixed("§cYou are not holding an item.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int amount = held.getCount();
|
||||
Identifier id = BuiltInRegistries.ITEM.getKey(held.getItem());
|
||||
mc.player.drop(true);
|
||||
AutismClientMessaging.sendPrefixed("§aDropped §f" + amount + "x " + id + "§a.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static int dropFullInventory() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!ready(mc)) return SUCCESS;
|
||||
|
||||
int droppedItems = 0;
|
||||
int droppedStacks = 0;
|
||||
for (int inventorySlot = 0; inventorySlot < 36; inventorySlot++) {
|
||||
ItemStack stack = mc.player.getInventory().getItem(inventorySlot);
|
||||
if (stack.isEmpty()) continue;
|
||||
|
||||
int handlerSlot = AutismInventoryHelper.toHandlerSlot(mc, inventorySlot);
|
||||
if (handlerSlot < 0) continue;
|
||||
|
||||
int stackCount = stack.getCount();
|
||||
if (AutismDropHelper.dropFromHandlerSlot(mc, handlerSlot, 0) > 0) {
|
||||
droppedItems += stackCount;
|
||||
droppedStacks++;
|
||||
}
|
||||
}
|
||||
|
||||
if (droppedStacks == 0) {
|
||||
AutismClientMessaging.sendPrefixed("§eYour inventory is empty.");
|
||||
} else {
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§aDropped §f" + droppedItems + " items §7(" + droppedStacks + " stacks§7)§a.");
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static int dropHeldAmount(int requested) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!ready(mc)) return SUCCESS;
|
||||
|
||||
int selectedSlot = mc.player.getInventory().getSelectedSlot();
|
||||
ItemStack held = mc.player.getInventory().getItem(selectedSlot);
|
||||
if (held.isEmpty()) {
|
||||
AutismClientMessaging.sendPrefixed("§cYou are not holding an item.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
Identifier id = BuiltInRegistries.ITEM.getKey(held.getItem());
|
||||
return dropResolvedItemAmount(mc, requested, id, held.getItem());
|
||||
}
|
||||
|
||||
private static int dropItemAmount(int requested, String rawItemId) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (!ready(mc)) return SUCCESS;
|
||||
|
||||
Identifier id = parseItemId(rawItemId);
|
||||
Item item = id == null ? null : BuiltInRegistries.ITEM.getOptional(id).orElse(null);
|
||||
if (item == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown item id: §f" + rawItemId);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
return dropResolvedItemAmount(mc, requested, id, item);
|
||||
}
|
||||
|
||||
private static int dropResolvedItemAmount(Minecraft mc, int requested, Identifier id, Item item) {
|
||||
List<Integer> matchingSlots = matchingInventorySlots(mc, item);
|
||||
int available = 0;
|
||||
for (int slot : matchingSlots) {
|
||||
available += mc.player.getInventory().getItem(slot).getCount();
|
||||
}
|
||||
if (available == 0) {
|
||||
AutismClientMessaging.sendPrefixed("§cYou do not have §f" + id + "§c in your inventory.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int remaining = Math.min(requested, available);
|
||||
int dropped = 0;
|
||||
for (int inventorySlot : matchingSlots) {
|
||||
if (remaining <= 0) break;
|
||||
ItemStack stack = mc.player.getInventory().getItem(inventorySlot);
|
||||
if (stack.isEmpty() || stack.getItem() != item) continue;
|
||||
|
||||
int amount = Math.min(remaining, stack.getCount());
|
||||
int handlerSlot = AutismInventoryHelper.toHandlerSlot(mc, inventorySlot);
|
||||
if (handlerSlot < 0) continue;
|
||||
if (AutismDropHelper.dropFromHandlerSlot(mc, handlerSlot, amount) == 0) continue;
|
||||
|
||||
dropped += amount;
|
||||
remaining -= amount;
|
||||
}
|
||||
|
||||
if (dropped == 0) {
|
||||
AutismClientMessaging.sendPrefixed("§cCould not drop §f" + id + "§c from the current screen.");
|
||||
} else {
|
||||
reportDrop(id, dropped, requested);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static List<Integer> matchingInventorySlots(Minecraft mc, Item item) {
|
||||
List<Integer> slots = new ArrayList<>();
|
||||
int selected = mc.player.getInventory().getSelectedSlot();
|
||||
addIfMatching(mc, item, selected, slots);
|
||||
for (int slot = 0; slot < 9; slot++) {
|
||||
if (slot != selected) addIfMatching(mc, item, slot, slots);
|
||||
}
|
||||
for (int slot = 9; slot < 36; slot++) {
|
||||
addIfMatching(mc, item, slot, slots);
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
private static void addIfMatching(Minecraft mc, Item item, int slot, List<Integer> slots) {
|
||||
ItemStack stack = mc.player.getInventory().getItem(slot);
|
||||
if (!stack.isEmpty() && stack.getItem() == item) slots.add(slot);
|
||||
}
|
||||
|
||||
private static Identifier parseItemId(String raw) {
|
||||
if (raw == null || raw.isBlank()) return null;
|
||||
String value = raw.trim().toLowerCase(java.util.Locale.ROOT);
|
||||
return Identifier.tryParse(value.indexOf(':') >= 0 ? value : "minecraft:" + value);
|
||||
}
|
||||
|
||||
private static void reportDrop(Identifier id, int dropped, int requested) {
|
||||
if (dropped < requested) {
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§eDropped §f" + dropped + "/" + requested + "x " + id + "§e (all available).");
|
||||
} else {
|
||||
AutismClientMessaging.sendPrefixed("§aDropped §f" + dropped + "x " + id + "§a.");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean ready(Minecraft mc) {
|
||||
if (mc.player != null && mc.gameMode != null) return true;
|
||||
AutismClientMessaging.sendPrefixed("§cNot in a world.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
46
src/main/java/autismclient/commands/impl/FakeGmCommand.java
Normal file
46
src/main/java/autismclient/commands/impl/FakeGmCommand.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismFakeGamemode;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.world.level.GameType;
|
||||
|
||||
public class FakeGmCommand extends Command {
|
||||
public FakeGmCommand() {
|
||||
super("fakegm", "Set a fake client-side game mode.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + AutismCommands.effectivePrefix()
|
||||
+ "fakegm <survival|creative|adventure|spectator|reset>");
|
||||
AutismClientMessaging.sendPrefixed("§7Real change: " + AutismCommands.effectivePrefix() + "gamemode");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("mode", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::gamemodes)
|
||||
.executes(ctx -> {
|
||||
String mode = StringArgumentType.getString(ctx, "mode").toLowerCase(java.util.Locale.ROOT);
|
||||
AutismFakeGamemode.Result result;
|
||||
if ("reset".equals(mode) || "r".equals(mode)) {
|
||||
result = AutismFakeGamemode.reset();
|
||||
} else {
|
||||
GameType resolved = AutismFakeGamemode.parseMode(mode);
|
||||
if (resolved == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown mode: §f" + mode);
|
||||
return SUCCESS;
|
||||
}
|
||||
result = AutismFakeGamemode.apply(resolved);
|
||||
}
|
||||
AutismClientMessaging.sendPrefixed((result.success() ? "§a" : "§c") + result.message());
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
71
src/main/java/autismclient/commands/impl/FriendCommand.java
Normal file
71
src/main/java/autismclient/commands/impl/FriendCommand.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.modules.TeamsModule;
|
||||
import autismclient.util.AutismPlayerScanner;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public final class FriendCommand extends Command {
|
||||
public FriendCommand() {
|
||||
super("friend", "Manage the friends/teams list. add <name>, remove <name>, clear.", "team");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
|
||||
root.executes(ctx -> listFriends());
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("add")
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("name", StringArgumentType.word())
|
||||
.suggests(FriendCommand::suggestServerPlayers)
|
||||
.executes(ctx -> TeamsModule.addFriend(StringArgumentType.getString(ctx, "name")) ? SUCCESS : 0)));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("remove")
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("name", StringArgumentType.word())
|
||||
.suggests(FriendCommand::suggestFriends)
|
||||
.executes(ctx -> TeamsModule.removeFriend(StringArgumentType.getString(ctx, "name")) ? SUCCESS : 0)));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("clear")
|
||||
.executes(ctx -> TeamsModule.clearFriends() ? SUCCESS : 0));
|
||||
}
|
||||
|
||||
private static int listFriends() {
|
||||
List<String> friends = TeamsModule.storedFriendNames();
|
||||
if (friends.isEmpty()) {
|
||||
autismclient.util.AutismClientMessaging.sendPrefixed("§7Friend list is empty.");
|
||||
return SUCCESS;
|
||||
}
|
||||
autismclient.util.AutismClientMessaging.sendPrefixed("§7Friends (§f" + friends.size() + "§7): §f" + String.join("§7, §f", friends));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static CompletableFuture<Suggestions> suggestServerPlayers(CommandContext<AutismCommandSource> ctx,
|
||||
SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (AutismPlayerScanner.ScannedPlayer player : AutismPlayerScanner.scan(Minecraft.getInstance())) {
|
||||
String name = player.name();
|
||||
if (name != null && name.toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(name);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
private static CompletableFuture<Suggestions> suggestFriends(CommandContext<AutismCommandSource> ctx,
|
||||
SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (String name : TeamsModule.storedFriendNames()) {
|
||||
if (name != null && name.toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(name);
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismFakeGamemode;
|
||||
import autismclient.util.AutismGamemode;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.world.level.GameType;
|
||||
|
||||
public class GamemodeCommand extends Command {
|
||||
public GamemodeCommand() {
|
||||
super("gamemode", "Set your real game mode (no chat command sent).");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + AutismCommands.effectivePrefix()
|
||||
+ "gamemode <survival|creative|adventure|spectator>");
|
||||
AutismClientMessaging.sendPrefixed("§7Client-side only: " + AutismCommands.effectivePrefix() + "fakegm");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("mode", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::realGamemodes)
|
||||
.executes(ctx -> {
|
||||
String mode = StringArgumentType.getString(ctx, "mode").toLowerCase(java.util.Locale.ROOT);
|
||||
GameType resolved = AutismFakeGamemode.parseMode(mode);
|
||||
if (resolved == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown mode: §f" + mode);
|
||||
if ("reset".equals(mode) || "r".equals(mode)) {
|
||||
AutismClientMessaging.sendPrefixed("§7Reset only applies to the fake mode: §f"
|
||||
+ AutismCommands.effectivePrefix() + "fakegm reset");
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
AutismFakeGamemode.Result result = AutismGamemode.real(resolved);
|
||||
AutismClientMessaging.sendPrefixed((result.success() ? "§a" : "§c") + result.message());
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
123
src/main/java/autismclient/commands/impl/GhostBlockCommand.java
Normal file
123
src/main/java/autismclient/commands/impl/GhostBlockCommand.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.modules.GhostBlockModule;
|
||||
import autismclient.modules.Module;
|
||||
import autismclient.modules.ModuleRegistry;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import java.util.Locale;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
public class GhostBlockCommand extends Command {
|
||||
public GhostBlockCommand() {
|
||||
super("ghostblock", "Client-side ghost blocks: place, remove and manage blocks only you can see.", "gb");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + prefix + "ghostblock <toggle|reset|creative|block>");
|
||||
AutismClientMessaging.sendPrefixed("§7" + prefix + "ghostblock toggle §8- §7toggle the module");
|
||||
AutismClientMessaging.sendPrefixed("§7" + prefix + "ghostblock reset §8- §7remove all ghost blocks");
|
||||
AutismClientMessaging.sendPrefixed("§7" + prefix + "ghostblock creative [on|off] §8- §7fake creative gamemode");
|
||||
AutismClientMessaging.sendPrefixed("§7" + prefix + "ghostblock block [id|off] §8- §7arm a block for placement");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("toggle")
|
||||
.executes(ctx -> {
|
||||
GhostBlockModule module = module();
|
||||
if (module == null) return SUCCESS;
|
||||
module.toggle();
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("reset")
|
||||
.executes(ctx -> {
|
||||
GhostBlockModule module = module();
|
||||
if (module == null) return SUCCESS;
|
||||
int removed = module.restoreAll();
|
||||
AutismClientMessaging.sendPrefixed(removed == 0
|
||||
? "§7No ghost blocks to remove."
|
||||
: "§aRemoved §f" + removed + "§a ghost block" + (removed == 1 ? "" : "s")
|
||||
+ " and restored the original states.");
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("creative")
|
||||
.executes(ctx -> setCreative(null))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("state", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::state)
|
||||
.executes(ctx -> setCreative(StringArgumentType.getString(ctx, "state")))));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("block")
|
||||
.executes(ctx -> {
|
||||
GhostBlockModule module = module();
|
||||
if (module == null) return SUCCESS;
|
||||
Block armed = module.armedBlock();
|
||||
AutismClientMessaging.sendPrefixed(armed == null
|
||||
? "§7No ghost block armed."
|
||||
: "§7Armed ghost block: §f" + BuiltInRegistries.BLOCK.getKey(armed));
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("block", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::blockIds)
|
||||
.executes(ctx -> arm(StringArgumentType.getString(ctx, "block")))));
|
||||
}
|
||||
|
||||
private static int setCreative(String state) {
|
||||
GhostBlockModule module = module();
|
||||
if (module == null) return SUCCESS;
|
||||
boolean target;
|
||||
if (state == null || "toggle".equalsIgnoreCase(state)) {
|
||||
target = !module.isCreative();
|
||||
} else if ("on".equalsIgnoreCase(state) || "enable".equalsIgnoreCase(state)) {
|
||||
target = true;
|
||||
} else if ("off".equalsIgnoreCase(state) || "disable".equalsIgnoreCase(state)) {
|
||||
target = false;
|
||||
} else {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown state: §f" + state);
|
||||
return SUCCESS;
|
||||
}
|
||||
module.setCreative(target);
|
||||
if (target && !module.isEnabled()) module.setEnabled(true);
|
||||
AutismClientMessaging.sendPrefixed(target
|
||||
? "§aFake creative on. §7Whatever you place becomes a ghost block."
|
||||
: "§7Fake creative off. §7Back to your real gamemode.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static int arm(String input) {
|
||||
GhostBlockModule module = module();
|
||||
if (module == null) return SUCCESS;
|
||||
String value = input.toLowerCase(Locale.ROOT);
|
||||
if ("off".equals(value) || "none".equals(value) || "reset".equals(value)) {
|
||||
module.setArmedBlock(null, false);
|
||||
AutismClientMessaging.sendPrefixed("§7Ghost block disarmed; held blocks place ghosts again.");
|
||||
return SUCCESS;
|
||||
}
|
||||
Identifier id = Identifier.tryParse(value.contains(":") ? value : "minecraft:" + value);
|
||||
Block block = id == null ? null : BuiltInRegistries.BLOCK.getOptional(id).orElse(null);
|
||||
if (block == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown block: §f" + input);
|
||||
return SUCCESS;
|
||||
}
|
||||
module.setArmedBlock(block, false);
|
||||
if (!module.isEnabled()) module.setEnabled(true);
|
||||
AutismClientMessaging.sendPrefixed("§aGhost block: §f" + BuiltInRegistries.BLOCK.getKey(block)
|
||||
+ (module.isCreative() ? "" : " §7(switch Mode to Creative to place it)"));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static GhostBlockModule module() {
|
||||
Module module = ModuleRegistry.get("ghostblock");
|
||||
if (module instanceof GhostBlockModule ghostBlock) return ghostBlock;
|
||||
AutismClientMessaging.sendPrefixed("§cGhostBlock module is not available.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
59
src/main/java/autismclient/commands/impl/GiveCommand.java
Normal file
59
src/main/java/autismclient/commands/impl/GiveCommand.java
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class GiveCommand extends Command {
|
||||
public GiveCommand() { super("give", "Give item to self (creative only)."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + AutismCommands.effectivePrefix() + "give <item> [count]");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("item", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::itemIds)
|
||||
.executes(ctx -> give(StringArgumentType.getString(ctx, "item"), 1))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("count", IntegerArgumentType.integer(1, 64))
|
||||
.suggests(CommandSuggest::counts)
|
||||
.executes(ctx -> give(StringArgumentType.getString(ctx, "item"), IntegerArgumentType.getInteger(ctx, "count")))));
|
||||
}
|
||||
|
||||
private static int give(String itemId, int count) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
boolean pov = autismclient.util.multi.MultiPilot.isActive();
|
||||
if (!pov && mc.player == null) { AutismClientMessaging.sendPrefixed("§cNot in a world."); return SUCCESS; }
|
||||
if (!pov && !mc.player.getAbilities().instabuild) { AutismClientMessaging.sendPrefixed("§cCreative mode required."); return SUCCESS; }
|
||||
Identifier id = itemId.contains(":") ? Identifier.tryParse(itemId) : Identifier.tryParse("minecraft:" + itemId);
|
||||
if (id == null) { AutismClientMessaging.sendPrefixed("§cInvalid item id: §f" + itemId); return SUCCESS; }
|
||||
Item item = BuiltInRegistries.ITEM.getOptional(id).orElse(null);
|
||||
if (item == null) { AutismClientMessaging.sendPrefixed("§cUnknown item: §f" + id); return SUCCESS; }
|
||||
ItemStack stack = new ItemStack(item, count);
|
||||
String povResult = autismclient.util.multi.MultiPilot.commandGiveCreative(stack);
|
||||
if (povResult != null) {
|
||||
AutismClientMessaging.sendPrefixed(("Sent".equals(povResult) ? "§a" : "§c")
|
||||
+ "POV give: §f" + povResult);
|
||||
return SUCCESS;
|
||||
}
|
||||
int slot = mc.player.getInventory().getSelectedSlot();
|
||||
int containerSlot = 36 + slot;
|
||||
mc.getConnection().send(new ServerboundSetCreativeModeSlotPacket((short) containerSlot, stack));
|
||||
mc.player.getInventory().setItem(slot, stack);
|
||||
AutismClientMessaging.sendPrefixed("§aGave §f" + id + " x" + count);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
132
src/main/java/autismclient/commands/impl/HClipCommand.java
Normal file
132
src/main/java/autismclient/commands/impl/HClipCommand.java
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.macro.HClipAction;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class HClipCommand extends Command {
|
||||
public HClipCommand() {
|
||||
super("hclip", "Lets you clip through blocks horizontally (forward) with movement packets.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("\u00a7eUsage: \u00a7f" + prefix + "hclip <blocks>");
|
||||
AutismClientMessaging.sendPrefixed("\u00a77Modes: \u00a7fdefault, forward, back, padding, single, custom (paper alias still works)");
|
||||
AutismClientMessaging.sendPrefixed("\u00a77Examples: \u00a7f" + prefix + "hclip forward \u00a77or \u00a7f" + prefix + "hclip custom -25 10 20 true true true");
|
||||
return SUCCESS;
|
||||
});
|
||||
|
||||
root.then(blocksArgument("blocks", options -> options));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("forward")
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = HClipAction.Options.defaults(0.0);
|
||||
options.mode = HClipAction.Mode.FORWARD;
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("back")
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = HClipAction.Options.defaults(0.0);
|
||||
options.mode = HClipAction.Mode.BACK;
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("default")
|
||||
.then(blocksArgument("blocks", options -> options)));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("paper")
|
||||
.then(blocksArgument("blocks", options -> options)));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("padding")
|
||||
.then(blocksArgument("blocks", options -> options)));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("single")
|
||||
.then(blocksArgument("blocks", HClipAction.Options::singlePacket)));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("normal")
|
||||
.then(blocksArgument("blocks", HClipAction.Options::singlePacket)));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("custom")
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Double>argument("blocks", DoubleArgumentType.doubleArg())
|
||||
.suggests(CommandSuggest::offsets)
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("segment", IntegerArgumentType.integer(1, 50))
|
||||
.suggests(CommandSuggest::vclipSegments)
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = HClipAction.Options.defaults(DoubleArgumentType.getDouble(ctx, "blocks"));
|
||||
options.segmentBlocks = IntegerArgumentType.getInteger(ctx, "segment");
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("maxPackets", IntegerArgumentType.integer(1, 100))
|
||||
.suggests(CommandSuggest::vclipPacketLimits)
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = HClipAction.Options.defaults(DoubleArgumentType.getDouble(ctx, "blocks"));
|
||||
options.segmentBlocks = IntegerArgumentType.getInteger(ctx, "segment");
|
||||
options.maxPackets = IntegerArgumentType.getInteger(ctx, "maxPackets");
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Boolean>argument("updateLocal", BoolArgumentType.bool())
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = customOptions(ctx);
|
||||
options.updateLocalPosition = BoolArgumentType.getBool(ctx, "updateLocal");
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Boolean>argument("vehicle", BoolArgumentType.bool())
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = customOptions(ctx);
|
||||
options.updateLocalPosition = BoolArgumentType.getBool(ctx, "updateLocal");
|
||||
options.tryVehicleFirst = BoolArgumentType.getBool(ctx, "vehicle");
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Boolean>argument("forceGround", BoolArgumentType.bool())
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = customOptions(ctx);
|
||||
options.updateLocalPosition = BoolArgumentType.getBool(ctx, "updateLocal");
|
||||
options.tryVehicleFirst = BoolArgumentType.getBool(ctx, "vehicle");
|
||||
options.forceGrounded = BoolArgumentType.getBool(ctx, "forceGround");
|
||||
run(options);
|
||||
return SUCCESS;
|
||||
}))))))));
|
||||
}
|
||||
|
||||
private interface OptionsCustomizer {
|
||||
HClipAction.Options apply(HClipAction.Options options);
|
||||
}
|
||||
|
||||
private static RequiredArgumentBuilder<AutismCommandSource, Double> blocksArgument(String name, OptionsCustomizer customizer) {
|
||||
return RequiredArgumentBuilder.<AutismCommandSource, Double>argument(name, DoubleArgumentType.doubleArg())
|
||||
.suggests(CommandSuggest::offsets)
|
||||
.executes(ctx -> {
|
||||
HClipAction.Options options = HClipAction.Options.defaults(DoubleArgumentType.getDouble(ctx, name));
|
||||
run(customizer.apply(options));
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
private static HClipAction.Options customOptions(com.mojang.brigadier.context.CommandContext<AutismCommandSource> ctx) {
|
||||
HClipAction.Options options = HClipAction.Options.defaults(DoubleArgumentType.getDouble(ctx, "blocks"));
|
||||
options.segmentBlocks = IntegerArgumentType.getInteger(ctx, "segment");
|
||||
options.maxPackets = IntegerArgumentType.getInteger(ctx, "maxPackets");
|
||||
options.maxRoutePackets = options.maxPackets;
|
||||
return options;
|
||||
}
|
||||
|
||||
public static void hclip(double blocks) {
|
||||
run(HClipAction.Options.defaults(blocks));
|
||||
}
|
||||
|
||||
private static void run(HClipAction.Options options) {
|
||||
options.showMessage = true;
|
||||
HClipAction.perform(Minecraft.getInstance(), options);
|
||||
}
|
||||
}
|
||||
46
src/main/java/autismclient/commands/impl/HelpCommand.java
Normal file
46
src/main/java/autismclient/commands/impl/HelpCommand.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import java.util.Locale;
|
||||
|
||||
public class HelpCommand extends Command {
|
||||
public HelpCommand() { super("help", "Show usage info for a command (or list all)."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> { listAll(); return SUCCESS; });
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("command", StringArgumentType.word())
|
||||
.suggests((ctx, builder) -> {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (Command command : AutismCommands.all()) {
|
||||
if (command.name().toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(command.name());
|
||||
for (String alias : command.aliases()) {
|
||||
if (alias != null && alias.toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(alias);
|
||||
}
|
||||
}
|
||||
return builder.buildFuture();
|
||||
})
|
||||
.executes(ctx -> {
|
||||
String name = StringArgumentType.getString(ctx, "command");
|
||||
Command cmd = AutismCommands.find(name);
|
||||
if (cmd == null) { AutismClientMessaging.sendPrefixed("§cNo such command: §f" + name); return SUCCESS; }
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("§b" + prefix + cmd.name() + " §7" + cmd.description());
|
||||
if (cmd.aliases().length > 0) AutismClientMessaging.sendPrefixed("§7aliases: §f" + String.join(", ", cmd.aliases()));
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
|
||||
private static void listAll() {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
for (Command c : AutismCommands.all()) {
|
||||
AutismClientMessaging.sendPrefixed("§b" + prefix + c.name() + "§7 - " + c.description());
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/main/java/autismclient/commands/impl/IrcCommand.java
Normal file
34
src/main/java/autismclient/commands/impl/IrcCommand.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.mm.MatchmakingManager;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
public class IrcCommand extends Command {
|
||||
public IrcCommand() { super("irc", "Send a message to the Matchmaking lobby chat."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + AutismCommands.effectivePrefix() + "irc <message>");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("message", StringArgumentType.greedyString())
|
||||
.executes(ctx -> {
|
||||
String msg = StringArgumentType.getString(ctx, "message");
|
||||
MatchmakingManager mm = MatchmakingManager.get();
|
||||
if (!mm.inLobby()) {
|
||||
AutismClientMessaging.sendPrefixed("§cNot in a lobby. Open Matchmaking and join or create one first.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
mm.sendChat(msg);
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismDropHelper;
|
||||
import autismclient.util.AutismInventoryClickHelper;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.inventory.ContainerInput;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
final class ItemClickCommandSupport {
|
||||
static final int MAX_REPEAT_COUNT = 100_000;
|
||||
|
||||
private ItemClickCommandSupport() {
|
||||
}
|
||||
|
||||
record ClickSpec(String label, ContainerInput input, int button, DropMode dropMode) {
|
||||
static ClickSpec click(String label, ContainerInput input, int button) {
|
||||
return new ClickSpec(label, input, button, DropMode.NONE);
|
||||
}
|
||||
|
||||
static ClickSpec drop(String label, DropMode dropMode) {
|
||||
return new ClickSpec(label, ContainerInput.THROW, dropMode == DropMode.STACK ? 1 : 0, dropMode);
|
||||
}
|
||||
}
|
||||
|
||||
enum DropMode {
|
||||
NONE,
|
||||
ITEM,
|
||||
STACK
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ClickExecutor {
|
||||
int execute(CommandContext<AutismCommandSource> context, ClickSpec spec, int times);
|
||||
}
|
||||
|
||||
static void attachModes(ArgumentBuilder<AutismCommandSource, ?> target, ClickExecutor executor) {
|
||||
target.then(repeated("left", ClickSpec.click("Left", ContainerInput.PICKUP, 0), executor));
|
||||
target.then(repeated("right", ClickSpec.click("Right", ContainerInput.PICKUP, 1), executor));
|
||||
target.then(repeated("middle", ClickSpec.click("Middle", ContainerInput.PICKUP, 2), executor));
|
||||
target.then(repeated("shift-left", ClickSpec.click("Shift Left", ContainerInput.QUICK_MOVE, 0), executor));
|
||||
target.then(repeated("shift-right", ClickSpec.click("Shift Right", ContainerInput.QUICK_MOVE, 1), executor));
|
||||
target.then(repeated("clone", ClickSpec.click("Clone", ContainerInput.CLONE, 2), executor));
|
||||
target.then(repeated("pickup-all", ClickSpec.click("Pick Up All", ContainerInput.PICKUP_ALL, 0), executor));
|
||||
target.then(repeated("drop-item", ClickSpec.drop("Drop Item", DropMode.ITEM), executor));
|
||||
target.then(single("drop-stack", ClickSpec.drop("Drop Stack", DropMode.STACK), executor));
|
||||
|
||||
LiteralArgumentBuilder<AutismCommandSource> swap = LiteralArgumentBuilder.literal("swap");
|
||||
RequiredArgumentBuilder<AutismCommandSource, Integer> hotbar = RequiredArgumentBuilder
|
||||
.<AutismCommandSource, Integer>argument("swap-hotbar", IntegerArgumentType.integer(1, 9))
|
||||
.suggests((context, builder) -> CommandSuggest.literals(
|
||||
builder, "1", "2", "3", "4", "5", "6", "7", "8", "9"))
|
||||
.executes(context -> executor.execute(
|
||||
context,
|
||||
ClickSpec.click("Swap " + IntegerArgumentType.getInteger(context, "swap-hotbar"),
|
||||
ContainerInput.SWAP,
|
||||
IntegerArgumentType.getInteger(context, "swap-hotbar") - 1),
|
||||
1));
|
||||
hotbar.then(timesArgument((context, times) -> executor.execute(
|
||||
context,
|
||||
ClickSpec.click("Swap " + IntegerArgumentType.getInteger(context, "swap-hotbar"),
|
||||
ContainerInput.SWAP,
|
||||
IntegerArgumentType.getInteger(context, "swap-hotbar") - 1),
|
||||
times)));
|
||||
swap.then(hotbar);
|
||||
target.then(swap);
|
||||
}
|
||||
|
||||
static int clickHandlerSlot(int handlerSlot, ClickSpec spec, int times, String targetLabel) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player == null || mc.gameMode == null || mc.getConnection() == null || mc.player.containerMenu == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo active inventory or GUI.");
|
||||
return 1;
|
||||
}
|
||||
if (handlerSlot < 0 || handlerSlot >= mc.player.containerMenu.slots.size()) {
|
||||
AutismClientMessaging.sendPrefixed("§cThat slot does not exist in the current GUI.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int requested = Math.max(1, times);
|
||||
int completed = 0;
|
||||
if (spec.dropMode() == DropMode.ITEM) {
|
||||
ItemStack stack = mc.player.containerMenu.slots.get(handlerSlot).getItem();
|
||||
if (!stack.isEmpty()) {
|
||||
int actual = Math.min(requested, stack.getCount());
|
||||
if (AutismDropHelper.dropFromHandlerSlot(mc, handlerSlot, actual) > 0) completed = actual;
|
||||
}
|
||||
} else if (spec.dropMode() == DropMode.STACK) {
|
||||
completed = AutismDropHelper.dropFromHandlerSlot(mc, handlerSlot, 0) > 0 ? 1 : 0;
|
||||
} else {
|
||||
for (int i = 0; i < requested; i++) {
|
||||
if (!AutismInventoryClickHelper.click(mc, handlerSlot, spec.button(), spec.input())) {
|
||||
break;
|
||||
}
|
||||
completed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (completed == 0) {
|
||||
AutismClientMessaging.sendPrefixed("§cCould not click §f" + targetLabel + "§c.");
|
||||
} else {
|
||||
String repeat = completed > 1 && spec.dropMode() != DropMode.STACK ? " x" + completed : "";
|
||||
String partial = completed < requested && spec.dropMode() != DropMode.STACK
|
||||
? " §e(" + completed + "/" + requested + ")"
|
||||
: "";
|
||||
AutismClientMessaging.sendPrefixed(
|
||||
"§a" + spec.label() + " clicked §f" + targetLabel + "§a" + repeat + "." + partial);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<AutismCommandSource> repeated(
|
||||
String literal,
|
||||
ClickSpec spec,
|
||||
ClickExecutor executor
|
||||
) {
|
||||
LiteralArgumentBuilder<AutismCommandSource> node = single(literal, spec, executor);
|
||||
node.then(timesArgument((context, times) -> executor.execute(context, spec, times)));
|
||||
return node;
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<AutismCommandSource> single(
|
||||
String literal,
|
||||
ClickSpec spec,
|
||||
ClickExecutor executor
|
||||
) {
|
||||
return LiteralArgumentBuilder.<AutismCommandSource>literal(literal)
|
||||
.executes(context -> executor.execute(context, spec, 1));
|
||||
}
|
||||
|
||||
private static RequiredArgumentBuilder<AutismCommandSource, Integer> timesArgument(TimesExecutor executor) {
|
||||
return RequiredArgumentBuilder.<AutismCommandSource, Integer>argument(
|
||||
"times", IntegerArgumentType.integer(1, MAX_REPEAT_COUNT))
|
||||
.suggests(CommandSuggest::counts)
|
||||
.executes(context -> executor.execute(context, IntegerArgumentType.getInteger(context, "times")));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface TimesExecutor {
|
||||
int execute(CommandContext<AutismCommandSource> context, int times);
|
||||
}
|
||||
}
|
||||
197
src/main/java/autismclient/commands/impl/MacroCommand.java
Normal file
197
src/main/java/autismclient/commands/impl/MacroCommand.java
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.args.MacroArgumentType;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismMacroManager;
|
||||
import autismclient.util.macro.MacroExecutor;
|
||||
import autismclient.util.multi.MultiManager;
|
||||
import autismclient.util.multi.MultiTakeoverState;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.Set;
|
||||
|
||||
public class MacroCommand extends Command {
|
||||
private static final ConcurrentHashMap<String, AtomicBoolean> RUNNING_LOOPS = new ConcurrentHashMap<>();
|
||||
|
||||
public MacroCommand() { super("macro", "Run a macro, optionally N times with a delay between starts."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
String prefix = AutismCommands.effectivePrefix();
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + prefix + "macro <name> [times] [delayTicks]");
|
||||
AutismClientMessaging.sendPrefixed("§7Also: §f" + prefix + "macro stop [name] §7or §f" + prefix + "macro clear");
|
||||
return SUCCESS;
|
||||
});
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("stop")
|
||||
.executes(ctx -> {
|
||||
String pov = MultiTakeoverState.activeAccountId();
|
||||
if (pov != null) {
|
||||
stopPovLoops(pov);
|
||||
MultiManager mgr = MultiManager.getIfInitialized();
|
||||
MultiManager.BroadcastResult result = mgr == null
|
||||
? new MultiManager.BroadcastResult(0, 1, 0, java.util.List.of("Multi unavailable"))
|
||||
: mgr.stopMacroOnScope(Set.of(pov));
|
||||
AutismClientMessaging.sendPrefixed("§ePOV macro stop: §f" + result.summary());
|
||||
return SUCCESS;
|
||||
}
|
||||
stopAllLoops();
|
||||
AutismMacroManager.get().stopMacro();
|
||||
AutismClientMessaging.sendPrefixed("§eStopped all macros.");
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("name", MacroArgumentType.macroName())
|
||||
.executes(ctx -> {
|
||||
String name = MacroArgumentType.get(ctx, "name");
|
||||
String pov = MultiTakeoverState.activeAccountId();
|
||||
if (pov != null) {
|
||||
AtomicBoolean flag = RUNNING_LOOPS.remove(povLoopKey(pov, name));
|
||||
if (flag != null) flag.set(false);
|
||||
MultiManager mgr = MultiManager.getIfInitialized();
|
||||
MultiManager.BroadcastResult result = mgr == null
|
||||
? new MultiManager.BroadcastResult(0, 1, 0, java.util.List.of("Multi unavailable"))
|
||||
: mgr.stopMacroOnScope(Set.of(pov));
|
||||
AutismClientMessaging.sendPrefixed("§eStopped POV macro §f" + name + "§e: " + result.summary());
|
||||
return SUCCESS;
|
||||
}
|
||||
AtomicBoolean flag = RUNNING_LOOPS.remove(name.toLowerCase());
|
||||
if (flag != null) flag.set(false);
|
||||
MacroExecutor.stopMacro(name);
|
||||
AutismClientMessaging.sendPrefixed("§eStopped: §f" + name);
|
||||
return SUCCESS;
|
||||
})));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("clear")
|
||||
.executes(ctx -> {
|
||||
String pov = MultiTakeoverState.activeAccountId();
|
||||
if (pov != null) {
|
||||
stopPovLoops(pov);
|
||||
AutismClientMessaging.sendPrefixed("§eCleared POV macro loops for §f" + pov + "§e.");
|
||||
} else {
|
||||
stopAllLoops();
|
||||
AutismClientMessaging.sendPrefixed("§eCleared macro loops.");
|
||||
}
|
||||
return SUCCESS;
|
||||
}));
|
||||
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("name", MacroArgumentType.macroName())
|
||||
.executes(ctx -> startLoop(MacroArgumentType.get(ctx, "name"), 1, 0))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("times", IntegerArgumentType.integer(1, 100_000))
|
||||
.executes(ctx -> startLoop(MacroArgumentType.get(ctx, "name"), IntegerArgumentType.getInteger(ctx, "times"), 0))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("delayTicks", IntegerArgumentType.integer(0, 20 * 60 * 60))
|
||||
.executes(ctx -> startLoop(
|
||||
MacroArgumentType.get(ctx, "name"),
|
||||
IntegerArgumentType.getInteger(ctx, "times"),
|
||||
IntegerArgumentType.getInteger(ctx, "delayTicks"))))));
|
||||
}
|
||||
|
||||
private static int startLoop(String name, int times, int delayTicks) {
|
||||
autismclient.util.AutismMacro macro = AutismMacroManager.get().get(name);
|
||||
if (macro == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cMacro not found: §f" + name);
|
||||
return SUCCESS;
|
||||
}
|
||||
String pov = MultiTakeoverState.activeAccountId();
|
||||
if (pov != null) return startPovLoop(name, macro, times, delayTicks, pov);
|
||||
if (times <= 1 && delayTicks <= 0) {
|
||||
AutismMacroManager.get().executeMacro(name);
|
||||
return SUCCESS;
|
||||
}
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
RUNNING_LOOPS.put(name.toLowerCase(), flag);
|
||||
long sleepMs = Math.max(0L, delayTicks * 50L);
|
||||
final int totalTimes = Math.max(1, times);
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
for (int i = 0; i < totalTimes && flag.get(); i++) {
|
||||
AutismMacroManager.get().executeMacro(name);
|
||||
|
||||
long start = System.nanoTime();
|
||||
long waitTimeoutNanos = 24L * 60L * 60L * 1_000_000_000L;
|
||||
while (MacroExecutor.isMacroRunning(name) && flag.get()
|
||||
&& System.nanoTime() - start < waitTimeoutNanos) {
|
||||
Thread.sleep(50);
|
||||
}
|
||||
if (!flag.get()) break;
|
||||
if (i + 1 < totalTimes && sleepMs > 0) Thread.sleep(sleepMs);
|
||||
}
|
||||
} catch (InterruptedException ignored) { Thread.currentThread().interrupt(); }
|
||||
finally { RUNNING_LOOPS.remove(name.toLowerCase(), flag); }
|
||||
}, "MacroLoop-" + name);
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
AutismClientMessaging.sendPrefixed("§aQueued " + totalTimes + "x §f" + name + "§a (delay " + delayTicks + "t)");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static int startPovLoop(String name, autismclient.util.AutismMacro macro, int times,
|
||||
int delayTicks, String accountId) {
|
||||
MultiManager mgr = MultiManager.getIfInitialized();
|
||||
if (mgr == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cMulti is unavailable.");
|
||||
return SUCCESS;
|
||||
}
|
||||
Set<String> scope = Set.of(accountId);
|
||||
if (times <= 1 && delayTicks <= 0) {
|
||||
MultiManager.BroadcastResult result = mgr.runMacroDirect(macro, scope);
|
||||
AutismClientMessaging.sendPrefixed((result.sent() > 0 ? "§a" : "§c")
|
||||
+ "POV macro §f" + name + "§a: " + result.summary());
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
String key = povLoopKey(accountId, name);
|
||||
AtomicBoolean flag = new AtomicBoolean(true);
|
||||
AtomicBoolean previous = RUNNING_LOOPS.put(key, flag);
|
||||
if (previous != null) previous.set(false);
|
||||
long sleepMs = Math.max(0L, delayTicks * 50L);
|
||||
int totalTimes = Math.max(1, times);
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
for (int i = 0; i < totalTimes && flag.get(); i++) {
|
||||
mgr.runMacroDirect(macro, scope);
|
||||
long start = System.nanoTime();
|
||||
long timeout = 24L * 60L * 60L * 1_000_000_000L;
|
||||
while (mgr.isMacroPlayingOnScope(name, scope) && flag.get()
|
||||
&& System.nanoTime() - start < timeout) {
|
||||
Thread.sleep(50L);
|
||||
}
|
||||
if (!flag.get()) break;
|
||||
if (i + 1 < totalTimes && sleepMs > 0L) Thread.sleep(sleepMs);
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
RUNNING_LOOPS.remove(key, flag);
|
||||
}
|
||||
}, "PovMacroLoop-" + name);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
AutismClientMessaging.sendPrefixed("§aQueued POV §f" + totalTimes + "x " + name
|
||||
+ "§a for §f" + accountId + "§a.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static String povLoopKey(String accountId, String name) {
|
||||
return "pov:" + accountId.toLowerCase(java.util.Locale.ROOT) + ":" + name.toLowerCase(java.util.Locale.ROOT);
|
||||
}
|
||||
|
||||
private static void stopPovLoops(String accountId) {
|
||||
String prefix = "pov:" + accountId.toLowerCase(java.util.Locale.ROOT) + ":";
|
||||
RUNNING_LOOPS.forEach((key, flag) -> {
|
||||
if (key.startsWith(prefix) && RUNNING_LOOPS.remove(key, flag)) flag.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
private static void stopAllLoops() {
|
||||
for (AtomicBoolean flag : RUNNING_LOOPS.values()) flag.set(false);
|
||||
RUNNING_LOOPS.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.modules.AutismModule;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
public class MatchmakingCommand extends Command {
|
||||
public MatchmakingCommand() { super("matchmaking", "Toggle the Matchmaking overlay.", "mm"); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
mc.execute(() -> {
|
||||
AutismModule mod = AutismModule.get();
|
||||
if (mod != null) mod.toggleMatchmakingUiBehavior();
|
||||
});
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
27
src/main/java/autismclient/commands/impl/ModulesCommand.java
Normal file
27
src/main/java/autismclient/commands/impl/ModulesCommand.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.modules.ModuleRegistry;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
public class ModulesCommand extends Command {
|
||||
public ModulesCommand() { super("modules", "List installed modules.", "features"); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
java.util.List<String> names = ModuleRegistry.names();
|
||||
AutismClientMessaging.sendPrefixed("§e" + names.size() + " modules:");
|
||||
StringBuilder line = new StringBuilder();
|
||||
for (String n : names) {
|
||||
if (line.length() > 0) line.append("§7, ");
|
||||
line.append("§f").append(n);
|
||||
if (line.length() > 200) { AutismClientMessaging.sendPrefixed(line.toString()); line.setLength(0); }
|
||||
}
|
||||
if (line.length() > 0) AutismClientMessaging.sendPrefixed(line.toString());
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
}
|
||||
184
src/main/java/autismclient/commands/impl/MultiCommand.java
Normal file
184
src/main/java/autismclient/commands/impl/MultiCommand.java
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.args.MacroArgumentType;
|
||||
import autismclient.commands.args.MultiBotArgumentType;
|
||||
import autismclient.commands.args.MultiProfileArgumentType;
|
||||
import autismclient.gui.screen.AutismMultiConsoleScreen;
|
||||
import autismclient.gui.screen.AutismMultiScreen;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismMacro;
|
||||
import autismclient.util.AutismMacroManager;
|
||||
import autismclient.util.multi.MultiManager;
|
||||
import autismclient.util.multi.MultiProfile;
|
||||
import autismclient.util.multi.MultiProfileManager;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class MultiCommand extends Command {
|
||||
public MultiCommand() {
|
||||
super("multi", "Open the Multi menu, or launch/stop a profile.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> { openMenu(); return SUCCESS; });
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("open")
|
||||
.executes(ctx -> { openMenu(); return SUCCESS; }));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("stop")
|
||||
.executes(ctx -> { stopBatch(); return SUCCESS; }));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("status")
|
||||
.executes(ctx -> { status(); return SUCCESS; }));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("launch")
|
||||
.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + AutismCommands.effectivePrefix() + "multi launch <profile>");
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("profile", MultiProfileArgumentType.profileName())
|
||||
.executes(ctx -> { launch(MultiProfileArgumentType.get(ctx, "profile")); return SUCCESS; })));
|
||||
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("macro")
|
||||
.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + AutismCommands.effectivePrefix()
|
||||
+ "multi macro <macro> [bot] §7- no bot = all bots");
|
||||
return SUCCESS;
|
||||
})
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("macro", MacroArgumentType.macroName())
|
||||
.executes(ctx -> { runMacro(MacroArgumentType.get(ctx, "macro"), ""); return SUCCESS; })
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("bot", MultiBotArgumentType.botName())
|
||||
.executes(ctx -> {
|
||||
runMacro(MacroArgumentType.get(ctx, "macro"), MultiBotArgumentType.get(ctx, "bot"));
|
||||
return SUCCESS;
|
||||
}))));
|
||||
}
|
||||
|
||||
private static void runMacro(String macroName, String botName) {
|
||||
MultiManager manager = MultiManager.get();
|
||||
if (!manager.isActive()) {
|
||||
AutismClientMessaging.sendPrefixed("§7No Multi batch is active.");
|
||||
return;
|
||||
}
|
||||
AutismMacro macro = AutismMacroManager.get().get(macroName);
|
||||
if (macro == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo macro named: §f" + macroName);
|
||||
return;
|
||||
}
|
||||
Set<String> scope = manager.scopeForBot(botName);
|
||||
if (scope == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo bot named: §f" + botName);
|
||||
String live = liveBotNames(manager);
|
||||
if (!live.isEmpty()) AutismClientMessaging.sendPrefixed("§7Bots: §f" + live);
|
||||
return;
|
||||
}
|
||||
MultiManager.BroadcastResult result = manager.runMacroDirect(macro, scope);
|
||||
String target = botName == null || botName.isBlank() ? "all bots" : botName;
|
||||
AutismClientMessaging.sendPrefixed("§aRunning §f" + macro.name + " §aon §f" + target
|
||||
+ " §7- " + result.summary());
|
||||
}
|
||||
|
||||
private static String liveBotNames(MultiManager manager) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (MultiManager.BotHandle bot : manager.liveBots()) {
|
||||
if (sb.length() > 0) sb.append("§7, §f");
|
||||
sb.append(bot.username());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void openMenu() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc == null) return;
|
||||
mc.execute(() -> {
|
||||
if (mc.gui.screen() != null) return;
|
||||
Runnable proceed = () -> {
|
||||
if (mc.level != null) {
|
||||
|
||||
autismclient.modules.AutismModule module = autismclient.modules.AutismModule.get();
|
||||
if (module != null) module.openMultiUiInGame();
|
||||
} else if (MultiManager.get().isActive()) {
|
||||
mc.gui.setScreen(new AutismMultiConsoleScreen(null));
|
||||
} else {
|
||||
mc.gui.setScreen(new AutismMultiScreen(null, currentServerAddress(mc)));
|
||||
}
|
||||
};
|
||||
|
||||
autismclient.gui.screen.AutismMultiDisclaimerScreen.open(mc, null, proceed);
|
||||
});
|
||||
}
|
||||
|
||||
private static void launch(String name) {
|
||||
MultiProfile profile = resolveByName(MultiProfileManager.get().all(), name);
|
||||
if (profile == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo profile named: §f" + name);
|
||||
String available = availableNames();
|
||||
if (!available.isEmpty()) AutismClientMessaging.sendPrefixed("§7Profiles: §f" + available);
|
||||
return;
|
||||
}
|
||||
MultiManager.StartResult result = MultiManager.get().start(profile);
|
||||
if (result.ok()) {
|
||||
AutismClientMessaging.sendPrefixed("§aLaunching Multi profile: §f" + profile.name
|
||||
+ " §7(" + profile.sessions.size() + " account(s)) - run "
|
||||
+ AutismCommands.effectivePrefix() + "multi to open the console.");
|
||||
} else {
|
||||
AutismClientMessaging.sendPrefixed("§cCould not launch: §f" + result.message());
|
||||
}
|
||||
}
|
||||
|
||||
private static void stopBatch() {
|
||||
if (!MultiManager.get().isActive()) {
|
||||
AutismClientMessaging.sendPrefixed("§7No Multi batch is active.");
|
||||
return;
|
||||
}
|
||||
MultiManager.get().disconnectAll("Stopped via command");
|
||||
AutismClientMessaging.sendPrefixed("§eStopped the Multi batch.");
|
||||
}
|
||||
|
||||
private static void status() {
|
||||
MultiManager mm = MultiManager.get();
|
||||
if (!mm.isActive()) {
|
||||
AutismClientMessaging.sendPrefixed("§7Multi: no batch active.");
|
||||
return;
|
||||
}
|
||||
MultiProfile active = mm.activeProfile();
|
||||
String name = active == null || active.name == null ? "(unknown)" : active.name;
|
||||
AutismClientMessaging.sendPrefixed("§aMulti: §f" + name
|
||||
+ " §7- §f" + mm.readyCount() + "§7 ready, §f" + mm.connectedCount() + "§7 connected.");
|
||||
}
|
||||
|
||||
private static String currentServerAddress(Minecraft mc) {
|
||||
ServerData sd = mc.getCurrentServer();
|
||||
return sd == null || sd.ip == null ? "" : sd.ip.trim();
|
||||
}
|
||||
|
||||
private static String availableNames() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (MultiProfile p : MultiProfileManager.get().all()) {
|
||||
if (p == null || p.name == null) continue;
|
||||
if (sb.length() > 0) sb.append("§7, §f");
|
||||
sb.append(p.name);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static MultiProfile resolveByName(Collection<MultiProfile> profiles, String name) {
|
||||
if (profiles == null || name == null) return null;
|
||||
String want = name.trim().toLowerCase(Locale.ROOT);
|
||||
if (want.isEmpty()) return null;
|
||||
for (MultiProfile p : profiles) {
|
||||
if (p != null && p.name != null && p.name.trim().toLowerCase(Locale.ROOT).equals(want)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.util.AutismNameScrape;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
public final class NameScrapeCommand extends Command {
|
||||
public NameScrapeCommand() {
|
||||
super("namescrape", "Scrape player names to clipboard. [count] limits it; deep = exhaustive; pause/resume/stop.",
|
||||
"scrapenames");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> apply(0, false));
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("count", IntegerArgumentType.integer(1))
|
||||
.executes(ctx -> apply(IntegerArgumentType.getInteger(ctx, "count"), false)));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("deep")
|
||||
.executes(ctx -> apply(0, true))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, Integer>argument("count", IntegerArgumentType.integer(1))
|
||||
.executes(ctx -> apply(IntegerArgumentType.getInteger(ctx, "count"), true))));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("pause").executes(ctx -> {
|
||||
AutismNameScrape.pause();
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("resume").executes(ctx -> {
|
||||
AutismNameScrape.resume();
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("stop").executes(ctx -> {
|
||||
AutismNameScrape.stop();
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
|
||||
private static int apply(int count, boolean deep) {
|
||||
AutismNameScrape.start(count, deep);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
79
src/main/java/autismclient/commands/impl/NbtCommand.java
Normal file
79
src/main/java/autismclient/commands/impl/NbtCommand.java
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.gui.screen.AutismOverlayHostScreen;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismItemCommandSerializer;
|
||||
import autismclient.util.AutismItemNbtInspectOverlay;
|
||||
import autismclient.util.AutismNotifications;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class NbtCommand extends Command {
|
||||
public NbtCommand() {
|
||||
super("nbt", "Inspect or copy the held item's components (NBT).");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
inspect();
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("get").executes(ctx -> {
|
||||
inspect();
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("copy").executes(ctx -> {
|
||||
copy();
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
|
||||
private static ItemStack held() {
|
||||
net.minecraft.world.entity.player.Player player = autismclient.util.multi.MultiPilot.commandPlayer();
|
||||
if (player == null) return ItemStack.EMPTY;
|
||||
ItemStack main = player.getMainHandItem();
|
||||
return !main.isEmpty() ? main : player.getOffhandItem();
|
||||
}
|
||||
|
||||
private static void inspect() {
|
||||
ItemStack stack = held();
|
||||
if (stack.isEmpty()) {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7cHold an item in either hand first.");
|
||||
return;
|
||||
}
|
||||
if (!AutismItemNbtInspectOverlay.openGlobal(stack)) {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7cCould not open the NBT inspector.");
|
||||
return;
|
||||
}
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null) {
|
||||
autismclient.util.AutismItemNbtInspectOverlay overlay =
|
||||
autismclient.util.AutismItemNbtInspectOverlay.getSharedOverlay(mc.font);
|
||||
mc.execute(() -> {
|
||||
|
||||
if (mc.gui.screen() == null) {
|
||||
mc.gui.setScreen(new AutismOverlayHostScreen(overlay));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void copy() {
|
||||
ItemStack stack = held();
|
||||
if (stack.isEmpty()) {
|
||||
AutismClientMessaging.sendPrefixed("\u00a7cHold an item in either hand first.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Minecraft.getInstance().keyboardHandler.setClipboard(AutismItemCommandSerializer.giveCommand(stack));
|
||||
AutismNotifications.copied("Copied /give command.");
|
||||
} catch (Throwable t) {
|
||||
AutismNotifications.error("Copy failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/main/java/autismclient/commands/impl/OreSimCommand.java
Normal file
80
src/main/java/autismclient/commands/impl/OreSimCommand.java
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.modules.Module;
|
||||
import autismclient.modules.ModuleOreSim;
|
||||
import autismclient.modules.ModuleRegistry;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismWaypoints;
|
||||
import autismclient.util.oresim.AutismOreSimEngine;
|
||||
import autismclient.util.oresim.AutismOreSimSeedInput;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class OreSimCommand extends Command {
|
||||
|
||||
private static final String G = "§7";
|
||||
private static final String W = "§f";
|
||||
private static final String R = "§c";
|
||||
private static final String Y = "§e";
|
||||
private static final String A = "§a";
|
||||
|
||||
public OreSimCommand() {
|
||||
super("oresim", "Report why OreSim is or is not predicting.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
report();
|
||||
return SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
private static void report() {
|
||||
Module xray = ModuleRegistry.get("xray");
|
||||
if (xray == null) {
|
||||
AutismClientMessaging.sendPrefixed(R + "Xray module not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
AutismClientMessaging.sendPrefixed(Y + "--- OreSim ---");
|
||||
AutismClientMessaging.sendPrefixed(G + "enabled: " + W + xray.isEnabled()
|
||||
+ G + " mode: " + W + xray.value("mode")
|
||||
+ G + " style: " + W + xray.value("render-style"));
|
||||
|
||||
Long seed = ModuleOreSim.debugSeed(xray);
|
||||
AutismOreSimSeedInput.Status seedStatus = ModuleOreSim.seedInputStatus(xray);
|
||||
AutismClientMessaging.sendPrefixed(G + "seed: "
|
||||
+ (seedStatus == AutismOreSimSeedInput.Status.INVALID ? R + "INVALID"
|
||||
: seed == null ? R + "NONE - enter a signed 64-bit value" : W + seed)
|
||||
+ G + " from: " + W + "the World Seed text field");
|
||||
AutismClientMessaging.sendPrefixed(G + "saved scope: " + W
|
||||
+ AutismWaypoints.scopeKey(Minecraft.getInstance())
|
||||
+ G + "; the same world/server scoping as waypoints. No world or server seed is read.");
|
||||
|
||||
AutismClientMessaging.sendPrefixed(G + "ore list entries: " + W + ModuleOreSim.debugSelectionSize(xray)
|
||||
+ G + " families: " + W + Integer.bitCount(ModuleOreSim.debugEnabledMask(xray)));
|
||||
|
||||
String worldgen = AutismOreSimEngine.failed() ? R + "FAILED"
|
||||
: AutismOreSimEngine.loading() ? Y + AutismOreSimEngine.status().name().toLowerCase(java.util.Locale.ROOT)
|
||||
: AutismOreSimEngine.ready() ? A + "ready" : R + AutismOreSimEngine.status().name().toLowerCase(java.util.Locale.ROOT);
|
||||
AutismClientMessaging.sendPrefixed(G + "local Minecraft 26.2 worldgen: " + worldgen);
|
||||
if (AutismOreSimEngine.failed()
|
||||
|| AutismOreSimEngine.status() == AutismOreSimEngine.Status.UNVERIFIED_WORLDGEN) {
|
||||
AutismClientMessaging.sendPrefixed(R + AutismOreSimEngine.failureMessage());
|
||||
}
|
||||
|
||||
AutismClientMessaging.sendPrefixed(G + "chunks simulated: " + W + AutismOreSimEngine.chunkCount()
|
||||
+ G + " positions: " + W + AutismOreSimEngine.storedPositions());
|
||||
|
||||
AutismClientMessaging.sendPrefixed(G + "generation source: " + W
|
||||
+ "local vanilla registries + terrain + carvers + biome decoration"
|
||||
+ G + "; received server chunks are not inputs.");
|
||||
|
||||
AutismClientMessaging.sendPrefixed(G + "nearby real ore: " + W + ModuleOreSim.nearDiagnostics());
|
||||
AutismClientMessaging.sendPrefixed(G + "matched = real ore within 5 blocks; touchingAir = of those,"
|
||||
+ " exposed; drawn = also in line of sight.");
|
||||
}
|
||||
}
|
||||
14
src/main/java/autismclient/commands/impl/PluginsCommand.java
Normal file
14
src/main/java/autismclient/commands/impl/PluginsCommand.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
public class PluginsCommand extends Command {
|
||||
public PluginsCommand() { super("plugins", "Open the plugin scanner (alias of `server plugins`)."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> { ServerCommand.openPlugins(); return SUCCESS; });
|
||||
}
|
||||
}
|
||||
48
src/main/java/autismclient/commands/impl/PrefixCommand.java
Normal file
48
src/main/java/autismclient/commands/impl/PrefixCommand.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.CommandSuggest;
|
||||
import autismclient.modules.AutismModule;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismCompatManager;
|
||||
import autismclient.util.AutismNotifications;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
|
||||
public class PrefixCommand extends Command {
|
||||
public PrefixCommand() {
|
||||
super("prefix", "Change the AUTISM command prefix.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("Current prefix: " + AutismCompatManager.effectiveCommandPrefix());
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("reset").executes(ctx -> {
|
||||
String prefix = AutismCompatManager.environmentDefaultCommandPrefix();
|
||||
AutismModule.get().setCommandPrefix(prefix);
|
||||
AutismClientMessaging.sendPrefixed("Prefix reset to " + prefix);
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("new", StringArgumentType.word())
|
||||
.suggests(CommandSuggest::prefixes)
|
||||
.executes(ctx -> {
|
||||
String requested = StringArgumentType.getString(ctx, "new");
|
||||
if (!AutismCompatManager.COMMAND_PREFIX_CHOICES.contains(requested)) {
|
||||
AutismClientMessaging.sendPrefixed("Prefix must be one of: . % - _ * # @ & =");
|
||||
return SUCCESS;
|
||||
}
|
||||
String selected = AutismCompatManager.normalizeStoredCommandPrefix(requested);
|
||||
AutismModule.get().setCommandPrefix(selected);
|
||||
if (!selected.equals(requested)) {
|
||||
AutismNotifications.warning("Meteor uses '.'. Prefix kept as '%'.");
|
||||
}
|
||||
AutismClientMessaging.sendPrefixed("Prefix set to " + selected);
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismPlayerScanner;
|
||||
import autismclient.util.AutismRemoteView;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public final class RemoteViewCommand extends Command {
|
||||
public RemoteViewCommand() {
|
||||
super("rv", "Watch a loaded player's POV.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> openLookedAt());
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("stop")
|
||||
.executes(ctx -> {
|
||||
if (!AutismRemoteView.isActive()) {
|
||||
AutismClientMessaging.sendPrefixed("§7Remote view is not active.");
|
||||
} else {
|
||||
AutismRemoteView.stop(false);
|
||||
AutismClientMessaging.sendPrefixed("§aRemote view stopped.");
|
||||
}
|
||||
return SUCCESS;
|
||||
}));
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("player", StringArgumentType.greedyString())
|
||||
.suggests(RemoteViewCommand::suggestPlayers)
|
||||
.executes(ctx -> openNamed(StringArgumentType.getString(ctx, "player"))));
|
||||
}
|
||||
|
||||
private static int openLookedAt() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
Entity entity = mc == null ? null : mc.crosshairPickEntity;
|
||||
if (!(entity instanceof Player) && mc != null && mc.hitResult instanceof EntityHitResult hit) {
|
||||
entity = hit.getEntity();
|
||||
}
|
||||
if (!(entity instanceof Player player)) {
|
||||
AutismClientMessaging.sendPrefixed("§cLook at a loaded player first.");
|
||||
return SUCCESS;
|
||||
}
|
||||
report(AutismRemoteView.start(player));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static int openNamed(String requested) {
|
||||
Player player = findLoadedPlayer(Minecraft.getInstance(), requested);
|
||||
if (player == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cPlayer is not loaded: §f" + requested.trim());
|
||||
return SUCCESS;
|
||||
}
|
||||
report(AutismRemoteView.start(player));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static Player findLoadedPlayer(Minecraft mc, String requested) {
|
||||
if (mc == null || mc.level == null || requested == null) return null;
|
||||
String wanted = requested.trim();
|
||||
if (wanted.isEmpty()) return null;
|
||||
for (Player player : mc.level.players()) {
|
||||
if (player == null) continue;
|
||||
String profile = player.getGameProfile() == null ? "" : player.getGameProfile().name();
|
||||
String shown = player.getName().getString();
|
||||
if (wanted.equalsIgnoreCase(profile) || wanted.equalsIgnoreCase(shown)) return player;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void report(AutismRemoteView.Result result) {
|
||||
AutismClientMessaging.sendPrefixed((result.ok() ? "§a" : "§c") + result.message());
|
||||
}
|
||||
|
||||
private static CompletableFuture<Suggestions> suggestPlayers(CommandContext<AutismCommandSource> ignored,
|
||||
SuggestionsBuilder builder) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
for (AutismPlayerScanner.ScannedPlayer player : AutismPlayerScanner.scan(Minecraft.getInstance())) {
|
||||
if (player.name().toLowerCase(Locale.ROOT).startsWith(remaining)) builder.suggest(player.name());
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
}
|
||||
32
src/main/java/autismclient/commands/impl/SayCommand.java
Normal file
32
src/main/java/autismclient/commands/impl/SayCommand.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class SayCommand extends Command {
|
||||
public SayCommand() { super("say", "Send a chat message."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: " + AutismCommands.effectivePrefix() + "say <message>");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("message", StringArgumentType.greedyString())
|
||||
.executes(ctx -> {
|
||||
String msg = StringArgumentType.getString(ctx, "message");
|
||||
var conn = Minecraft.getInstance().getConnection();
|
||||
if (conn == null) { AutismClientMessaging.sendPrefixed("§cNot connected."); return SUCCESS; }
|
||||
if (msg.startsWith("/")) conn.sendCommand(msg.substring(1));
|
||||
|
||||
else AutismCommands.sendPlainChat(conn, msg);
|
||||
return SUCCESS;
|
||||
}));
|
||||
}
|
||||
}
|
||||
85
src/main/java/autismclient/commands/impl/SendCommand.java
Normal file
85
src/main/java/autismclient/commands/impl/SendCommand.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.commands.AutismCommands;
|
||||
import autismclient.commands.args.PacketClassArgumentType;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismPacketArgumentBuilder;
|
||||
import autismclient.util.AutismPacketRegistry;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
|
||||
public class SendCommand extends Command {
|
||||
public SendCommand() { super("send", "Send a raw C2S packet by class name with optional field=value arguments."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> {
|
||||
AutismClientMessaging.sendPrefixed("§eUsage: §f" + AutismCommands.effectivePrefix() + "send <packetName> [field=value ...]");
|
||||
AutismClientMessaging.sendPrefixed("§7Tip: press Tab after the space to search C2S packet classes.");
|
||||
AutismClientMessaging.sendPrefixed("§7Example: §f" + AutismCommands.effectivePrefix() + "send ServerboundSetCarriedItemPacket slot=0");
|
||||
return SUCCESS;
|
||||
});
|
||||
root.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("packet", PacketClassArgumentType.packetClass())
|
||||
.executes(ctx -> send(PacketClassArgumentType.get(ctx, "packet"), ""))
|
||||
.then(RequiredArgumentBuilder.<AutismCommandSource, String>argument("args", StringArgumentType.greedyString())
|
||||
.suggests((ctx, builder) -> {
|
||||
Class<? extends Packet<?>> cls = AutismPacketRegistry.getPacket(PacketClassArgumentType.get(ctx, "packet"));
|
||||
return AutismPacketArgumentBuilder.suggest(cls, builder);
|
||||
})
|
||||
.executes(ctx -> send(PacketClassArgumentType.get(ctx, "packet"), StringArgumentType.getString(ctx, "args")))));
|
||||
}
|
||||
|
||||
private int send(String name, String args) {
|
||||
Class<? extends Packet<?>> cls = AutismPacketRegistry.getPacket(name);
|
||||
if (cls == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cUnknown packet: §f" + name);
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!AutismPacketRegistry.getC2SPackets().contains(cls)) {
|
||||
AutismClientMessaging.sendPrefixed("§cRefusing to send non-C2S packet: §f" + name);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
AutismPacketArgumentBuilder.PreparedArgs prepared;
|
||||
try {
|
||||
prepared = AutismPacketArgumentBuilder.prepare(args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
AutismClientMessaging.sendPrefixed("§cBad arguments: " + e.getMessage());
|
||||
return SUCCESS;
|
||||
}
|
||||
AutismPacketArgumentBuilder.Result result = AutismPacketArgumentBuilder.build(cls, prepared.args());
|
||||
if (result.help()) {
|
||||
sendLines("§e", result.message());
|
||||
return SUCCESS;
|
||||
}
|
||||
if (!result.ok()) {
|
||||
sendLines("§c", "Failed to build §f" + name + "§c: " + result.message());
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (prepared.dryRun()) {
|
||||
AutismClientMessaging.sendPrefixed("§eBuilt §f" + name + "§7 (" + result.source() + ") §e- dry run, not sent.");
|
||||
return SUCCESS;
|
||||
}
|
||||
var conn = Minecraft.getInstance().getConnection();
|
||||
if (conn == null) {
|
||||
AutismClientMessaging.sendPrefixed("§cNo network connection.");
|
||||
return SUCCESS;
|
||||
}
|
||||
autismclient.util.AutismSharedState.get().sendPacketBypassDelay(conn, result.packet());
|
||||
AutismClientMessaging.sendPrefixed("§aSent §f" + name + "§7 (" + result.source() + ")");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
private static void sendLines(String color, String message) {
|
||||
if (message == null || message.isBlank()) return;
|
||||
for (String line : message.split("\\R")) {
|
||||
if (!line.isBlank()) AutismClientMessaging.sendPrefixed(color + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/main/java/autismclient/commands/impl/ServerCommand.java
Normal file
53
src/main/java/autismclient/commands/impl/ServerCommand.java
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package autismclient.commands.impl;
|
||||
|
||||
import autismclient.commands.Command;
|
||||
import autismclient.commands.AutismCommandSource;
|
||||
import autismclient.gui.screen.AutismOverlayHostScreen;
|
||||
import autismclient.modules.AutismModule;
|
||||
import autismclient.util.AutismClientMessaging;
|
||||
import autismclient.util.AutismOverlayManager;
|
||||
import autismclient.util.AutismServerInfoOverlay;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
public class ServerCommand extends Command {
|
||||
public ServerCommand() { super("server", "Open the server info panel, or the plugin scanner."); }
|
||||
|
||||
@Override
|
||||
public void build(LiteralArgumentBuilder<AutismCommandSource> root) {
|
||||
root.executes(ctx -> { openInfo(); return SUCCESS; });
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("info").executes(ctx -> { openInfo(); return SUCCESS; }));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("plugins").executes(ctx -> { openPlugins(); return SUCCESS; }));
|
||||
root.then(LiteralArgumentBuilder.<AutismCommandSource>literal("tps").executes(ctx -> { tps(); return SUCCESS; }));
|
||||
}
|
||||
|
||||
static void openInfo() {
|
||||
openOverlay(false);
|
||||
}
|
||||
|
||||
static void openPlugins() {
|
||||
openOverlay(true);
|
||||
}
|
||||
|
||||
private static void openOverlay(boolean pluginsTab) {
|
||||
AutismServerInfoOverlay overlay = AutismModule.get().getServerDataOverlay();
|
||||
if (overlay == null) { AutismClientMessaging.sendPrefixed("§cServer overlay unavailable."); return; }
|
||||
AutismOverlayManager.get().register(overlay);
|
||||
if (pluginsTab) overlay.openPluginsTab();
|
||||
else overlay.openInfoTab();
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc != null) {
|
||||
mc.execute(() -> {
|
||||
if (mc.gui.screen() == null) {
|
||||
mc.gui.setScreen(new AutismOverlayHostScreen(overlay));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void tps() {
|
||||
double tps = autismclient.util.macro.ServerTickTracker.getEstimatedTps();
|
||||
AutismClientMessaging.sendPrefixed(String.format("§eTPS: §f%.2f §7(estimated)", tps));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue