001/*
002 * This file is part of Baritone.
003 *
004 * Baritone is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU Lesser General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * Baritone is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public License
015 * along with Baritone.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package baritone.api.utils;
019
020import baritone.api.BaritoneAPI;
021import baritone.api.Settings;
022import net.minecraft.ChatFormatting;
023import net.minecraft.client.GuiMessageTag;
024import net.minecraft.client.Minecraft;
025import net.minecraft.network.chat.Component;
026import net.minecraft.network.chat.MutableComponent;
027
028import java.util.Arrays;
029import java.util.Calendar;
030import java.util.stream.Stream;
031
032/**
033 * An ease-of-access interface to provide the {@link Minecraft} game instance,
034 * chat and console logging mechanisms, and the Baritone chat prefix.
035 *
036 * @author Brady
037 * @since 8/1/2018
038 */
039public interface Helper {
040
041    /**
042     * Instance of {@link Helper}. Used for static-context reference.
043     */
044    Helper HELPER = new Helper() {};
045
046    /**
047     * The main game instance returned by {@link Minecraft#getInstance()}.
048     * Deprecated since {@link IPlayerContext#minecraft()} should be used instead (In the majority of cases).
049     */
050    @Deprecated
051    Minecraft mc = Minecraft.getInstance();
052
053    /**
054     * The tag to assign to chat messages when {@link Settings#useMessageTag} is {@code true}.
055     */
056    GuiMessageTag MESSAGE_TAG = new GuiMessageTag(0xFF55FF, null, Component.literal("Baritone message."), "Baritone");
057
058    static Component getPrefix() {
059        // Inner text component
060        final Calendar now = Calendar.getInstance();
061        final boolean xd = now.get(Calendar.MONTH) == Calendar.APRIL && now.get(Calendar.DAY_OF_MONTH) <= 3;
062        MutableComponent baritone = Component.literal(xd ? "Baritoe" : BaritoneAPI.getSettings().shortBaritonePrefix.value ? "B" : "Baritone");
063        baritone.setStyle(baritone.getStyle().withColor(ChatFormatting.LIGHT_PURPLE));
064
065        // Outer brackets
066        MutableComponent prefix = Component.literal("");
067        prefix.setStyle(baritone.getStyle().withColor(ChatFormatting.DARK_PURPLE));
068        prefix.append("[");
069        prefix.append(baritone);
070        prefix.append("]");
071
072        return prefix;
073    }
074
075    /**
076     * Send a message to display as a toast popup
077     *
078     * @param title   The title to display in the popup
079     * @param message The message to display in the popup
080     */
081    default void logToast(Component title, Component message) {
082        Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message));
083    }
084
085    /**
086     * Send a message to display as a toast popup
087     *
088     * @param title   The title to display in the popup
089     * @param message The message to display in the popup
090     */
091    default void logToast(String title, String message) {
092        logToast(Component.literal(title), Component.literal(message));
093    }
094
095    /**
096     * Send a message to display as a toast popup
097     *
098     * @param message The message to display in the popup
099     */
100    default void logToast(String message) {
101        logToast(Helper.getPrefix(), Component.literal(message));
102    }
103
104    /**
105     * Send a message as a desktop notification
106     *
107     * @param message The message to display in the notification
108     */
109    default void logNotification(String message) {
110        logNotification(message, false);
111    }
112
113    /**
114     * Send a message as a desktop notification
115     *
116     * @param message The message to display in the notification
117     * @param error   Whether to log as an error
118     */
119    default void logNotification(String message, boolean error) {
120        if (BaritoneAPI.getSettings().desktopNotifications.value) {
121            logNotificationDirect(message, error);
122        }
123    }
124
125    /**
126     * Send a message as a desktop notification regardless of desktopNotifications
127     * (should only be used for critically important messages)
128     *
129     * @param message The message to display in the notification
130     */
131    default void logNotificationDirect(String message) {
132        logNotificationDirect(message, false);
133    }
134
135    /**
136     * Send a message as a desktop notification regardless of desktopNotifications
137     * (should only be used for critically important messages)
138     *
139     * @param message The message to display in the notification
140     * @param error   Whether to log as an error
141     */
142    default void logNotificationDirect(String message, boolean error) {
143        Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().notifier.value.accept(message, error));
144    }
145
146    /**
147     * Send a message to chat only if chatDebug is on
148     *
149     * @param message The message to display in chat
150     */
151    default void logDebug(String message) {
152        if (!BaritoneAPI.getSettings().chatDebug.value) {
153            //System.out.println("Suppressed debug message:");
154            //System.out.println(message);
155            return;
156        }
157        // We won't log debug chat into toasts
158        // Because only a madman would want that extreme spam -_-
159        logDirect(message, false);
160    }
161
162    /**
163     * Send components to chat with the [Baritone] prefix
164     *
165     * @param logAsToast Whether to log as a toast notification
166     * @param components The components to send
167     */
168    default void logDirect(boolean logAsToast, Component... components) {
169        MutableComponent component = Component.literal("");
170        if (!logAsToast && !BaritoneAPI.getSettings().useMessageTag.value) {
171            component.append(getPrefix());
172            component.append(Component.literal(" "));
173        }
174        Arrays.asList(components).forEach(component::append);
175        if (logAsToast) {
176            logToast(getPrefix(), component);
177        } else {
178            Minecraft.getInstance().execute(() -> BaritoneAPI.getSettings().logger.value.accept(component));
179        }
180    }
181
182    /**
183     * Send components to chat with the [Baritone] prefix
184     *
185     * @param components The components to send
186     */
187    default void logDirect(Component... components) {
188        logDirect(BaritoneAPI.getSettings().logAsToast.value, components);
189    }
190
191    /**
192     * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a
193     * direct response to a chat command)
194     *
195     * @param message    The message to display in chat
196     * @param color      The color to print that message in
197     * @param logAsToast Whether to log as a toast notification
198     */
199    default void logDirect(String message, ChatFormatting color, boolean logAsToast) {
200        Stream.of(message.split("\n")).forEach(line -> {
201            MutableComponent component = Component.literal(line.replace("\t", "    "));
202            component.setStyle(component.getStyle().withColor(color));
203            logDirect(logAsToast, component);
204        });
205    }
206
207    /**
208     * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a
209     * direct response to a chat command)
210     *
211     * @param message The message to display in chat
212     * @param color   The color to print that message in
213     */
214    default void logDirect(String message, ChatFormatting color) {
215        logDirect(message, color, BaritoneAPI.getSettings().logAsToast.value);
216    }
217
218    /**
219     * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a
220     * direct response to a chat command)
221     *
222     * @param message    The message to display in chat
223     * @param logAsToast Whether to log as a toast notification
224     */
225    default void logDirect(String message, boolean logAsToast) {
226        logDirect(message, ChatFormatting.GRAY, logAsToast);
227    }
228
229    /**
230     * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a
231     * direct response to a chat command)
232     *
233     * @param message The message to display in chat
234     */
235    default void logDirect(String message) {
236        logDirect(message, BaritoneAPI.getSettings().logAsToast.value);
237    }
238
239    default void logUnhandledException(final Throwable exception) {
240        HELPER.logDirect("An unhandled exception occurred. " +
241                        "The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues",
242                ChatFormatting.RED);
243        exception.printStackTrace();
244    }
245}