Java (Minecraft Forge): Somehow getting NullPointerExceptions immediately after checking that the variable in question is not null

3 weeks ago 32
ARTICLE AD BOX

I'm trying to debug a minecraft mod called SimpleRadio, which has been crashing after running for a while on a server with many people on at a time. The following code snippets are from RadioManager.java, RadioManager.java, and Wire.java respectively in the latest version (Version 3.4.6 for Minecraft version 1.20.1) of the mod: https://github.com/CodinGlitch/SimpleRadio/tree/1.20.1

In all cases, we've experienced crashes in the vein of NullPointer exceptions complaining that a variable that was just checked to not be null immediately before is now somehow null.

I'm fairly certain that there's some multithreading nonsense going on with the mod; one of the crashes threw a ConcurrentModificationException for either sourceQueue or pendingSources (the line counts are off so i'm not sure which one exactly from the stack trace alone).

What I'm trying to say is that I'm fairly certain that multithreading without synchronized lists is the core cause of the errors I'm seeing here, but the nature of the error I'm seeing from it is so bonkers that I want to be more than sure before I try whatever fix on a server that's trying to host 15+ people that really just want to play the game without radio-related crashes.

(Note that I don't mean multithreading in terms of somehow multithreading the Minecraft server ticking, but that the networking thread which is accepting packets (?) and the main server thread are messing with one another.)

My point with all of this is, just, what?! How can this error occur when it is already so obviously made sure of that the condition for it to happen can't happen in the first place? This seems so impossible to me, even IF multithreading nonsense is occuring. Help!

(Below is the code followed with ALL of the error stack traces. Not gonna leave anything out here!)

(Example 1, in RadioManager.java)

@Override public BlockPos travelExtension(BlockPos pos, LevelAccessor level) { for (Direction direction : Direction.values()) { BlockPos offsetPos = pos.relative(direction); BlockEntity blockEntity = level.getBlockEntity(offsetPos); if (blockEntity instanceof InsulatorBlockEntity insulatorBlockEntity) { List<Wiring> wires = insulatorBlockEntity.getWires(); if (wires.isEmpty()) continue; Wiring wire = wires.get(0); Router router = wire.transport(insulatorBlockEntity.getRouter()); if (router == null) continue; BlockPos routerPos = router.getLocation().blockPos(); BlockState blockState = level.getBlockState(routerPos); if (!(blockState.getBlock() instanceof InsulatorBlock)) continue; Direction routerDirection = blockState.getValue(InsulatorBlock.FACING); return routerPos.relative(routerDirection.getOpposite()); } } return pos; } java.lang.NullPointerException: Cannot read field "location" because "router" is null at com.codinglitch.simpleradio.core.registry.blocks.InsulatorBlock.travelExtension(InsulatorBlock.java:74) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.api.central.Frequencing.getAntennaBase(Frequencing.java:63) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.api.central.Frequencing.calculateAntennaPower(Frequencing.java:46) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.core.registry.blocks.TransmitterBlockEntity.tick(TransmitterBlockEntity.java:102) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at net.minecraft.world.level.chunk.LevelChunk$BoundTickingBlockEntity.mixinextras$bridge$m_155252_$7(LevelChunk.java) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.tt20.json:world.LevelChunkMixin,pl:mixin:A} at net.minecraft.world.level.chunk.LevelChunk$BoundTickingBlockEntity.wrapOperation$zhe000$onTick(LevelChunk.java:796) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.tt20.json:world.LevelChunkMixin,pl:mixin:A} at net.minecraft.world.level.chunk.LevelChunk$BoundTickingBlockEntity.m_142224_(LevelChunk.java:689) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:mixin.tt20.json:world.LevelChunkMixin,pl:mixin:A} at net.minecraft.world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapper.m_142224_(LevelChunk.java:782) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:classloading} at net.minecraft.world.level.Level.redirect$zgd000$observable$redirectTick(Level.java:2040) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:botania_xplat.mixins.json:LevelAccessor,pl:mixin:APP:refurbished_furniture.common.mixins.json:LevelMixin,pl:mixin:APP:observable.common.json:LevelMixin,pl:mixin:APP:kubejs-common.mixins.json:LevelMixin,pl:mixin:APP:simpleradio.mixins.json:MixinLevel,pl:mixin:A} at net.minecraft.world.level.Level.m_46463_(Level.java:468) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:botania_xplat.mixins.json:LevelAccessor,pl:mixin:APP:refurbished_furniture.common.mixins.json:LevelMixin,pl:mixin:APP:observable.common.json:LevelMixin,pl:mixin:APP:kubejs-common.mixins.json:LevelMixin,pl:mixin:APP:simpleradio.mixins.json:MixinLevel,pl:mixin:A} at net.minecraft.server.level.ServerLevel.m_8793_(ServerLevel.java:351) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:botania_xplat.mixins.json:ServerLevelMixin,pl:mixin:APP:refurbished_furniture.common.mixins.json:ServerLevelMixin,pl:mixin:APP:observable.common.json:ServerLevelMixin,pl:mixin:APP:mixin.tt20.json:world.ServerLevelMixin,pl:mixin:APP:blueprint.mixins.json:ServerLevelMixin,pl:mixin:APP:moonlight-common.mixins.json:ServerLevelMixin,pl:mixin:APP:immersiveengineering.mixins.json:coremods.ServerWorldMixin,pl:mixin:APP:ksyxis.mixins.json:ServerLevelMixin,pl:mixin:APP:supplementaries-common.mixins.json:ServerLevelMixin,pl:mixin:APP:kubejs-common.mixins.json:ServerLevelMixin,pl:mixin:APP:simpleradio.mixins.json:MixinServerLevel,pl:mixin:APP:create.mixins.json:accessor.ServerLevelAccessor,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_5703_(MinecraftServer.java:893) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.dedicated.DedicatedServer.m_5703_(DedicatedServer.java:283) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:APP:blueprint.mixins.json:DedicatedServerMixin,pl:mixin:APP:mixins/common/nochatreports.mixins.json:server.MixinDedicatedServer,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_5705_(MinecraftServer.java:814) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:661) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_206580_(MinecraftServer.java:251) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at java.lang.Thread.run(Thread.java:833) ~[?:?] {re:mixin}

(Example 2, in RadioManager.java)

List<QueuedSource> acceptedSources = new ArrayList<>(); Iterator<QueuedSource> iterator = sourceQueue.iterator(); while (iterator.hasNext()) { QueuedSource source = iterator.next(); if (source == null) { iterator.remove(); continue; } source.time--; if (source.time <= 0) { acceptedSources.add(source); iterator.remove(); } } java.lang.NullPointerException: Cannot read field "time" because "source" is null at com.codinglitch.simpleradio.radio.RadioManager.serverTick(RadioManager.java:391) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:mixin,re:classloading} at net.minecraft.server.MinecraftServer.handler$bkb000$simpleradio$tickServer_radioTicking(MinecraftServer.java:5340) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m5705(MinecraftServer.java:836) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m130011(MinecraftServer.java:661) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m206580(MinecraftServer.java:251) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at java.lang.Thread.run(Thread.java:833) ~[?:?] {re:mixin}

(Example 3, in Wire.java)

if (SimpleRadioLibrary.SERVER_CONFIG.wire.transmissionTime != -1) { AtomicInteger timeUntilDemise = new AtomicInteger(); AtomicReference<Float> placeOfDemise = new AtomicReference<>((float) 0); if (RadioManager.getInstance().readQueue(queued -> { if (queued.source.getWireMedium().equals(this) && queued.router.equals(origin)) { int maxProgress = Math.round(SimpleRadioLibrary.SERVER_CONFIG.wire.transmissionTime * this.getLength()); float progress = (float) queued.time / maxProgress; timeUntilDemise.set((int) Math.ceil((float)queued.time / 2f)); if (isReversed) { placeOfDemise.set(1 - progress); } else { placeOfDemise.set(progress); } return true; } return false; })) { this.queueDemise(timeUntilDemise.get(), placeOfDemise.get()); } }

(Corresponding check for example 3, in RadioManager.java)

public boolean readQueue(Predicate<QueuedSource> filter) { for (QueuedSource source : sourceQueue) { if (source == null) continue; if (filter.test(source)) return true; } for (QueuedSource source : pendingSources) { if (source == null) continue; if (filter.test(source)) return true; } return false; } java.lang.NullPointerException: Cannot read field "source" because "queued" is null at com.codinglitch.simpleradio.core.registry.entities.Wire.lambda$relay$0(Wire.java:158) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.radio.RadioManager.readQueue(RadioManager.java:425) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:mixin,re:classloading} at com.codinglitch.simpleradio.core.registry.entities.Wire.relay(Wire.java:157) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.api.central.Socket.distribute(Socket.java:47) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.radio.RadioRouter.route(RadioRouter.java:231) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.radio.RadioRouter.route(RadioRouter.java:258) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.radio.RadioRouter.accept(RadioRouter.java:203) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:classloading} at com.codinglitch.simpleradio.radio.RadioManager.serverTick(RadioManager.java:400) ~[simpleradio-forge-1.20.1-3.4.6.jar%23401!/:3.4.6] {re:mixin,re:classloading} at net.minecraft.server.MinecraftServer.handler$bkb000$simpleradio$tickServer_radioTicking(MinecraftServer.java:5340) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_5705_(MinecraftServer.java:836) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:661) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at net.minecraft.server.MinecraftServer.m_206580_(MinecraftServer.java:251) ~[server-1.20.1-20230612.114412-srg.jar%23431!/:?] {re:mixin,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,pl:mixin:A} at java.lang.Thread.run(Thread.java:833) ~[?:?] {re:mixin}
Read Entire Article