Posted April 25, 20196 yr I made a capability for an extended reach which works great on singleplayer, but on servers/multiplayer it doesn't work at all as the capability returned using EntityPlayer#getCapability(Capability, EnumFacing) returns null. After using some basic reflection I found out this is probably because the Entity#capabilities field is null, even though this is not the case on singleplayer. CommonEventHandler.java (registered on both the client and the server): package com.ptsmods.morecommands.miscellaneous; import com.ptsmods.morecommands.miscellaneous.Reference.LogType; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; public class CommonEventHandler extends EventHandler { public CommonEventHandler() {} @SubscribeEvent public void attachCapabilities(AttachCapabilitiesEvent<Entity> event) { if (event.getObject() instanceof EntityPlayer) Reference.print(LogType.INFO, "Attaching capability to player"); if (!event.getCapabilities().containsKey(new ResourceLocation(Reference.MOD_ID, "reach")) && event.getObject() instanceof EntityPlayer) event.addCapability(new ResourceLocation(Reference.MOD_ID, "reach"), new ReachProvider()); } @SubscribeEvent public void onPlayerClone(PlayerEvent.Clone event) { Reference.print(LogType.INFO, "Changed world, cloning reach capability.", event.getOriginal().getCapability(ReachProvider.reachCap, null).get(), FMLCommonHandler.instance().getSide(), Reference.isSingleplayer()); event.getEntityPlayer().getCapability(ReachProvider.reachCap, null).set(FMLCommonHandler.instance().getSide() == Side.SERVER || Reference.isSingleplayer() ? (EntityPlayerMP) event.getEntityPlayer() : null, event.getOriginal().getCapability(ReachProvider.reachCap, null).get()); } } IReach.java: package com.ptsmods.morecommands.miscellaneous; import javax.annotation.Nullable; import net.minecraft.entity.player.EntityPlayerMP; public interface IReach { public float get(); public void set(float reach); public void set(@Nullable EntityPlayerMP player, float reach); } Reach.java: package com.ptsmods.morecommands.miscellaneous; import javax.annotation.Nullable; import com.ptsmods.morecommands.net.ServerReachUpdatePacket; import net.minecraft.entity.player.EntityPlayerMP; public class Reach implements IReach { private float reach = 5; public Reach() {} @Override public float get() { return reach; } @Override public void set(float reach) { set(null, reach); } @Override public void set(@Nullable EntityPlayerMP player, float reach) { this.reach = reach < 1 ? 1F : reach; if (player != null) Reference.netWrapper.sendTo(new ServerReachUpdatePacket(reach), player); } } ReachStorage.java: package com.ptsmods.morecommands.miscellaneous; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTPrimitive; import net.minecraft.nbt.NBTTagFloat; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability.IStorage; public class ReachStorage implements IStorage<IReach> { public ReachStorage() {} @Override public NBTBase writeNBT(Capability<IReach> capability, IReach instance, EnumFacing side) { return new NBTTagFloat(instance.get()); } @Override public void readNBT(Capability<IReach> capability, IReach instance, EnumFacing side, NBTBase nbt) { instance.set(null, ((NBTPrimitive) nbt).getFloat()); } } ReachProvider.java: package com.ptsmods.morecommands.miscellaneous; import net.minecraft.nbt.NBTBase; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.common.capabilities.ICapabilitySerializable; public class ReachProvider implements ICapabilitySerializable<NBTBase> { @CapabilityInject(IReach.class) public static final Capability<IReach> reachCap = null; private IReach instance = reachCap.getDefaultInstance(); public ReachProvider() {} @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { return capability == reachCap; } @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { return capability == reachCap ? reachCap.<T>cast(instance) : null; } @Override public NBTBase serializeNBT() { return reachCap.getStorage().writeNBT(reachCap, instance, null); } @Override public void deserializeNBT(NBTBase nbt) { reachCap.getStorage().readNBT(reachCap, instance, null, nbt); } } And the capability is registered on the client in the preInit method and on the server in the serverStart method using the following line: CapabilityManager.INSTANCE.register(IReach.class, new ReachStorage(), () -> new Reach()); Altough in the serverStart event it first checks to make sure it's not singleplayer. Am I doing something wrong is this a collection of some internal problems? (Most likely the former)
April 25, 20196 yr Author I have fixed it myself, it seems like having a common event handler is not such a bright idea. I have moved both methods in the CommonEventHandler class to the ServerEventHandler and the ClientEventHandler and it now works fine.
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.