MineColonies

MineColonies

53M Downloads

[BUG] @Nullable on com.ldtteam.structurize.util.BlueprintPositionInfo#getBlockInfo not handled in various places in code

Akjosch opened this issue · 13 comments

commented

Is there an existing issue for this?

  • I have searched the existing issues

Are you using the latest MineColonies Version?

  • I am running the latest alpha version of MineColonies for my Minecraft version.

Did you check on the Wiki? or ask on Discord?

  • I checked the MineColonies Wiki and made sure my issue is not covered there. Or I was sent from discord to open an issue here.

What were you playing at the time? Were you able to reproduce it in both settings?

  • Single Player
  • Multi Player

Minecraft Version

1.20.1

MineColonies Version

1.20.1-1.1.483, version/main code branch

Structurize Version

1.20.1-1.0.697, 1.20.1-1.0.699 and 1.20.1-1.0.700

Related Mods and their Versions

Shouldn't matter

Current Behavior

At currently 21 places (in 20 lines) in code, Minecolonies uses info.getBlockInfo().getState(), with info being a com.ldtteam.structurize.util.BlueprintPositionInfo instance. The problem is that com.ldtteam.structurize.util.BlueprintPositionInfo#getBlockInfo can return null (and is correctly annotated with @Nullable), and so all those places are a potential NPE waiting to happen.

And in fact, it does happen for me, with the result in log looking typically something like this:

[21:09:33] [Server thread/WARN]: Statemachine for state BUILDING_STEP threw an exception:
java.lang.NullPointerException: Cannot invoke "com.ldtteam.structurize.util.BlockInfo.getState()" because the return value of "com.ldtteam.structurize.util.BlueprintPositionInfo.getBlockInfo()" is null
	at com.minecolonies.core.entity.ai.workers.AbstractEntityAIStructure.lambda$structureStep$6(AbstractEntityAIStructure.java:411) ~[minecolonies-1.20.1-1.1.483-BETA.jar%23268!/:1.20.1-1.1.483-BETA]
(... rest skipped, not relevant here...)

Relevant code points:

Expected Behavior

Check for null return values from @Nullable methods.

Reproduction Steps

Have a builder try to upgrade a building where they have to replace an air block with something else at some point (which affects almost all up gradable structures). Potentially start the upgrade before the commit in #9735 (which added a lot of those places), update the mod, then load the world again; but I didn't test that possibility yet.

Logs

Not relevant

Anything else?

  • Add a thumbs-up to the bug report if you are also affected. This helps the bug report become more visible to the team and doesn't clutter the comments.
  • Add a comment if you have any insights or background information that isn't already part of the conversation.
commented

In addition, com.ldtteam.structurize.util.BlockInfo#getState is also @Nullable, as seen here:

public record BlockInfo(BlockPos getPos, @Nullable BlockState getState, @Nullable CompoundTag getTileEntityData)

This should also be taken into account on a bunch of the lines above.

commented

It should never return null at all. The reason it is causing exceptions is that somewhere it is exceeding a bound.

commented

Well, it is and, more to the point, can return null according to Structurize's internal documentation. Is the issue then on Structurize's end, where the @Nullable annotation should be removed and the values guarded with Object.requireNonNull(...) on construction time? Because I can open an issue with the details there if required.

commented

In a few places we might be fine with it returning null. Where we assume it does not return null it should not return null. Indeed it will only return null if we try to query something outside of the blueprint.

commented

Like Ray said it shouldn't be null in the first place. Javadoc is sometimes dated.
Simply putting an if statement with a non null check doesn't solve the underlying issue, if you do that they will skip stuff instead of crash.
You rather want your building full of holes and an unexplainable issue?

commented

Javadoc is sometimes dated.

That's why I asked if I should open a Structurize issue instead. As a general rule, I prefer to not suggest API changes if I can help it, but you know your code better than I do obviously.

commented

In a few places we might be fine with it returning null.

There's not a single piece of code currently inside MineColonies where you call that method and not call getState() on the result immediately, without checking for null. So those "few places" are exactly "zero places".

commented

reports an issue occuring in his logs > "Logs not relevant" :D

commented

@someaddons The issue of Minecolonies not using the Structurize API properly is in the code, not the logs. The logs just show a symptom of it, and even then the stack trace isn't showing where the BlueprintPositionInfo with a null info field gets created, only where it gets accessed improperly.

I could recreated the stack trace (which has nothing to do with what's logged!) for it easily again if it's any help, but it's essentially https://github.com/ldtteam/Structurize/blob/2c33200215591bfafe1f79d144a0d0df0aee4bc0/src/main/java/com/ldtteam/structurize/blueprints/v1/Blueprint.java#L896 getting called with a pos argument (BlockPos[x=1,y=-1,z=0] in my case) which isn't a key in what getBlockInfoAsMap() returns. This in turn gets called in https://github.com/ldtteam/Structurize/blob/2c33200215591bfafe1f79d144a0d0df0aee4bc0/src/main/java/com/ldtteam/structurize/placement/AbstractBlueprintIterator.java#L116 - already with a non-existing positional parameter.

The NPE happens a few lines later; see attached stack trace. But that isn't the point of the issue. The point is that Structurize can and does return BlueprintPositionInfo instances with a null info field, and Minecolonies doesn't handle those cases gracefully.

stacktrace.txt

commented
commented

I think the "just let it fail" approach only really works when you fail at the earliest possible moment, not some time later like in this case.

Be it as it may, reverting Minecolonies to 474 and Structurize to 693, then firing and re-hiring the offending builders was at least a workaround for me here. They went right back to decorating the huts they were working on before. So all I have is to find out what the other problem the builder is having with upgrading a building. But that seems unrelated (no error log, just an endless loop), so is a topic for another issue, once I gathered enough information.

commented

As far as I know a reproduce is shutting down the world during a builder water clearing step. This messes up the progress pos state somehow outside of boundaries is my guess.

commented

As far as I know a reproduce is shutting down the world during a builder water clearing step. This messes up the progress pos state somehow outside of boundaries is my guess.

Just in case, apparently the AI issue can still happen, so either this issue or the original issue should probably be reopened:
#9793 (comment)