简体   繁体   English

CraftBukkit 开发:为什么会抛出 NullPointerException?

[英]CraftBukkit development: Why does this throw NullPointerException?

I'm writing a simple Bukkit/Spigot plugin that replaces endermen inside of End City structures with shulkers.我正在编写一个简单的 Bukkit/Spigot 插件,用潜影贝代替末地城结构内的末影人。 Here's the code:这是代码:

package ru.cardboardbox.shulkerspawner;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.BlockVector;

import java.util.logging.Logger;

public final class main extends JavaPlugin implements Listener {
    World world_the_end = Bukkit.getWorld("world_the_end");
    private Logger log = Logger.getLogger("Minecraft");
    @Override
    public void onEnable() {
        log.info("[ShulkerSpawner] Good morning sunshine!");
        getServer().getPluginManager().registerEvents(this, this);
    }
    @EventHandler(ignoreCancelled = true)
    public void onSpawn(CreatureSpawnEvent e) {

        Location spawneeStandingOnBlockLocation = e.getLocation().add(new BlockVector(0,-1,0));
        Location spawneeRealLocation = e.getLocation();
        Block checkedBlock = spawneeStandingOnBlockLocation.getBlock();
        Material checkedBlockMaterial = checkedBlock.getType();
        Entity spawnedEntity = e.getEntity();
        log.info("[ShulkerSpawner] We're checking at " + spawneeRealLocation + ", the entity being an instance of " + e.getEntityType());
        log.info("[ShulkerSpawner] The entity is standing on the block " + spawneeStandingOnBlockLocation.toString() + ", which is " + checkedBlockMaterial.toString());

        if (e.getEntityType() == EntityType.ENDERMAN) {



            /////// NULL POINTER NEXT LINE
            Location endCityOrNull = world_the_end.locateNearestStructure(spawneeRealLocation, StructureType.END_CITY,3,true);



            if (endCityOrNull != null) {
                if (    checkedBlockMaterial == Material.PURPUR_BLOCK  ||
                        checkedBlockMaterial == Material.PURPUR_SLAB   ||
                        checkedBlockMaterial == Material.PURPUR_PILLAR ||
                        checkedBlockMaterial == Material.PURPUR_STAIRS )
                {
                    log.info("[ShulkerSpawner] Successfully(?) spawned a shulker at " + spawneeRealLocation.toString());
                    //It's cruft, but it's EXACTLY what I need. Don't ask why. I really need to replace endermen with shulkers.
                    world_the_end.spawnEntity(spawneeRealLocation,EntityType.SHULKER);
                    spawnedEntity.remove();
                } else {
                    log.info("[ShulkerSpawner] Entity is not on city blocks");
                }
            } else {
                log.info("[ShulkerSpawner] Entity is not near a city");
            }
        } else {
            log.info("[ShulkerSpawner] Entity is not an Enderman");
        }
    }


}

Problem: The marked line, against all odds, tends to throw up with NullPointerException.问题:标记线不顾一切地倾向于抛出 NullPointerException。 The method locateNearestStructure is nullable and returns null when a structure was no found.方法 locateNearestStructure 可以为空,并在未找到结构时返回 null。

The kicker: The NullPointerException is thrown upon assignment.踢球者:分配时抛出 NullPointerException。 It's not thrown for the world where the structure we're looking for doesn't spawn (so it would always return null).它不会针对我们正在寻找的结构没有产生的世界抛出(因此它总是会返回 null)。

The question: Why does this happen, and how can this be fixed?问题:为什么会发生这种情况,如何解决?

    [08:56:54] [Server thread/INFO]: [ShulkerSpawner] We're checking at Location{world=CraftWorld{name=world_the_end},x=1667.5,y=102.0,z=35.5,pitch=0.0,yaw=1.818178}, the entity being an instance of ENDERMAN
[08:56:54] [Server thread/INFO]: [ShulkerSpawner] The entity is standing on the block Location{world=CraftWorld{name=world_the_end},x=1667.5,y=101.0,z=35.5,pitch=0.0,yaw=1.818178}, which is PURPUR_BLOCK
[08:56:54] [Server thread/ERROR]: Could not pass event CreatureSpawnEvent to shulkerspawner v1.0-SNAPSHOT
java.lang.NullPointerException: null
    at ru.cardboardbox.shulkerspawner.main.onSpawn(main.java:39) ~[?:?]
    at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor7.execute(Unknown Source) ~[?:?]
    at org.bukkit.plugin.EventExecutor.lambda$create$1(EventExecutor.java:69) ~[patched_1.15.2.jar:git-Paper-129]
    at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:80) ~[patched_1.15.2.jar:git-Paper-129]
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[patched_1.15.2.jar:git-Paper-129]
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:559) ~[patched_1.15.2.jar:git-Paper-129]
    at org.bukkit.craftbukkit.v1_15_R1.event.CraftEventFactory.callCreatureSpawnEvent(CraftEventFactory.java:631) ~[patched_1.15.2.jar:git-Paper-129]
    at org.bukkit.craftbukkit.v1_15_R1.event.CraftEventFactory.doEntityAddEventCalling(CraftEventFactory.java:552) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.WorldServer.addEntity0(WorldServer.java:1214) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.WorldServer.addEntity(WorldServer.java:1121) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.SpawnerCreature.spawnMobs(SpawnerCreature.java:123) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.ChunkProviderServer.lambda$tickChunks$7(ChunkProviderServer.java:723) ~[patched_1.15.2.jar:git-Paper-129]
    at it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap$1.forEach(Long2ObjectLinkedOpenHashMap.java:1661) ~[patched_1.15.2.jar:git-Paper-129]
    at com.google.common.collect.Iterables$UnmodifiableIterable.forEach(Iterables.java:105) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.ChunkProviderServer.tickChunks(ChunkProviderServer.java:659) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.ChunkProviderServer.tick(ChunkProviderServer.java:602) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.WorldServer.doTick(WorldServer.java:406) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.MinecraftServer.b(MinecraftServer.java:1245) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.DedicatedServer.b(DedicatedServer.java:430) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.MinecraftServer.a(MinecraftServer.java:1112) ~[patched_1.15.2.jar:git-Paper-129]
    at net.minecraft.server.v1_15_R1.MinecraftServer.run(MinecraftServer.java:934) ~[patched_1.15.2.jar:git-Paper-129]
    at java.lang.Thread.run(Thread.java:835) [?:?]

To suggestions about world_the_end containing a null reference: This strangely covers all dimensions of the world, and works right in the overworld.关于包含空引用的world_the_end的建议:这奇怪地涵盖了世界的所有维度,并且在主世界中正常工作。 Only in The End it throws up like this.只有在《末日》中它才会像这样吐出来。 Folder for The End is called "world_the_end" Did it default to the default world, and am I completely misunderstanding how bukkit worlds work? The End 的文件夹名为“world_the_end” 它是否默认为默认世界,我是否完全误解了 bukkit 世界的工作方式?

You could try to get all worlds in the server and identify the end one as follows:您可以尝试在服务器中获取所有世界并确定最终世界,如下所示:

public World getTheEnd() {
    for(World w: Bukkit.getServer().getWorlds()) {
        if(w.getEnvironment().equals(World.Environment.THE_END)) {
            return w;
        }
    }
    return null;
}

In Spigot/Bukkit server World objects have a parameter of type World.Environment which can be:在 Spigot/Bukkit 服务器中, World对象有一个World.Environment类型的参数,它可以是:

  • World.Environment.NORMAL
  • World.Environment.NETHER
  • World.Environment.THE_END

Note that this solution will only work if your server has only 1 end world.请注意,此解决方案仅在您的服务器只有 1 个末地世界时才有效。 Otherwise the function getTheEnd() will return an (probably) unpredictable the end world.否则,函数getTheEnd()将返回(可能)不可预测的最终世界。

Please let me know if this solved your issue.请让我知道这是否解决了您的问题。

The answer is a combination of the other answer, and the comments, since world_the_end is null, you need a better way to get the end dimension.答案是另一个答案和评论的组合,因为 world_the_end 为空,您需要一种更好的方法来获取最终维度。

World world = spawnedEntity.getWorld();
if(!world.getEnvironment().equals(World.Environment.THE_END)){
    // Since the world is not the end stop there.
    return;
}

Now you can replace world_the_end with world, since we know it's the end, and at that point you also know it's an Enderman.现在您可以将 world_the_end 替换为 world,因为我们知道它是尽头,并且此时您也知道它是 Enderman。

You can not directly set the world_the_end object, because worlds are loaded after your plugin is being loaded.您不能直接设置world_the_end对象,因为世界是在加载插件之后加载的。 So there exists no world with the name world_the_end at this time.因此,此时不存在名为world_the_end的世界。

You can set the attribute in the onEnable() method, like this:您可以在onEnable()方法中设置属性,如下所示:

public final class main extends JavaPlugin implements Listener {   

    World world_the_end;

    private Logger log = Logger.getLogger("Minecraft");

    @Override
    public void onEnable() {
        world_the_end = Bukkit.getWorld("world_the_end");

        log.info("[ShulkerSpawner] Good morning sunshine!");
        getServer().getPluginManager().registerEvents(this, this);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM