Toast Control

Toast Control

134M Downloads

[Crash 1.21.1] NullPointerException: null

pietro-lopes opened this issue · 3 comments

commented

A player reported this:

https://gnomebot.dev/paste/mclogs/ymX684C#L2175

No reports on how to reproduce or why it is happening

commented

That's... uh, weird, at best.

The faulting line is

A crash can only occur here if the visible field is null, but this field is defined (by vanilla) as

private final List<ToastComponent.ToastInstance<?>> visible = new ArrayList<>();

I suspect you have some overzealous optimization™ mod present that is trying to null it out for some kind of memory saving or cause it to be lazily initialized.

I don't think there's any change I can make from my end here though; as far as TC is concerned that is a final field which may never have a null value.

commented

What on hell, now I'm curious 🧐
I will mixin debug and see who is touching that.
Thanks for the guidance

commented
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package net.minecraft.client.gui.components.toasts;

import com.aizistral.nochatreports.common.config.NCRConfig;
import com.google.common.collect.Queues;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Deque;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.toasts.SystemToast.SystemToastId;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.client.ClientHooks;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
import ovh.corail.tombstone.event.ClientEventHandler;

@OnlyIn(Dist.CLIENT)
public class ToastComponent {
    public static final int SLOT_COUNT = 5;
    public static final int NO_SPACE = -1;
    public final Minecraft minecraft;
    public final List<ToastInstance<?>> visible = new ArrayList();
    public BitSet occupiedSlots = new BitSet(5);
    public Deque<Toast> queued = Queues.newArrayDeque();

    public ToastComponent(Minecraft p_94918_) {
        this.minecraft = p_94918_;
    }

    public void render(GuiGraphics p_283249_) {
        if (!this.minecraft.options.hideGui) {
            int i = p_283249_.guiWidth();
            this.visible.removeIf((p_280780_) -> {
                if (p_280780_ != null && p_280780_.render(i, p_283249_)) {
                    this.occupiedSlots.clear(p_280780_.index, p_280780_.index + p_280780_.slotCount);
                    return true;
                } else {
                    return false;
                }
            });
            if (!this.queued.isEmpty() && this.freeSlots() > 0) {
                this.queued.removeIf((p_243239_) -> {
                    int j = p_243239_.slotCount();
                    int k = this.findFreeIndex(j);
                    if (k != -1) {
                        this.visible.add(new ToastInstance(this, p_243239_, k, j));
                        this.occupiedSlots.set(k, k + j);
                        return true;
                    } else {
                        return false;
                    }
                });
            }
        }

        this.handler$bln000$tombstone$render(p_283249_, (CallbackInfo)null);
    }

    public int findFreeIndex(int p_243272_) {
        if (this.freeSlots() >= p_243272_) {
            int i = 0;

            for(int j = 0; j < 5; ++j) {
                if (this.occupiedSlots.get(j)) {
                    i = 0;
                } else {
                    ++i;
                    if (i == p_243272_) {
                        return j + 1 - i;
                    }
                }
            }
        }

        return -1;
    }

    public int freeSlots() {
        return 5 - this.occupiedSlots.cardinality();
    }

    @Nullable
    public <T extends Toast> T getToast(Class<? extends T> p_94927_, Object p_94928_) {
        for(ToastInstance<?> toastinstance : this.visible) {
            if (toastinstance != null && p_94927_.isAssignableFrom(toastinstance.getToast().getClass()) && toastinstance.getToast().getToken().equals(p_94928_)) {
                return (T)toastinstance.getToast();
            }
        }

        for(Toast toast : this.queued) {
            if (p_94927_.isAssignableFrom(toast.getClass()) && toast.getToken().equals(p_94928_)) {
                return (T)toast;
            }
        }

        return null;
    }

    public void clear() {
        this.occupiedSlots.clear();
        this.visible.clear();
        this.queued.clear();
    }

    public void addToast(Toast p_94923_) {
        CallbackInfo callbackInfo2 = new CallbackInfo("addToast", true);
        this.handler$doh000$nochatreports$onAddToast(p_94923_, callbackInfo2);
        if (!callbackInfo2.isCancelled()) {
            if (!ClientHooks.onToastAdd(p_94923_)) {
                this.queued.add(p_94923_);
            }
        }
    }

    public Minecraft getMinecraft() {
        return this.minecraft;
    }

    public double getNotificationDisplayTimeMultiplier() {
        return (Double)this.minecraft.options.notificationDisplayTime().get();
    }

    @MixinMerged(
        mixin = "ovh.corail.tombstone.mixin.ToastComponentMixin",
        priority = 1000,
        sessionId = "70b74f7b-ec65-4454-9795-481bc621e288"
    )
    private void handler$bln000$tombstone$render(GuiGraphics guiGraphics, CallbackInfo callbackInfo) {
        ClientEventHandler.renderScreenMessage();
    }

    @MixinMerged(
        mixin = "com.aizistral.nochatreports.common.mixins.client.MixinToastComponent",
        priority = 1000,
        sessionId = "70b74f7b-ec65-4454-9795-481bc621e288"
    )
    private void handler$doh000$nochatreports$onAddToast(Toast toast, CallbackInfo info) {
        if (NCRConfig.getClient().hideWarningToast() && toast instanceof SystemToast sys) {
            if (sys.getToken() == SystemToastId.UNSECURE_SERVER_WARNING) {
                info.cancel();
            }
        }

    }
}

This is what we have 🤔