diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index f1d50925d..ed31e4160 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -17,8 +17,10 @@ package baritone.api; +import baritone.api.utils.NotificationHelper; import baritone.api.utils.SettingsUtil; import baritone.api.utils.TypeUtils; +import baritone.api.utils.gui.BaritoneToast; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; @@ -33,6 +35,7 @@ import java.lang.reflect.Type; import java.util.List; import java.util.*; import java.util.function.Consumer; +import java.util.function.BiConsumer; /** * Baritone's settings. Settings apply to all Baritone instances. @@ -1069,6 +1072,20 @@ public final class Settings { */ public final Setting> logger = new Setting<>(Minecraft.getMinecraft().ingameGUI.getChatGUI()::printChatMessage); + /** + * The function that is called when Baritone will send a desktop notification. This function can be added to + * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting + * {@link Setting#value}; + */ + public final Setting> notifier = new Setting<>(NotificationHelper::notify); + + /** + * The function that is called when Baritone will show a toast. This function can be added to + * via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting + * {@link Setting#value}; + */ + public final Setting> toaster = new Setting<>(BaritoneToast::addOrUpdate); + /** * The size of the box that is rendered when the current goal is a GoalYLevel */ diff --git a/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java b/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java index 54ae9e8c4..e438da308 100644 --- a/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java +++ b/src/api/java/baritone/api/command/helpers/TabCompleteHelper.java @@ -253,8 +253,8 @@ public class TabCompleteHelper { public TabCompleteHelper addSettings() { return append( BaritoneAPI.getSettings().allSettings.stream() + .filter(s -> !SettingsUtil.javaOnlySetting(s)) .map(Settings.Setting::getName) - .filter(s -> !s.equalsIgnoreCase("logger")) .sorted(String.CASE_INSENSITIVE_ORDER) ); } diff --git a/src/api/java/baritone/api/utils/Helper.java b/src/api/java/baritone/api/utils/Helper.java index f423c4f4c..9bed37383 100755 --- a/src/api/java/baritone/api/utils/Helper.java +++ b/src/api/java/baritone/api/utils/Helper.java @@ -18,7 +18,6 @@ package baritone.api.utils; import baritone.api.BaritoneAPI; -import baritone.api.utils.gui.BaritoneToast; import net.minecraft.client.Minecraft; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; @@ -71,7 +70,7 @@ public interface Helper { * @param message The message to display in the popup */ default void logToast(ITextComponent title, ITextComponent message) { - mc.addScheduledTask(() -> BaritoneToast.addOrUpdate(mc.getToastGui(), title, message, BaritoneAPI.getSettings().toastTimer.value)); + mc.addScheduledTask(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message)); } /** @@ -93,6 +92,48 @@ public interface Helper { logToast(Helper.getPrefix(), new TextComponentString(message)); } + /** + * Send a message as a desktop notification + * + * @param message The message to display in the notification + */ + default void logNotification(String message) { + logNotification(message, false); + } + + /** + * Send a message as a desktop notification + * + * @param message The message to display in the notification + * @param error Whether to log as an error + */ + default void logNotification(String message, boolean error) { + if (BaritoneAPI.getSettings().desktopNotifications.value) { + logNotificationDirect(message, error); + } + } + + /** + * Send a message as a desktop notification regardless of desktopNotifications + * (should only be used for critically important messages) + * + * @param message The message to display in the notification + */ + default void logNotificationDirect(String message) { + logNotificationDirect(message, false); + } + + /** + * Send a message as a desktop notification regardless of desktopNotifications + * (should only be used for critically important messages) + * + * @param message The message to display in the notification + * @param error Whether to log as an error + */ + default void logNotificationDirect(String message, boolean error) { + mc.addScheduledTask(() -> BaritoneAPI.getSettings().notifier.value.accept(message, error)); + } + /** * Send a message to chat only if chatDebug is on * diff --git a/src/main/java/baritone/utils/NotificationHelper.java b/src/api/java/baritone/api/utils/NotificationHelper.java similarity index 99% rename from src/main/java/baritone/utils/NotificationHelper.java rename to src/api/java/baritone/api/utils/NotificationHelper.java index 54abbf87f..f0ec336dd 100644 --- a/src/main/java/baritone/utils/NotificationHelper.java +++ b/src/api/java/baritone/api/utils/NotificationHelper.java @@ -15,7 +15,7 @@ * along with Baritone. If not, see . */ -package baritone.utils; +package baritone.api.utils; import org.apache.commons.lang3.SystemUtils; diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index 48c8ee4df..0e95965f9 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -50,6 +50,7 @@ public class SettingsUtil { private static final Path SETTINGS_PATH = getMinecraft().gameDir.toPath().resolve("baritone").resolve("settings.txt"); private static final Pattern SETTING_PATTERN = Pattern.compile("^(?[^ ]+) +(?.+)"); // key and value split by the first space + private static final String[] JAVA_ONLY_SETTINGS = {"logger", "notifier", "toaster"}; private static boolean isComment(String line) { return line.startsWith("#") || line.startsWith("//"); @@ -111,7 +112,7 @@ public class SettingsUtil { System.out.println("NULL SETTING?" + setting.getName()); continue; } - if (setting.getName().equals("logger")) { + if (javaOnlySetting(setting)) { continue; // NO } if (setting.value == setting.defaultValue) { @@ -165,13 +166,28 @@ public class SettingsUtil { } public static String settingToString(Settings.Setting setting) throws IllegalStateException { - if (setting.getName().equals("logger")) { - return "logger"; + if (javaOnlySetting(setting)) { + return setting.getName(); } return setting.getName() + " " + settingValueToString(setting); } + /** + * This should always be the same as whether the setting can be parsed from or serialized to a string + * + * @param the setting + * @return true if the setting can not be set or read by the user + */ + public static boolean javaOnlySetting(Settings.Setting setting) { + for (String name : JAVA_ONLY_SETTINGS) { // no JAVA_ONLY_SETTINGS.contains(...) because that would be case sensitive + if (setting.getName().equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { Settings.Setting setting = settings.byLowerName.get(settingName); if (setting == null) { diff --git a/src/api/java/baritone/api/utils/gui/BaritoneToast.java b/src/api/java/baritone/api/utils/gui/BaritoneToast.java index 82df67413..eb6361478 100644 --- a/src/api/java/baritone/api/utils/gui/BaritoneToast.java +++ b/src/api/java/baritone/api/utils/gui/BaritoneToast.java @@ -71,4 +71,8 @@ public class BaritoneToast implements IToast { baritonetoast.setDisplayedText(title, subtitle); } } + + public static void addOrUpdate(ITextComponent title, ITextComponent subtitle) { + addOrUpdate(net.minecraft.client.Minecraft.getMinecraft().getToastGui(), title, subtitle, baritone.api.BaritoneAPI.getSettings().toastTimer.value); + } } diff --git a/src/main/java/baritone/command/ExampleBaritoneControl.java b/src/main/java/baritone/command/ExampleBaritoneControl.java index 8d737941a..7050bf302 100644 --- a/src/main/java/baritone/command/ExampleBaritoneControl.java +++ b/src/main/java/baritone/command/ExampleBaritoneControl.java @@ -124,7 +124,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener } } else if (argc.hasExactlyOne()) { for (Settings.Setting setting : settings.allSettings) { - if (setting.getName().equals("logger")) { + if (SettingsUtil.javaOnlySetting(setting)) { continue; } if (setting.getName().equalsIgnoreCase(pair.getFirst())) { @@ -177,7 +177,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener .stream(); } Settings.Setting setting = settings.byLowerName.get(argc.getString().toLowerCase(Locale.US)); - if (setting != null) { + if (setting != null && !SettingsUtil.javaOnlySetting(setting)) { if (setting.getValueClass() == Boolean.class) { TabCompleteHelper helper = new TabCompleteHelper(); if ((Boolean) setting.value) { diff --git a/src/main/java/baritone/command/defaults/SetCommand.java b/src/main/java/baritone/command/defaults/SetCommand.java index 49064c5c3..e9e35d413 100644 --- a/src/main/java/baritone/command/defaults/SetCommand.java +++ b/src/main/java/baritone/command/defaults/SetCommand.java @@ -23,6 +23,7 @@ import baritone.api.Settings; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.exception.CommandException; +import baritone.api.command.exception.CommandInvalidStateException; import baritone.api.command.exception.CommandInvalidTypeException; import baritone.api.command.helpers.Paginator; import baritone.api.command.helpers.TabCompleteHelper; @@ -64,7 +65,7 @@ public class SetCommand extends Command { args.requireMax(1); List toPaginate = (viewModified ? SettingsUtil.modifiedSettings(Baritone.settings()) : Baritone.settings().allSettings).stream() - .filter(s -> !s.getName().equals("logger")) + .filter(s -> !javaOnlySetting(s)) .filter(s -> s.getName().toLowerCase(Locale.US).contains(search.toLowerCase(Locale.US))) .sorted((s1, s2) -> String.CASE_INSENSITIVE_ORDER.compare(s1.getName(), s2.getName())) .collect(Collectors.toList()); @@ -128,6 +129,12 @@ public class SetCommand extends Command { if (setting == null) { throw new CommandInvalidTypeException(args.consumed(), "a valid setting"); } + if (javaOnlySetting(setting)) { + // ideally it would act as if the setting didn't exist + // but users will see it in Settings.java or its javadoc + // so at some point we have to tell them or they will see it as a bug + throw new CommandInvalidStateException(String.format("Setting %s can only be used via the api.", setting.getName())); + } if (!doingSomething && !args.hasAny()) { logDirect(String.format("Value of setting %s:", setting.getName())); logDirect(settingValueToString(setting)); diff --git a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java index c54216568..d20b519dc 100644 --- a/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java +++ b/src/main/java/baritone/pathing/calc/AbstractNodeCostSearch.java @@ -25,7 +25,6 @@ import baritone.api.utils.BetterBlockPos; import baritone.api.utils.Helper; import baritone.api.utils.PathCalculationResult; import baritone.pathing.movement.CalculationContext; -import baritone.utils.NotificationHelper; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.Optional; @@ -217,9 +216,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder, Helper { if (logInfo) { logDebug("Even with a cost coefficient of " + COEFFICIENTS[COEFFICIENTS.length - 1] + ", I couldn't get more than " + Math.sqrt(bestDist) + " blocks"); logDebug("No path found =("); - if (Baritone.settings().desktopNotifications.value) { - NotificationHelper.notify("No path found =(", true); - } + logNotification("No path found =(", true); } return Optional.empty(); } diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index cf5c0193a..e08aa9450 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -40,7 +40,6 @@ import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; -import baritone.utils.NotificationHelper; import baritone.utils.PathingCommandContext; import baritone.utils.schematic.MapArtSchematic; import baritone.utils.schematic.SelectionSchematic; @@ -435,8 +434,8 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil numRepeats++; if (repeat.equals(new Vec3i(0, 0, 0)) || (max != -1 && numRepeats >= max)) { logDirect("Done building"); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnBuildFinished.value) { - NotificationHelper.notify("Done building", false); + if (Baritone.settings().notificationOnBuildFinished.value) { + logNotification("Done building", false); } onLostControl(); return null; diff --git a/src/main/java/baritone/process/CustomGoalProcess.java b/src/main/java/baritone/process/CustomGoalProcess.java index f925bec79..ea9ff2092 100644 --- a/src/main/java/baritone/process/CustomGoalProcess.java +++ b/src/main/java/baritone/process/CustomGoalProcess.java @@ -23,7 +23,6 @@ import baritone.api.process.ICustomGoalProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; import baritone.utils.BaritoneProcessHelper; -import baritone.utils.NotificationHelper; /** * As set by ExampleBaritoneControl or something idk @@ -94,8 +93,8 @@ public final class CustomGoalProcess extends BaritoneProcessHelper implements IC if (Baritone.settings().disconnectOnArrival.value) { ctx.world().sendQuittingDisconnectingPacket(); } - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnPathComplete.value) { - NotificationHelper.notify("Pathing complete", false); + if (Baritone.settings().notificationOnPathComplete.value) { + logNotification("Pathing complete", false); } return new PathingCommand(this.goal, PathingCommandType.CANCEL_AND_SET_GOAL); } diff --git a/src/main/java/baritone/process/ExploreProcess.java b/src/main/java/baritone/process/ExploreProcess.java index c42ece0e5..3664d4188 100644 --- a/src/main/java/baritone/process/ExploreProcess.java +++ b/src/main/java/baritone/process/ExploreProcess.java @@ -29,7 +29,6 @@ import baritone.api.process.PathingCommandType; import baritone.api.utils.MyChunkPos; import baritone.cache.CachedWorld; import baritone.utils.BaritoneProcessHelper; -import baritone.utils.NotificationHelper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; @@ -84,8 +83,8 @@ public final class ExploreProcess extends BaritoneProcessHelper implements IExpl public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (calcFailed) { logDirect("Failed"); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnExploreFinished.value) { - NotificationHelper.notify("Exploration failed", true); + if (Baritone.settings().notificationOnExploreFinished.value) { + logNotification("Exploration failed", true); } onLostControl(); return null; @@ -93,8 +92,8 @@ public final class ExploreProcess extends BaritoneProcessHelper implements IExpl IChunkFilter filter = calcFilter(); if (!Baritone.settings().disableCompletionCheck.value && filter.countRemain() == 0) { logDirect("Explored all chunks"); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnExploreFinished.value) { - NotificationHelper.notify("Explored all chunks", false); + if (Baritone.settings().notificationOnExploreFinished.value) { + logNotification("Explored all chunks", false); } onLostControl(); return null; diff --git a/src/main/java/baritone/process/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java index a8806d2ba..0ef85f07d 100644 --- a/src/main/java/baritone/process/FarmProcess.java +++ b/src/main/java/baritone/process/FarmProcess.java @@ -31,7 +31,6 @@ import baritone.api.utils.input.Input; import baritone.cache.WorldScanner; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; -import baritone.utils.NotificationHelper; import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; @@ -272,8 +271,8 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro if (calcFailed) { logDirect("Farm failed"); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnFarmFail.value) { - NotificationHelper.notify("Farm failed", true); + if (Baritone.settings().notificationOnFarmFail.value) { + logNotification("Farm failed", true); } onLostControl(); return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 8b0b5bac0..6d938e9d2 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -30,7 +30,6 @@ import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.MovementHelper; import baritone.utils.BaritoneProcessHelper; import baritone.utils.BlockStateInterface; -import baritone.utils.NotificationHelper; import net.minecraft.block.Block; import net.minecraft.block.BlockAir; import net.minecraft.block.BlockFalling; @@ -89,15 +88,15 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro if (calcFailed) { if (!knownOreLocations.isEmpty() && Baritone.settings().blacklistClosestOnFailure.value) { logDirect("Unable to find any path to " + filter + ", blacklisting presumably unreachable closest instance..."); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnMineFail.value) { - NotificationHelper.notify("Unable to find any path to " + filter + ", blacklisting presumably unreachable closest instance...", true); + if (Baritone.settings().notificationOnMineFail.value) { + logNotification("Unable to find any path to " + filter + ", blacklisting presumably unreachable closest instance...", true); } knownOreLocations.stream().min(Comparator.comparingDouble(ctx.player()::getDistanceSq)).ifPresent(blacklist::add); knownOreLocations.removeIf(blacklist::contains); } else { logDirect("Unable to find any path to " + filter + ", canceling mine"); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnMineFail.value) { - NotificationHelper.notify("Unable to find any path to " + filter + ", canceling mine", true); + if (Baritone.settings().notificationOnMineFail.value) { + logNotification("Unable to find any path to " + filter + ", canceling mine", true); } cancel(); return null; @@ -232,8 +231,8 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro locs.addAll(dropped); if (locs.isEmpty()) { logDirect("No locations for " + filter + " known, cancelling"); - if (Baritone.settings().desktopNotifications.value && Baritone.settings().notificationOnMineFail.value) { - NotificationHelper.notify("No locations for " + filter + " known, cancelling", true); + if (Baritone.settings().notificationOnMineFail.value) { + logNotification("No locations for " + filter + " known, cancelling", true); } cancel(); return;