All Downloads are FREE. Search and download functionalities are using the official Maven repository.

personthecat.catlib.event.error.LibErrorContext Maven / Gradle / Ivy

Go to download

Utilities for serialization, commands, noise generation, IO, and some new data types.

The newest version!
package personthecat.catlib.event.error;

import lombok.extern.log4j.Log4j2;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.class_124;
import net.minecraft.class_156;
import net.minecraft.class_1657;
import net.minecraft.class_2583;
import net.minecraft.class_2588;
import net.minecraft.class_310;
import net.minecraft.class_746;
import org.jetbrains.annotations.ApiStatus;
import personthecat.catlib.command.CommandUtils;
import personthecat.catlib.config.LibConfig;
import personthecat.catlib.data.ModDescriptor;
import personthecat.catlib.data.collections.MultiValueHashMap;
import personthecat.catlib.data.collections.MultiValueMap;
import personthecat.catlib.exception.FormattedException;
import personthecat.catlib.exception.GenericFormattedException;
import personthecat.catlib.exception.ModLoadException;
import personthecat.catlib.util.McUtils;
import personthecat.fresult.functions.ThrowingRunnable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

@Log4j2
public class LibErrorContext {

    private static final Map> COMMON_ERRORS = new ConcurrentHashMap<>();
    private static final Map> FATAL_ERRORS = new ConcurrentHashMap<>();
    private static final Set ERRED_MODS = ConcurrentHashMap.newKeySet();
    private static final AtomicLong LAST_BROADCAST = new AtomicLong(0L);
    private static final long BROADCAST_INTERVAL = 3000L;

    public static void warn(final ModDescriptor mod, final FormattedException e) {
        register(Severity.WARN, mod, e);
    }

    public static void error(final ModDescriptor mod, final FormattedException e) {
        register(Severity.ERROR, mod, e);
    }

    public static void fatal(final ModDescriptor mod, final FormattedException e) {
        register(Severity.FATAL, mod, e);
    }

    public static void register(final Severity level, final ModDescriptor mod, final FormattedException e) {
        e.onErrorReceived(level, mod, log);

        if (level == Severity.FATAL) {
            FATAL_ERRORS.computeIfAbsent(mod, m -> Collections.synchronizedList(new ArrayList<>())).add(e);
        } else if (level.isAtLeast(LibConfig.errorLevel())) {
            COMMON_ERRORS.computeIfAbsent(mod, m -> Collections.synchronizedList(new ArrayList<>())).add(e);
        } else {
            log.warn("Ignoring error at level: " + level, e);
            e.onErrorIgnored(level, mod, log);
            return;
        }
        ERRED_MODS.add(mod);

        if (McUtils.isClientSide()) {
            broadcastErrors();
        }
    }

    public static boolean run(final ModDescriptor mod, final ThrowingRunnable f) {
        try {
            f.run();
        } catch (final FormattedException e) {
            fatal(mod, e);
            return true;
        }
        return false;
    }

    public static  boolean apply(final ModDescriptor mod, final ThrowingRunnable f) {
        return apply(mod, f, GenericFormattedException::new);
    }

    @SuppressWarnings("unchecked")
    public static  boolean apply(
            final ModDescriptor mod, final ThrowingRunnable f, final Function e) {

        try {
            f.run();
        } catch (final Throwable t) {
            final FormattedException formatted;
            try {
                formatted = e.apply((E) t);
            } catch (final ClassCastException ignored) {
                throw new RuntimeException(t);
            }
            fatal(mod, formatted);
            return true;
        }
        return false;
    }

    public static boolean hasErrors() {
        return !COMMON_ERRORS.isEmpty() || !FATAL_ERRORS.isEmpty();
    }

    public static boolean isFatal() {
        return !FATAL_ERRORS.isEmpty();
    }

    public static int numMods() {
        return ERRED_MODS.size();
    }

    public static List getMods() {
        return new ArrayList<>(ERRED_MODS);
    }

    public static Collection get(final Class type) {
        final Set matching = new HashSet<>();
        COMMON_ERRORS.forEach((mod, errors) -> errors.forEach(error -> {
            if (type.isInstance(error)) matching.add(error);
        }));
        FATAL_ERRORS.forEach((mod, errors) -> errors.forEach(error -> {
            if (type.isInstance(error)) matching.add(error);
        }));
        return matching;
    }

    public static Collection get(final ModDescriptor m, final Class type) {
        final Set matching = new HashSet<>();
        final List common = COMMON_ERRORS.get(m);
        if (common != null) {
            common.forEach(error -> {
                if (type.isInstance(error)) matching.add(error);
            });
        }
        final List fatal = FATAL_ERRORS.get(m);
        if (fatal != null) {
            fatal.forEach(error -> {
                if (type.isInstance(error)) matching.add(error);
            });
        }
        return matching;
    }

    public static void clear(final Class type) {
        if (type == FormattedException.class || type == GenericFormattedException.class) {
            throw new UnsupportedOperationException("Operation affects too many mods (" + type.getSimpleName() + ")");
        }
        COMMON_ERRORS.forEach((mod, errors) -> errors.removeIf(type::isInstance));
        FATAL_ERRORS.forEach((mod, errors) -> errors.removeIf(type::isInstance));
    }

    public static void clear(final ModDescriptor m, final Class type) {
        COMMON_ERRORS.forEach((mod, errors) -> {
            if (mod.equals(m)) errors.removeIf(type::isInstance);
        });
        FATAL_ERRORS.forEach((mod, errors) -> {
            if (mod.equals(m)) errors.removeIf(type::isInstance);
        });
    }

    public static MultiValueMap getCommon() {
        final MultiValueMap common = new MultiValueHashMap<>();
        common.putAll(COMMON_ERRORS);
        return common;
    }

    public static MultiValueMap getFatal() {
        final MultiValueMap fatal = new MultiValueHashMap<>();
        fatal.putAll(FATAL_ERRORS);
        return fatal;
    }

    @ApiStatus.Internal
    public static void outputServerErrors(final boolean notify) {
        if (hasErrors()) {
            for (final Map.Entry> entry : COMMON_ERRORS.entrySet()) {
                log.error("Encountered {} warnings for {}", entry.getValue().size(), entry.getKey().getModId());
                entry.getValue().forEach(log::warn);
            }
            for (final Map.Entry> entry : FATAL_ERRORS.entrySet()) {
                log.error("Encountered {} errors for {}", entry.getValue().size(), entry.getKey().getModId());
                entry.getValue().forEach(log::fatal);
            }
            if (!FATAL_ERRORS.isEmpty()) {
                throw new ModLoadException(FATAL_ERRORS.size() + " mods encountered fatal errors");
            }
            dispose();
        } else if (notify) {
            log.info("No errors in context. Server init complete!");
        }
    }

    @Environment(EnvType.CLIENT)
    private static void broadcastErrors() {
        final class_746 player = class_310.method_1551().field_1724;
        if (player != null) {
            broadcastErrors(player);
        }
    }

    @ApiStatus.Internal
    @Environment(EnvType.CLIENT)
    public static void broadcastErrors(final class_1657 player) {
        final long currentUpdate = System.currentTimeMillis();
        final long lastUpdate = LAST_BROADCAST.get();
        if (currentUpdate - lastUpdate > BROADCAST_INTERVAL) {
            player.method_9203(new class_2588("catlib.errorText.clickHere")
                .method_27696(class_2583.field_24360.method_10977(class_124.field_1061)
                .method_10958(CommandUtils.clickToRun("/catlib errors"))),
                    class_156.field_25140);
            LAST_BROADCAST.set(currentUpdate);
        }
    }

    @ApiStatus.Internal
    public static void dispose() {
        COMMON_ERRORS.clear();
        FATAL_ERRORS.clear();
        ERRED_MODS.clear();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy