eafooe Posted August 14, 2019 Posted August 14, 2019 (edited) Hi all, I have made a capability (see spoiler) that I attach to vanilla VillagerEntity instances during the AttachCapabilitiesEvent<Entity>: @SubscribeEvent public static void attachEntityCapabilities(AttachCapabilitiesEvent<Entity> event){ if (event.getObject() instanceof VillagerEntity){ event.addCapability(NoseProvider.IDENTIFIER, new NoseProvider()); } } Currently, the capability has one boolean, 'hasNose' which allows me to know whether the VillagerEntity instance has a nose, or whether it was removed by the player. Spoiler INose.java public interface INose { boolean hasNose(); void setHasNose(boolean hasNose); } Nose.java public class Nose implements INose { private boolean hasNose = true; @Override public boolean hasNose() { return hasNose; } @Override public void setHasNose(boolean hasNose) { this.hasNose = hasNose; } } NoseStorage.java public class NoseStorage implements Capability.IStorage<INose> { @Nullable @Override public INBT writeNBT(Capability<INose> capability, INose instance, Direction side) { VillagersNose.LOGGER.info("Writing HasNose: " + instance.hasNose() + " to NBT"); CompoundNBT nbt = new CompoundNBT(); nbt.putBoolean("HasNose", instance.hasNose()); return nbt; } @Override public void readNBT(Capability<INose> capability, INose instance, Direction side, INBT nbt) { CompoundNBT hi = (CompoundNBT) nbt; instance.setHasNose(hi.getBoolean("HasNose")); VillagersNose.LOGGER.info("Read HasNose: " + hi.getBoolean("HasNose") + " from NBT"); VillagersNose.LOGGER.info("HasNose is now equal to: " + instance.hasNose()); } } NoseProvider.java public class NoseProvider implements ICapabilitySerializable<INBT> { public static final ResourceLocation IDENTIFIER = new ResourceLocation(MODID, "capability_nose"); @CapabilityInject(INose.class) public static Capability<INose> NOSE_CAP = null; private INose instance = NOSE_CAP.getDefaultInstance(); @Override public INBT serializeNBT() { VillagersNose.LOGGER.info("Serializing NBT..."); return NOSE_CAP.getStorage().writeNBT(NOSE_CAP, instance, null); } @Override public void deserializeNBT(INBT nbt) { VillagersNose.LOGGER.info("Deserializing NBT..."); NOSE_CAP.getStorage().readNBT(NOSE_CAP, instance, null, nbt); } @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { return NOSE_CAP.orEmpty(cap, LazyOptional.of(() -> instance)); } } If 'hasNose' is set to false, the VillagerEntity instance should render without a nose. I believe I need to send a packet from the server and to the client with this information during the events specified in the following quote?: On 6/8/2019 at 8:12 AM, diesieben07 said: To keep entity data up to date on the client you must sync in the following places: PlayerEvent.StartTracking when PlayerEvent.StartTracking#getTarget is your entity (or has your capability in this case). Send the packet to PlayerEvent.StartTracking#getEntityPlayer. EntityJoinWorldEvent when EntityJoinWorldEvent#getEntity is your entity (or has your capability in this case). Send the packet to all players tracking the entity (EntityTracker#getTrackingPlayers or use PacketDistributor.TRACKING_ENTITY when using NetworkRegistry.newSimpleChannel). Whenever the data changes send the packet to all players tracking the entity (see above). I have a packet class set up, but I'm not sure what I should put in the handle method to make sure the client gets the data that it needs. public class ClientPacket { private int entityId; public ClientPacket(int entityId){ this.entityId = entityId; } static void encode(ClientPacket msg, PacketBuffer buffer){ buffer.writeInt(msg.entityId); VillagersNose.LOGGER.info("Wrote entityId: " + buffer.readInt()); } static ClientPacket decode(PacketBuffer buffer){ int entityId = buffer.readInt(); VillagersNose.LOGGER.info("Read entityId: " + entityId); return new ClientPacket(entityId); } // Send from server to client static void handle(ClientPacket msg, Supplier<NetworkEvent.Context> ctx){ ctx.get().enqueueWork(() -> { // ? // ? I've tried all sorts of things here but nothing seems to work // ? }); ctx.get().setPacketHandled(true); } } In addition to the handle method, I want to ensure that I am sending my packet correctly. Is the following code what I should be doing? // Send to all players tracking entity @SubscribeEvent public static void entityJoinWorldEvent(EntityJoinWorldEvent event){ Entity entity = event.getEntity(); if (entity instanceof VillagerEntity && !event.getWorld().isRemote){ PacketDistributor.PacketTarget dest = PacketDistributor.TRACKING_ENTITY.with(() -> entity); PacketHandler.INSTANCE.send(dest, new ClientPacket(entity.getEntityId())); } } I know this is kind of a lengthy question, so thank you for taking your time to help me out. Edited August 14, 2019 by eafooe Quote
Animefan8888 Posted August 15, 2019 Posted August 15, 2019 3 hours ago, eafooe said: static void encode(ClientPacket msg, PacketBuffer buffer){ You should also probably write into the packet buffer the boolean about the nose here. And then of course retrieve it where in decode. 3 hours ago, eafooe said: static void handle(ClientPacket msg, Supplier<NetworkEvent.Context> ctx){ Then here get the entity from the ID and then change the nose value. 3 hours ago, eafooe said: public static void entityJoinWorldEvent(EntityJoinWorldEvent event){ This is the wrong event. Use the PlayerEvent.StartTracking 1 Quote VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect. Forge and vanilla BlockState generator.
loordgek Posted August 15, 2019 Posted August 15, 2019 11 hours ago, eafooe said: return NOSE_CAP.orEmpty(cap, LazyOptional.of(() -> instance)); make a field holding the LazyOptional that makes it possible to cache the capability 1 Quote
eafooe Posted August 15, 2019 Author Posted August 15, 2019 6 hours ago, loordgek said: make a field holding the LazyOptional that makes it possible to cache the capability Like this? public class NoseProvider implements ICapabilitySerializable<INBT> { public static final ResourceLocation IDENTIFIER = new ResourceLocation(MODID, "capability_nose"); @CapabilityInject(INose.class) public static Capability<INose> NOSE_CAP = null; private INose instance = NOSE_CAP.getDefaultInstance(); private final LazyOptional<INose> holder = LazyOptional.of(() -> instance); @Override public INBT serializeNBT() { VillagersNose.LOGGER.info("Serializing NBT..."); return NOSE_CAP.getStorage().writeNBT(NOSE_CAP, instance, null); } @Override public void deserializeNBT(INBT nbt) { VillagersNose.LOGGER.info("Deserializing NBT..."); NOSE_CAP.getStorage().readNBT(NOSE_CAP, instance, null, nbt); } @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { return NOSE_CAP.orEmpty(cap, holder); } } 1 Quote
eafooe Posted August 15, 2019 Author Posted August 15, 2019 10 minutes ago, diesieben07 said: Don't blindly return your capability in getCapability. You must return an empty optional if the queried capability is not present in your provider. Is this what you mean? @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { if (cap == NOSE_CAP){ return holder.cast(); } return LazyOptional.empty(); } Quote
loordgek Posted August 15, 2019 Posted August 15, 2019 no the other way was fine @diesieben07 you really need to look at what "orEmpty" does, hint: it is in the name Quote
eafooe Posted August 15, 2019 Author Posted August 15, 2019 33 minutes ago, loordgek said: no the other way was fine @diesieben07 you really need to look at what "orEmpty" does, hint: it is in the name Aren't they equivalent? Quote
loordgek Posted August 15, 2019 Posted August 15, 2019 yes but '' orEmpty " is shorter return NOSE_CAP.orEmpty(cap, holder); vs if (cap == NOSE_CAP){ return holder.cast(); } return LazyOptional.empty(); 1 Quote
loordgek Posted August 16, 2019 Posted August 16, 2019 not really LazyOptional can be used for anything but it intended to be used by cabability 10 hours ago, diesieben07 said: Great naming then -_-. Why is it not OptionalCapability? ??????? https://github.com/MinecraftForge/MinecraftForge/commit/8e43dfa7a626387556e642f21aeb632e8e96cdba#diff-c3e85f4c50091d558045cf5dc0dac9b4 blame tterrag1098 Quote
eafooe Posted August 16, 2019 Author Posted August 16, 2019 Thanks so much everyone; I have it working! I'm proud of my funky, little guy 1 Quote
Recommended Posts
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.