Posted January 16, 20205 yr I want to make an item that can shoot a fireball when left click, but I can't figure out how to make it. Here is the code. Please tell me in detail what to do. package arknights.item; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.*; import net.minecraft.potion.Effect; import net.minecraft.potion.EffectInstance; import net.minecraft.stats.Stats; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.SoundCategory; import net.minecraft.world.World; import org.apache.commons.lang3.tuple.Pair; import java.util.*; import static net.minecraft.potion.Effects.*; public class PureOriginiums extends Item { List<Pair<EffectInstance, Float>> effects = new ArrayList<>(); String playerName = null; public PureOriginiums(Properties p_i48476_1_) { super(p_i48476_1_); } @Override public UseAction getUseAction(ItemStack stack) { return UseAction.EAT; } @Override public int getUseDuration(ItemStack stack) { return 32; } //public static final Food PUREORIGINIUMS = (new Food.Builder()).hunger(10).saturation(0.3F).effect(new EffectInstance(Effects.SPEED, 400, 255), 1.0F).build(); @Override public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) { this.effects.clear(); this.playerName = playerIn.getScoreboardName(); ItemStack itemstack = playerIn.getHeldItem(handIn); playerIn.setActiveHand(handIn); return ActionResult.func_226249_b_(itemstack); } @Override public ItemStack onItemUseFinish(ItemStack stack, World worldIn, LivingEntity entityLiving) { //String playerName = entityLiving.getScoreboardName() int effectId = (int)(1+Math.random()*(10-1+1)); return onOriginniumsEaten(worldIn, stack, (PlayerEntity) entityLiving); } public ItemStack onOriginniumsEaten(World p_213357_1_, ItemStack p_213357_2_, PlayerEntity playerEntity) { playerEntity.getFoodStats().addStats(20,20); playerEntity.addStat(Stats.ITEM_USED.get(p_213357_2_.getItem())); p_213357_1_.playSound(null, playerEntity.func_226277_ct_(), playerEntity.func_226278_cu_(), playerEntity.func_226281_cx_(), playerEntity.getEatSound(p_213357_2_), SoundCategory.NEUTRAL, 1.0F, 1.0F + (p_213357_1_.rand.nextFloat() - p_213357_1_.rand.nextFloat()) * 0.4F); this.effectsList(); applyFoodEffects(p_213357_2_, p_213357_1_, playerEntity); //playerEntity.setHealth(20.0F); //playerEntity.addPotionEffect(new EffectInstance(Effects.SPEED, 600, 10)); if ( !(playerEntity).abilities.isCreativeMode) { p_213357_2_.shrink(1); } return p_213357_2_; } private void applyFoodEffects(ItemStack p_213349_1_, World p_213349_2_, LivingEntity p_213349_3_) { for(Pair<EffectInstance, Float> pair : this.effects) { if (!p_213349_2_.isRemote && pair.getLeft() != null && p_213349_2_.rand.nextFloat() < pair.getRight()) { p_213349_3_.addPotionEffect(new EffectInstance(pair.getLeft())); } } } void effectsList(){ if( /*this.playerName == "Dev" || */this.playerName.regionMatches(0, "Dr", 0, 2)){ addEffectToList(SPEED, 600, 10); addEffectToList(HASTE, 600, 255); addEffectToList(STRENGTH, 200, 10); addEffectToList(JUMP_BOOST, 600, 2); addEffectToList(RESISTANCE, 600, 3); addEffectToList(FIRE_RESISTANCE, 600, 2); addEffectToList(WATER_BREATHING, 600, 1); addEffectToList(NIGHT_VISION, 600, 1); addEffectToList(DOLPHINS_GRACE, 600, 1); } else { int random1,random2,random3; random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(SPEED, random2, random3); break; case 2: addEffectToList(SLOWNESS, random2, random3); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(HASTE, random2, random3); break; case 2: addEffectToList(MINING_FATIGUE, random2, random3); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*2); switch(random1){ case 1: addEffectToList(STRENGTH, random2, random3); break; case 2: addEffectToList(WEAKNESS, random2, random3); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*5); switch(random1){ case 1: addEffectToList(JUMP_BOOST, random2, random3); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(NAUSEA, random2, 2); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*3); switch(random1){ case 1: addEffectToList(RESISTANCE, random2, random3); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(FIRE_RESISTANCE, random2, 1); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(WATER_BREATHING, random2, 1); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(BLINDNESS, random2, 1); break; case 2: addEffectToList(NIGHT_VISION, random2, 1); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(SLOW_FALLING, random2, 1); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(DOLPHINS_GRACE, random2, 1); break; default: break; } addEffectToList(WITHER, 800, 2); } } void addEffectToList(Effect effect, int time, int level){ this.effects.add(Pair.of(new EffectInstance(effect, time, level), 1.0F)); } } Simple introduction to this item: It can be eaten when right click just like common food, but when you left click, it will become a wand which can shoot fire ball. If your player name begins with "Dr" you will get good effects, or you will get some random effects and you may probably die because of wither. Thanks for your help : ) Edited January 17, 20205 yr by NGYF
January 17, 20205 yr Author 22 hours ago, diesieben07 said: You can use PlayerInteractEvent.LeftClickEmpty. Thanks for your help, but may I ask you to tell me how to use it in detail? Actually I have read some posts about it in the forum and try to make an event handler. Should I use PlayerInteractEvent.LeftClickEmpty in this way? public class LeftClickEventHandler { @SubscribeEvent public ActionResult<ItemStack> leftClick(LeftClickEmpty event) { //spawn a fireball } } It is said that I should use pakets to notify the server. What should I do? I have read the forge documents, but I don't know what to do and I can't find any example in the forum. So could you please give me an example about it?
January 18, 20205 yr Author 22 hours ago, diesieben07 said: Yes, the empty left click is not automatically sent to the server, so you can only receive that event on the client. To make it spawn entities, you need to send a packet to the server and then spawn the entity there. Thank you. But I'm still a bit confused about handling packets. There are two classes called MyMessage and EntityPlayerMP. I think I should replace EntityPlayerMP with my leftclick class.But if it is right, what does MyMessage refer to? I will appreciate it if you can tell me how to handle packets properly.?
January 20, 20205 yr Author On 1/18/2020 at 10:10 PM, diesieben07 said: Did you even read the linked documentation? Yes, but maybe I miss something... I'm sorry. Thanks for your patience, I will try it myself.
January 21, 20205 yr Author On 1/17/2020 at 6:24 PM, diesieben07 said: Yes, the empty left click is not automatically sent to the server, so you can only receive that event on the client. To make it spawn entities, you need to send a packet to the server and then spawn the entity there. Sorry to bother you again. I try to send packets to server when I left click the item, but it throws an error. Here is the error: Spoiler Description: Unexpected error java.lang.ExceptionInInitializerError: null at arknights.item.BaseItem.emptyLeftClick(BaseItem.java:21) ~[classes/:?] {re:classloading} at net.minecraftforge.eventbus.EventBus.doCastFilter(EventBus.java:212) ~[eventbus-2.0.0-milestone.1-service.jar:?] {} at net.minecraftforge.eventbus.EventBus.lambda$addListener$11(EventBus.java:204) ~[eventbus-2.0.0-milestone.1-service.jar:?] {} at net.minecraftforge.eventbus.EventBus.post(EventBus.java:258) ~[eventbus-2.0.0-milestone.1-service.jar:?] {} at net.minecraftforge.common.ForgeHooks.onEmptyLeftClick(ForgeHooks.java:795) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading} at net.minecraft.client.Minecraft.clickMouse(Minecraft.java:1181) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.client.Minecraft.processKeyBinds(Minecraft.java:1476) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.client.Minecraft.runTick(Minecraft.java:1313) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:862) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.client.Minecraft.run(Minecraft.java:520) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading,pl:accesstransformer:B,pl:runtimedistcleaner:A} at net.minecraft.client.main.Main.main(SourceFile:202) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {re:classloading,pl:runtimedistcleaner:A} at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_231] {} at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_231] {} at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_231] {} at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_231] {} at net.minecraftforge.userdev.FMLUserdevClientLaunchProvider.lambda$launchService$0(FMLUserdevClientLaunchProvider.java:55) ~[forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {} at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:37) [modlauncher-5.0.0-milestone.4.jar:?] {} at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:54) [modlauncher-5.0.0-milestone.4.jar:?] {} at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:72) [modlauncher-5.0.0-milestone.4.jar:?] {} at cpw.mods.modlauncher.Launcher.run(Launcher.java:81) [modlauncher-5.0.0-milestone.4.jar:?] {} at cpw.mods.modlauncher.Launcher.main(Launcher.java:65) [modlauncher-5.0.0-milestone.4.jar:?] {} at net.minecraftforge.userdev.LaunchTesting.main(LaunchTesting.java:101) [forge-1.15.1-30.0.30_mapped_snapshot_20190719-1.14.3.jar:?] {} Caused by: java.lang.IllegalArgumentException: Registration of network channels is locked at net.minecraftforge.fml.network.NetworkRegistry.createInstance(NetworkRegistry.java:130) ~[?:?] {re:classloading} at net.minecraftforge.fml.network.NetworkRegistry.access$000(NetworkRegistry.java:49) ~[?:?] {re:classloading} at net.minecraftforge.fml.network.NetworkRegistry$ChannelBuilder.createNetworkInstance(NetworkRegistry.java:400) ~[?:?] {re:classloading} at net.minecraftforge.fml.network.NetworkRegistry$ChannelBuilder.simpleChannel(NetworkRegistry.java:409) ~[?:?] {re:classloading} at arknights.network.PacketHandler.<clinit>(PacketHandler.java:28) ~[?:?] {re:classloading} ... 22 more Here is my code: Spoiler BaseItem.java (I made this class so that I can make some wands or something else may use leftclick easily.) package arknights.item; import arknights.network.PacketHandler; import arknights.network.packets.LeftClickPKT; import net.minecraft.entity.LivingEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerInteractEvent; public class BaseItem extends Item { public BaseItem(Properties p_i48487_1_) { super(p_i48487_1_); MinecraftForge.EVENT_BUS.addListener(this::emptyLeftClick); } public void emptyLeftClick(PlayerInteractEvent.LeftClickEmpty event){ PacketHandler.sendToServer(new LeftClickPKT()); } public void leftClick(LivingEntity livingEntity, World worldIn, ItemStack stack){ } } PureOriginiums.java (The only change is method "leftclick", I spawn an arrow for test.) package arknights.item; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.AbstractArrowEntity; import net.minecraft.entity.projectile.ArrowEntity; import net.minecraft.item.*; import net.minecraft.potion.Effect; import net.minecraft.potion.EffectInstance; import net.minecraft.stats.Stats; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.SoundCategory; import net.minecraft.world.World; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import org.apache.commons.lang3.tuple.Pair; import java.util.*; import static net.minecraft.potion.Effects.*; public class PureOriginiums extends BaseItem { List<Pair<EffectInstance, Float>> effects = new ArrayList<>(); String playerName = null; public PureOriginiums(Properties p_i48476_1_) { super(p_i48476_1_); } @Override public void leftClick(LivingEntity livingEntity, World worldIn, ItemStack stack){ AbstractArrowEntity abstractarrowentity = new ArrowEntity(worldIn, livingEntity); abstractarrowentity.setNoGravity(true); abstractarrowentity.shoot(livingEntity, livingEntity.rotationPitch, livingEntity.rotationYaw, 0.0F, 0.1F * 3.0F, 1.0F); } @Override public UseAction getUseAction(ItemStack stack) { return UseAction.EAT; } @Override public int getUseDuration(ItemStack stack) { return 32; } //public static final Food PUREORIGINIUMS = (new Food.Builder()).hunger(10).saturation(0.3F).effect(new EffectInstance(Effects.SPEED, 400, 255), 1.0F).build(); @Override public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) { this.effects.clear(); this.playerName = playerIn.getScoreboardName(); ItemStack itemstack = playerIn.getHeldItem(handIn); playerIn.setActiveHand(handIn); return ActionResult.func_226249_b_(itemstack); } @Override public ItemStack onItemUseFinish(ItemStack stack, World worldIn, LivingEntity entityLiving) { //String playerName = entityLiving.getScoreboardName() int effectId = (int)(1+Math.random()*(10-1+1)); return onOriginniumsEaten(worldIn, stack, (PlayerEntity) entityLiving); } public ItemStack onOriginniumsEaten(World p_213357_1_, ItemStack p_213357_2_, PlayerEntity playerEntity) { playerEntity.getFoodStats().addStats(20,20); playerEntity.addStat(Stats.ITEM_USED.get(p_213357_2_.getItem())); p_213357_1_.playSound(null, playerEntity.func_226277_ct_(), playerEntity.func_226278_cu_(), playerEntity.func_226281_cx_(), playerEntity.getEatSound(p_213357_2_), SoundCategory.NEUTRAL, 1.0F, 1.0F + (p_213357_1_.rand.nextFloat() - p_213357_1_.rand.nextFloat()) * 0.4F); this.effectsList(); applyFoodEffects(p_213357_2_, p_213357_1_, playerEntity); //playerEntity.setHealth(20.0F); //playerEntity.addPotionEffect(new EffectInstance(Effects.SPEED, 600, 10)); if ( !(playerEntity).abilities.isCreativeMode) { p_213357_2_.shrink(1); } return p_213357_2_; } private void applyFoodEffects(ItemStack p_213349_1_, World p_213349_2_, LivingEntity p_213349_3_) { for(Pair<EffectInstance, Float> pair : this.effects) { if (!p_213349_2_.isRemote && pair.getLeft() != null && p_213349_2_.rand.nextFloat() < pair.getRight()) { p_213349_3_.addPotionEffect(new EffectInstance(pair.getLeft())); } } } void effectsList(){ if( /*this.playerName == "Dev" || */this.playerName.regionMatches(0, "Dr", 0, 2)){ addEffectToList(SPEED, 600, 10); addEffectToList(HASTE, 600, 255); addEffectToList(STRENGTH, 200, 10); addEffectToList(JUMP_BOOST, 600, 2); addEffectToList(RESISTANCE, 600, 3); addEffectToList(FIRE_RESISTANCE, 600, 2); addEffectToList(WATER_BREATHING, 600, 1); addEffectToList(NIGHT_VISION, 600, 1); addEffectToList(DOLPHINS_GRACE, 600, 1); } else { int random1,random2,random3; random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(SPEED, random2, random3); break; case 2: addEffectToList(SLOWNESS, random2, random3); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(HASTE, random2, random3); break; case 2: addEffectToList(MINING_FATIGUE, random2, random3); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*2); switch(random1){ case 1: addEffectToList(STRENGTH, random2, random3); break; case 2: addEffectToList(WEAKNESS, random2, random3); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*5); switch(random1){ case 1: addEffectToList(JUMP_BOOST, random2, random3); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(NAUSEA, random2, 2); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); random3 = (int)(1+Math.random()*3); switch(random1){ case 1: addEffectToList(RESISTANCE, random2, random3); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(FIRE_RESISTANCE, random2, 1); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(WATER_BREATHING, random2, 1); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(BLINDNESS, random2, 1); break; case 2: addEffectToList(NIGHT_VISION, random2, 1); break;default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(SLOW_FALLING, random2, 1); break; default: break; } random1 = (int)(1+Math.random()*2); random2 = (int)(100+Math.random()*1000); //random3 = (int)(1+Math.random()*10); switch(random1){ case 1: addEffectToList(DOLPHINS_GRACE, random2, 1); break; default: break; } addEffectToList(WITHER, 800, 2); } } void addEffectToList(Effect effect, int time, int level){ this.effects.add(Pair.of(new EffectInstance(effect, time, level), 1.0F)); } } LeftClickPacket.java package arknights.network.packets; import arknights.item.BaseItem; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; import java.util.function.Supplier; public class LeftClickPacket { public static void encode(LeftClickPacket msg, PacketBuffer buf){ } public static LeftClickPacket decode(PacketBuffer buf){ return new LeftClickPacket(); } public static class Handler { public static void handle(LeftClickPacket msg, Supplier<NetworkEvent.Context> ctx){ ctx.get().enqueueWork(() -> { PlayerEntity player = ctx.get().getSender(); ItemStack item = player.getHeldItemMainhand(); if(!item.isEmpty() && (item.getItem() instanceof BaseItem)){ ((BaseItem) item.getItem()).leftClick(player, player.getEntityWorld(), item); } }); ctx.get().setPacketHandled(true); } } } PacketHandler.java package arknights.network; import arknights.Arknights; import arknights.network.packets.LeftClickPacket; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.fml.network.NetworkDirection; import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.simple.SimpleChannel; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; public final class PacketHandler { private static final String PROTOCOL_VERSION = Integer.toString(1); private static final SimpleChannel HANDLER = NetworkRegistry.ChannelBuilder .named(new ResourceLocation(Arknights.MODID, "main_channel")) .clientAcceptedVersions(PROTOCOL_VERSION::equals) .serverAcceptedVersions(PROTOCOL_VERSION::equals) .networkProtocolVersion(() -> PROTOCOL_VERSION) .simpleChannel(); private static int index; public static void register(){ registerMessage(LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, LeftClickPacket.Handler::handle); } private static <MSG> void registerMessage(Class<MSG> type, BiConsumer<MSG, PacketBuffer> encoder, Function<PacketBuffer, MSG> decoder, BiConsumer<MSG, Supplier<NetworkEvent.Context>> consumer){ HANDLER.registerMessage(index++, type, encoder, decoder, consumer); } public static void sendToServer(Object msg){ HANDLER.sendToServer(msg); } public static void sendTo(Object msg, ServerPlayerEntity player){ if(!(player instanceof FakePlayer)){ HANDLER.sendTo(msg, player.connection.netManager, NetworkDirection.PLAY_TO_CLIENT); } } } Please tell me how to fix it. Thanks.
January 22, 20205 yr Author 7 hours ago, diesieben07 said: Network channels must be registered during FMLCommonSetupEvent. Not at some random time during some random static initializer. Thank you! Maybe I will complete it today!
January 22, 20205 yr Author 7 hours ago, diesieben07 said: Network channels must be registered during FMLCommonSetupEvent. Not at some random time during some random static initializer. Sorry... There is still a strange problem... I add this to my main class, and I think it work. FMLJavaModLoadingContext.get().getModEventBus().addListener(PacketHandler::register); But it still throws an error like this: [08:57:50] [Server thread/ERROR] [ne.mi.fm.ne.si.IndexedMessageCodec/SIMPLENET]: Received empty payload on channel fml:handshake The game will not exit, but there is nothing when I left click my item. Maybe I make some stupid mistakes...
January 22, 20205 yr Author 45 minutes ago, diesieben07 said: Show more of your code. ok. Arknights.java package arknights; import arknights.network.PacketHandler; import arknights.network.packets.LeftClickPacket; import arknights.registry.BlockList; import arknights.registry.ItemHandler; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.item.*; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.InterModComms; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.stream.Collectors; // The value here should match an entry in the META-INF/mods.toml file @Mod(Arknights.MODID) public class Arknights { public static final String MODID = "arknights"; // Directly reference a log4j logger. private static final Logger LOGGER = LogManager.getLogger(); public static final ItemGroup ARKNIGHTS = new ItemGroup("arknights") { @Override public ItemStack createIcon() { return new ItemStack(ItemHandler.PUREORIGINIUMS); } }; //Registry Lists private static final BlockList BLOCKLIST = new BlockList(); private static final ItemHandler ITEMLIST = new ItemHandler(); //public static final Block ORIGINIUMS_ORE = new Block(Block.Properties.create(Material.GLASS).hardnessAndResistance(3.0F, 3.0F)).setRegistryName(MODID+":originiums_ore"); //public static final BlockItem ORIGINIUMS_ORE_ITEM = (BlockItem) new BlockItem(ORIGINIUMS_ORE, new Item.Properties().group(ARKNIGHTS)).setRegistryName(MODID+":originiums_ore"); //public static final Item PUREORIGINIUMS = new PureOriginiums(new Item.Properties().group(ARKNIGHTS)).setRegistryName(MODID+":pure_originiums"); public Arknights() { // Register the setup method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); // Register the enqueueIMC method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC); // Register the processIMC method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC); // Register the doClientStuff method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); FMLJavaModLoadingContext.get().getModEventBus().addListener(PacketHandler::register); // Register ourselves for server and other game events we are interested in MinecraftForge.EVENT_BUS.register(this); } private void setup(final FMLCommonSetupEvent event) { //PacketHandler.HANDLER.registerMessage(PacketHandler.index++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, LeftClickPacket.Handler::handle); // some preinit code LOGGER.info("HELLO FROM PREINIT"); LOGGER.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName()); } private void doClientStuff(final FMLClientSetupEvent event) { // do something that can only be done on the client LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings); } private void enqueueIMC(final InterModEnqueueEvent event) { // some example code to dispatch IMC to another mod InterModComms.sendTo("examplemod", "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";}); } private void processIMC(final InterModProcessEvent event) { // some example code to receive and process InterModComms from other mods LOGGER.info("Got IMC {}", event.getIMCStream(). map(m->m.getMessageSupplier().get()). collect(Collectors.toList())); } // You can use SubscribeEvent and let the Event Bus discover methods to call @SubscribeEvent public void onServerStarting(FMLServerStartingEvent event) { // do something when the server starts LOGGER.info("HELLO from server starting"); } // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD // Event bus for receiving Registry Events) @Mod.EventBusSubscriber(modid = Arknights.MODID, bus=Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { @SubscribeEvent public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) { // register a new block here for(Block block : BLOCKLIST.blockList){ blockRegistryEvent.getRegistry().register(block); } LOGGER.info("HELLO from Register Block"); } @SubscribeEvent public static void onItemsRegistry(final RegistryEvent.Register<Item> itemRegisterEvent) { for(Item item : BLOCKLIST.blockItemsList){ itemRegisterEvent.getRegistry().register(item); } //MinecraftForge.EVENT_BUS.addListener(ItemHandler::register); ItemHandler.register(itemRegisterEvent); /* for (Item item : ITEMLIST.itemList){ itemRegisterEvent.getRegistry().register(item); }*/ } } } Nothing has changed in other code. Thanks. Edited January 22, 20205 yr by NGYF
January 22, 20205 yr Author Just now, diesieben07 said: Then you are still registering your network channel in a static initializer. Maybe I need an example... The former code reference ProjectE, and it use the static initializer. And the documentation is static too. I try to register it in a not static initializer (maybe in a wrong way), and it doesn't work. When I left click, nothing has happened. By the way, the thrown error appears when I join the world, and when I delete the packet registry, it still appears. Here is the code: PacketHandler.java package arknights.network; import arknights.network.packets.LeftClickPacket; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.simple.SimpleChannel; public final class PacketHandler { private static final String PROTOCOL_VERSION = Integer.toString(1); public SimpleChannel HANDLER = NetworkRegistry.newSimpleChannel( new ResourceLocation("corruption","main") ,() -> PROTOCOL_VERSION , PROTOCOL_VERSION::equals , PROTOCOL_VERSION::equals); int id = 0; public void register(){ HANDLER.registerMessage(id++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, LeftClickPacket.Handler::handle); } } Arknights.java Here is the whole class. Spoiler package arknights; import arknights.network.PacketHandler; import arknights.network.packets.LeftClickPacket; import arknights.registry.BlockList; import arknights.registry.ItemHandler; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.item.*; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.InterModComms; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.stream.Collectors; // The value here should match an entry in the META-INF/mods.toml file @Mod(Arknights.MODID) public class Arknights { public static final String MODID = "arknights"; // Directly reference a log4j logger. private static final Logger LOGGER = LogManager.getLogger(); public static final ItemGroup ARKNIGHTS = new ItemGroup("arknights") { @Override public ItemStack createIcon() { return new ItemStack(ItemHandler.PUREORIGINIUMS); } }; public static final PacketHandler PACKETHANDLER = new PacketHandler(); //Registry Lists private static final BlockList BLOCKLIST = new BlockList(); //public static final Block ORIGINIUMS_ORE = new Block(Block.Properties.create(Material.GLASS).hardnessAndResistance(3.0F, 3.0F)).setRegistryName(MODID+":originiums_ore"); //public static final BlockItem ORIGINIUMS_ORE_ITEM = (BlockItem) new BlockItem(ORIGINIUMS_ORE, new Item.Properties().group(ARKNIGHTS)).setRegistryName(MODID+":originiums_ore"); //public static final Item PUREORIGINIUMS = new PureOriginiums(new Item.Properties().group(ARKNIGHTS)).setRegistryName(MODID+":pure_originiums"); public Arknights() { // Register the setup method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); // Register the enqueueIMC method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC); // Register the processIMC method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC); // Register the doClientStuff method for modloading FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); //FMLJavaModLoadingContext.get().getModEventBus().addListener(PacketHandler::register); PACKETHANDLER.register(); // Register ourselves for server and other game events we are interested in MinecraftForge.EVENT_BUS.register(this); } private void setup(final FMLCommonSetupEvent event) { //PacketHandler.HANDLER.registerMessage(PacketHandler.index++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, LeftClickPacket.Handler::handle); // some preinit code LOGGER.info("HELLO FROM PREINIT"); LOGGER.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName()); } private void doClientStuff(final FMLClientSetupEvent event) { // do something that can only be done on the client LOGGER.info("Got game settings {}", event.getMinecraftSupplier().get().gameSettings); } private void enqueueIMC(final InterModEnqueueEvent event) { // some example code to dispatch IMC to another mod InterModComms.sendTo("examplemod", "helloworld", () -> { LOGGER.info("Hello world from the MDK"); return "Hello world";}); } private void processIMC(final InterModProcessEvent event) { // some example code to receive and process InterModComms from other mods LOGGER.info("Got IMC {}", event.getIMCStream(). map(m->m.getMessageSupplier().get()). collect(Collectors.toList())); } // You can use SubscribeEvent and let the Event Bus discover methods to call @SubscribeEvent public void onServerStarting(FMLServerStartingEvent event) { // do something when the server starts LOGGER.info("HELLO from server starting"); } // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD // Event bus for receiving Registry Events) @Mod.EventBusSubscriber(modid = Arknights.MODID, bus=Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { @SubscribeEvent public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) { // register a new block here for(Block block : BLOCKLIST.blockList){ blockRegistryEvent.getRegistry().register(block); } LOGGER.info("HELLO from Register Block"); } @SubscribeEvent public static void onItemsRegistry(final RegistryEvent.Register<Item> itemRegisterEvent) { for(Item item : BLOCKLIST.blockItemsList){ itemRegisterEvent.getRegistry().register(item); } //MinecraftForge.EVENT_BUS.addListener(ItemHandler::register); ItemHandler.register(itemRegisterEvent); /* for (Item item : ITEMLIST.itemList){ itemRegisterEvent.getRegistry().register(item); }*/ } } } I think there is something not so relevant, so I provide a simple one. public class Arknights { //something //code I add public static final PacketHandler PACKETHANDLER = new PacketHandler(); public Arknights() { // Register methods for modloading //code I add PACKETHANDLER.register(); // Register ourselves for server and other game events we are interested in } BaseItem.java package arknights.item; import arknights.Arknights; import arknights.network.PacketHandler; import arknights.network.packets.LeftClickPacket; import net.minecraft.entity.LivingEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerInteractEvent; public class BaseItem extends Item { public BaseItem(Properties p_i48487_1_) { super(p_i48487_1_); MinecraftForge.EVENT_BUS.addListener(this::emptyLeftClick); } public void emptyLeftClick(PlayerInteractEvent.LeftClickEmpty event){ Arknights.PACKETHANDLER.HANDLER.sendToServer(new LeftClickPacket()); } public void leftClick(LivingEntity livingEntity, World worldIn, ItemStack stack){ } }
January 22, 20205 yr Author 4 minutes ago, diesieben07 said: I don't know how this is so hard to understand. The registration of your network channel must happen in FMLCommonSetupEvent. You are still not doing that. Now you are calling newSimpleChannel from the constructor of PacketHandler, which you call from the constructor of Arknights. This is not in FMLCommonSetupEvent. private void setup(final FMLCommonSetupEvent event) { PacketHandler.register(); } Should I do it just like this? (The method is in Arknights.java) I'm new to modding... And the problem becomes more and more complicated... I test the mod with out packet handler but it still tells me "Received empty payload on channel fml:handshake". I also test the method leftclick in my item. I add "System.out.print("A"); " to the method, and when I left click, an "A" prints to the terminal. Does it means the packet is succesfully sent? If so, why it doesn't spawn an arrow? Could you tell me how to spawn entities properly?
January 22, 20205 yr Author 9 minutes ago, diesieben07 said: Post the complete debug.log. How would it? Printing "A" to the console does not tell you anything about the state of your packet. I upload the log. Here is one part of the log, it appears when I join the game. It starts at line 16620. [22一月2020 19:05:17.222] [Server thread/INFO] [net.minecraft.server.MinecraftServer/]: Dev加入了游戏 [22一月2020 19:05:17.344] [Render thread/DEBUG] [net.minecraftforge.fml.network.FMLLoginWrapper/FMLHANDSHAKE]: Recieved login wrapper packet event for channel fml:handshake with index -1 [22一月2020 19:05:17.344] [Render thread/ERROR] [net.minecraftforge.fml.network.simple.IndexedMessageCodec/SIMPLENET]: Received empty payload on channel fml:handshake [22一月2020 19:05:18.760] [Server thread/INFO] [net.minecraft.server.integrated.IntegratedServer/]: Saving and pausing game... [22一月2020 19:05:18.985] [Render thread/INFO] [net.minecraft.advancements.AdvancementList/]: Loaded 12 advancements [22一月2020 19:05:19.007] [Server thread/INFO] [net.minecraft.server.MinecraftServer/]: Saving chunks for level 'RhodesIsland'/minecraft:overworld [22一月2020 19:05:19.683] [Server thread/DEBUG] [net.minecraftforge.fml.FMLWorldPersistenceHook/WP]: Gathering id map for writing to world save RhodesIsland [22一月2020 19:05:19.881] [Server thread/DEBUG] [net.minecraftforge.fml.network.FMLLoginWrapper/FMLHANDSHAKE]: Recieved login wrapper packet event for channel fml:handshake with index -1 [22一月2020 19:05:19.881] [Server thread/ERROR] [net.minecraftforge.fml.network.simple.IndexedMessageCodec/SIMPLENET]: Received empty payload on channel fml:handshake The item will send a packet to server when it is left clicked. The method is in BaseItem.java public void emptyLeftClick(PlayerInteractEvent.LeftClickEmpty event){ PacketHandler.HANDLER.sendToServer(new LeftClickPacket()); } And "leftclick" method will be called like this: LeftClickPacket.java package arknights.network.packets; import arknights.item.BaseItem; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; import java.util.function.Supplier; public class LeftClickPacket { public static void encode(LeftClickPacket msg, PacketBuffer buf){ } public static LeftClickPacket decode(PacketBuffer buf){ return new LeftClickPacket(); } public static class Handler { public static void handle(LeftClickPacket msg, Supplier<NetworkEvent.Context> ctx){ ctx.get().enqueueWork(() -> { PlayerEntity player = ctx.get().getSender(); ItemStack item = player.getHeldItemMainhand(); if(!item.isEmpty() && (item.getItem() instanceof BaseItem)){ ((BaseItem) item.getItem()).leftClick(player, player.getEntityWorld(), item); } }); ctx.get().setPacketHandled(true); } } } leftClick method (inside my item class): public void leftClick(LivingEntity livingEntity, World worldIn, ItemStack stack){ System.out.print("A"); } Does it mean the packet is successfully sent?
January 22, 20205 yr A comment, if I may: 1 hour ago, diesieben07 said: The registration of your network channel must happen in FMLCommonSetupEvent. You are still not doing that. Now you are calling newSimpleChannel from the constructor of PacketHandler, which you call from the constructor of Arknights. This is not in FMLCommonSetupEvent. The problem in your code is that you construct your SimpleChannel at the wrong time. NetworkRegistry::newSimpleChannel should be called from your FMLCommonSetupEvent function, but instead you call it when the class Arknights is constructed -- a long time before FMLCommonSetupEvent will be called. To fix this, here are the adjustments: Spoiler @Mod(Arknights.MODID) public class Arknights { // ...other code and stuff private void setup(final FMLCommonSetupEvent event) { PacketHandler.register(); // this should be, for now, the ONLY code referencing PacketHandler in this class } // ...other code and stuff } public class PacketHandler { private static final String PROTOCOL_VERSION = Integer.toString(1); public static SimpleChannel HANDLER = NetworkRegistry.newSimpleChannel( // change to static new ResourceLocation("arknights", "main"), // use modid in resourcelocation, to be unique in case other mods use corruption as modid () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); private static int id = 0; // change to private static (more to hide unneeded variables) public static void register() { // change to static HANDLER.registerMessage(id++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, LeftClickPacket.Handler::handle); } } And the explanations: Spoiler ProjectE and other mods work with the static initialization method of registering SimpleChannel beacuse the first time they reference their PacketHandler class is in FMLCommonSetupEvent. See, the JVM loads classes lazily -- classes only get loaded (and the static code in them only get called) if and only when another class calls them. So, when the JVM loads your PacketHandler class, it sees the static initialization of SimpleChannel and it will call NetworkRegistry::newSimpleChannel. This is safe, because if the first reference to PacketHandler is during FMLCommonSetupEvent, then the static code in PacketHandler -- such as the call to NetworkRegistry::newSimpleChannel -- also runs during FMLCommonSetupEvent. This is how ProjectE and many other mods do this. Yes, there are other ways to register your SimpleChannel -- like putting your NetworkRegistry::newSimpleChannel call inside of register() -- but this is, in my opinion, the easiest to read and write. (Plus, most tutorials will likely use this method, because it just works.)
January 22, 20205 yr Author 27 minutes ago, sciwhiz12 said: A comment, if I may: The problem in your code is that you construct your SimpleChannel at the wrong time. NetworkRegistry::newSimpleChannel should be called from your FMLCommonSetupEvent function, but instead you call it when the class Arknights is constructed -- a long time before FMLCommonSetupEvent To fix this, here are the adjustments: Hide contents @Mod(Arknights.MODID) public class Arknights { // ...other code and stuff private void setup(final FMLCommonSetupEvent event) { PacketHandler.register(); // this should be, for now, the ONLY code referencing PacketHandler in this class } // ...other code and stuff } public class PacketHandler { private static final String PROTOCOL_VERSION = Integer.toString(1); public static SimpleChannel HANDLER = NetworkRegistry.newSimpleChannel( // change to static new ResourceLocation("arknights", "main"), // use modid in resourcelocation, to be unique in case other mods use corruption as modid () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals); private static int id = 0; // change to private static (more to hide unneeded variables) public static void register() { // change to static HANDLER.registerMessage(id++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, LeftClickPacket.Handler::handle); } } And the explanations: Hide contents ProjectE and other mods work with the static initialization method of registering SimpleChannel beacuse the first time they reference their PacketHandler class is in FMLCommonSetupEvent. See, the JVM loads classes lazily -- classes only get loaded (and the static code in them only get called) if and only when another class calls them. So, when the JVM loads your PacketHandler class, it sees the static initialization of SimpleChannel and it will call NetworkRegistry::newSimpleChannel. This is safe, because if the first reference to PacketHandler is during FMLCommonSetupEvent, then the static code in PacketHandler -- such as the call to NetworkRegistry::newSimpleChannel -- also runs during FMLCommonSetupEvent. This is how ProjectE and many other mods do this. Yes, there are other ways to register your SimpleChannel -- like putting your NetworkRegistry::newSimpleChannel call inside of register() -- but this is, in my opinion, the easiest to read and write. (Plus, most tutorials will likely use this method, because it just works.) Thank you for your patience! It helps me a lot.
January 22, 20205 yr 17 minutes ago, NGYF said: Thank you for your patience! It helps me a lot. You're welcome! And another thing which I noticed; You are registering your listener for PlayerInteractEvent.LeftClickEmpty in your BaseItem class. This is a problem: if you create multiple instances of BaseItem, you are registering your listener multiple times, which means that your code will send multiple packets for the same event. To fix this, I recommend moving that code out of your BaseItem class, and into another class -- either a dedicated EventListener class, or even just inside your Arknights mod class: Spoiler @Mod(Arknights.MODID) public class Arknights { // ...other code @Mod.EventBusSubscriber(modid = Arknights.MODID, bus = Bus.MOD) public static class EventListener { @SubscribeEvent public static void emptyLeftClick(PlayerInteractEvent.LeftClickEmpty event) { PacketHandler.HANDLER.sendToServer(new LeftClickPacket()); } } // ...other code } public class BaseItem extends Item { public BaseItem(Properties props) { super(props); } public void leftClick(LivingEntity livingEntity, World worldIn, ItemStack stack) {} } This fixes the problem, and gives a cleaner way to add more event listeners in the future.
January 22, 20205 yr Author 2 minutes ago, sciwhiz12 said: You're welcome! And another thing which I noticed; You are registering your listener for PlayerInteractEvent.LeftClickEmpty in your BaseItem class. This is a problem: if you create multiple instances of BaseItem, you are registering your listener multiple times, which means that your code will send multiple packets for the same event. To fix this, I recommend moving that code out of your BaseItem class, and into another class -- either a dedicated EventListener class, or even just inside your Arknights mod class: Hide contents @Mod(Arknights.MODID) public class Arknights { // ...other code @Mod.EventBusSubscriber(modid = Arknights.MODID, bus = Bus.MOD) public static class EventListener { @SubscribeEvent public static void emptyLeftClick(PlayerInteractEvent.LeftClickEmpty event) { PacketHandler.HANDLER.sendToServer(new LeftClickPacket()); } } // ...other code } public class BaseItem extends Item { public BaseItem(Properties props) { super(props); } public void leftClick(LivingEntity livingEntity, World worldIn, ItemStack stack) {} } This fixes the problem, and gives a cleaner way to add more event listeners in the future. Thank you! That's useful!
January 22, 20205 yr Author Holy shit! I make an very stupid mistake... I forget to add entity to world! That's why I can't find the arrow. I'm so stupid...
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.