Posted September 6, 20196 yr every time I load up one of my worlds, my "BlockPopulation" capability on the server side seems to reset back to zero. I suspect that it's creating a new instance of the capability every time I load the world, but only on the server side. I think it has something to do with my capability storage class. [SOLUTION] I thought that if code was being executed on the client side that (World.isRemote) would return false, but it's the other way around. This led me to think there was a problem when actually there wasn't. BlockPopulationStorage package com.Garrett.backtobasics.capabilities; import net.minecraft.nbt.INBT; import net.minecraft.nbt.IntNBT; import net.minecraft.util.Direction; import net.minecraftforge.common.capabilities.Capability; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; /** * This class is responsible for saving and reading block population data from/to the server */ public class BlockPopulationStorage implements Capability.IStorage<IBlockPopulation> { private static final Logger LOGGER = LogManager.getLogger(); @Nullable @Override public INBT writeNBT(Capability<IBlockPopulation> capability, IBlockPopulation instance, Direction side) { return new IntNBT(instance.getStores()); } @Override public void readNBT(Capability<IBlockPopulation> capability, IBlockPopulation instance, Direction side, INBT nbt) { instance.setStores(((IntNBT) nbt).getInt()); } } Here is where I change the values in my BlockPopulation capability (in the "onBlockPlacedBy" method): package com.Garrett.backtobasics.blocks; import com.Garrett.backtobasics.capabilities.BlockPopulationProvider; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.item.BlockItemUseContext; import net.minecraft.item.ItemStack; import net.minecraft.state.StateContainer; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.world.IBlockReader; import net.minecraft.world.World; import net.minecraftforge.fml.network.NetworkHooks; import javax.annotation.Nullable; public class AutoMiner extends Block { public AutoMiner() { super(Properties .create(Material.IRON) .sound(SoundType.METAL) .hardnessAndResistance(10, 15) .lightValue(0) .harvestLevel(0) ); setRegistryName("backtobasics:auto_miner"); } @Override public void onBlockPlacedBy(World worldIn, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { LOGGER.info("onBlockPlacedBY"); LOGGER.info("world.isRemote = " + worldIn.isRemote); worldIn.getCapability(BlockPopulationProvider.BLOCK_POPULATION_CAPABILITY).ifPresent(h -> { h.addStore(); LOGGER.info("number of stores: " + h.getStores()); }); } @Override public boolean hasTileEntity(BlockState state) { return true; } @Nullable @Override public TileEntity createTileEntity(BlockState state, IBlockReader world) { return new AutoMinerTileEntity(); } // tells block what way to face when placed @Nullable @Override public BlockState getStateForPlacement(BlockItemUseContext context) { BlockState blockstate = super.getStateForPlacement(context); if (blockstate != null) { blockstate = blockstate.with(BlockStateProperties.FACING, context.getNearestLookingDirection()); } return blockstate; } @Override public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult result) { if (!world.isRemote) { TileEntity tileEntity = world.getTileEntity(pos); if (tileEntity instanceof INamedContainerProvider) { NetworkHooks.openGui((ServerPlayerEntity) player, (INamedContainerProvider) tileEntity, tileEntity.getPos()); } else { throw new IllegalStateException("Our named container provider is missing!"); } return true; } return super.onBlockActivated(state, world, pos, player, hand, result); } // adds support for "facing" property in block states json files @Override protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) { builder.add(BlockStateProperties.FACING); } } here are the other classes that are involved, in case they are needed: BlockPopulationProvider package com.Garrett.backtobasics.capabilities; import net.minecraft.nbt.INBT; import net.minecraft.util.Direction; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.CapabilityInject; import net.minecraftforge.common.capabilities.ICapabilitySerializable; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.NonNullSupplier; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BlockPopulationProvider implements ICapabilitySerializable<INBT> { @CapabilityInject(IBlockPopulation.class) public static Capability<IBlockPopulation> BLOCK_POPULATION_CAPABILITY = null; private final LazyOptional<IBlockPopulation> holder = LazyOptional.of(BlockPopulation::new); @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { return cap == BLOCK_POPULATION_CAPABILITY ? holder.cast() : LazyOptional.empty(); } @Override public INBT serializeNBT() { NonNullSupplier<IBlockPopulation> nonNullSupplier = new NonNullSupplier<IBlockPopulation>() { @Nonnull @Override public IBlockPopulation get() { return null; } }; return BLOCK_POPULATION_CAPABILITY.getStorage().writeNBT(BLOCK_POPULATION_CAPABILITY, holder.orElseGet(nonNullSupplier), null); } @Override public void deserializeNBT(INBT nbt) { NonNullSupplier<IBlockPopulation> nonNullSupplier = new NonNullSupplier<IBlockPopulation>() { @Nonnull @Override public IBlockPopulation get() { return null; } }; BLOCK_POPULATION_CAPABILITY.getStorage().readNBT(BLOCK_POPULATION_CAPABILITY, holder.orElseGet(nonNullSupplier), null, nbt); } } BackToBasicsEventHandler package com.Garrett.backtobasics.capabilities; import com.Garrett.backtobasics.BackToBasics; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; /** * Event handler */ public class BackToBasicsEventHandler { public static final ResourceLocation BLOCK_POPULATION_CAPABILITY = new ResourceLocation(BackToBasics.MODID, "block_population"); @SubscribeEvent public void attachCapability(AttachCapabilitiesEvent<World> event) { if (!(event.getObject() instanceof World)) return; event.addCapability(BLOCK_POPULATION_CAPABILITY, new BlockPopulationProvider()); } } Edited September 8, 20196 yr by andGarrett
September 6, 20196 yr 1 hour ago, andGarrett said: I think it has something to do with my capability storage class. Have you added log statements to the read and write methods? 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.
September 6, 20196 yr Author 16 hours ago, Animefan8888 said: Have you added log statements to the read and write methods? are you talking about this one in BlockPopulationStorage? @Nullable @Override public INBT writeNBT(Capability<IBlockPopulation> capability, IBlockPopulation instance, Direction side) { return new IntNBT(instance.getStores()); }
September 6, 20196 yr 15 minutes ago, andGarrett said: are you talking about this one in BlockPopulationStorage? Well yes, but you might as well do it in all of them. 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.
September 6, 20196 yr Author 11 minutes ago, Animefan8888 said: Well yes, but you might as well do it in all of them. I'm not sure how to add a "log statement."
September 7, 20196 yr 2 hours ago, andGarrett said: I'm not sure how to add a "log statement. Logger#debug or Logger#info (I would use the first one) 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.
September 7, 20196 yr Author 22 minutes ago, Animefan8888 said: Logger#debug or Logger#info (I would use the first one) oooooooooh, I thought you meant something else, lol. I know that that is. I put them in the "writeNBT" method as well as the "readNBT" method. it only seems to be happening on the client. maybe it has something to do with how I registered it in my main mod class? CapabilityManager.INSTANCE.register(IBlockPopulation.class, new BlockPopulationStorage(), BlockPopulation::new); Edited September 7, 20196 yr by andGarrett
September 7, 20196 yr 25 minutes ago, andGarrett said: maybe it has something to do with how I registered it in my main mod class? Did you register it only on the client/attaching it only on the client? 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.
September 7, 20196 yr Author 34 minutes ago, Animefan8888 said: Did you register it only on the client/attaching it only on the client? I registered it in my main mod class. search "banana" to find the lines: package com.Garrett.backtobasics; import com.Garrett.backtobasics.blocks.*; import com.Garrett.backtobasics.capabilities.BackToBasicsEventHandler; import com.Garrett.backtobasics.capabilities.BlockPopulation; import com.Garrett.backtobasics.capabilities.BlockPopulationStorage; import com.Garrett.backtobasics.capabilities.IBlockPopulation; import com.Garrett.backtobasics.items.*; import com.Garrett.backtobasics.world.OreGeneration; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.inventory.container.ContainerType; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.math.BlockPos; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.DistExecutor; 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 com.Garrett.backtobasics.setup.ModSetup; import com.Garrett.backtobasics.setup.*; import java.util.stream.Collectors; // The value here should match an entry in the META-INF/mods.toml file @Mod("backtobasics") public class BackToBasics { public static final String MODID = "backtobasics"; public static ModSetup setup = new ModSetup(); public static IProxy proxy = DistExecutor.runForDist(() -> () -> new ClientProxy(), () -> () -> new ServerProxy()); // Directly reference a log4j logger. private static final Logger LOGGER = LogManager.getLogger(); public BackToBasics() { // 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); // Register ourselves for server and other game events we are interested in MinecraftForge.EVENT_BUS.register(this); // BTB Register my custom events BackToBasicsEventHandler backToBasicsEventHandler = new BackToBasicsEventHandler(); MinecraftForge.EVENT_BUS.register(backToBasicsEventHandler); // banana } private void setup(final FMLCommonSetupEvent event) { // some preinit code setup.init(); proxy.init(); LOGGER.info("HELLO FROM PREINIT"); LOGGER.info("DIRT BLOCK >> {}", Blocks.DIRT.getRegistryName()); // BTB OreGeneration.setupOreGeneration(); // BTB CapabilityManager.INSTANCE.register(IBlockPopulation.class, new BlockPopulationStorage(), BlockPopulation::new); // banana } 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("backtobasics", "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(bus=Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { @SubscribeEvent public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) { // register a new block here LOGGER.info("HELLO from Register Block"); blockRegistryEvent.getRegistry().register(new AutoMiner()); blockRegistryEvent.getRegistry().register(new Store()); blockRegistryEvent.getRegistry().register(new DiamondVein()); blockRegistryEvent.getRegistry().register(new GoldVein()); blockRegistryEvent.getRegistry().register(new IronVein()); blockRegistryEvent.getRegistry().register(new RedstoneVein()); } @SubscribeEvent public static void onItemRegistry(final RegistryEvent.Register<Item> ItemRegistryEvent) { // register a new item here Item.Properties properties = new Item.Properties() .group(setup.itemGroup); ItemRegistryEvent.getRegistry().register(new BlockItem(ModBlocks.AUTO_MINER, properties).setRegistryName("backtobasics:auto_miner")); ItemRegistryEvent.getRegistry().register(new BlockItem(ModBlocks.STORE, properties).setRegistryName("backtobasics:store")); ItemRegistryEvent.getRegistry().register(new BlockItem(ModBlocks.DIAMOND_VEIN, properties).setRegistryName("backtobasics:diamond_vein")); ItemRegistryEvent.getRegistry().register(new BlockItem(ModBlocks.GOLD_VEIN, properties).setRegistryName("backtobasics:gold_vein")); ItemRegistryEvent.getRegistry().register(new BlockItem(ModBlocks.IRON_VEIN, properties).setRegistryName("backtobasics:iron_vein")); ItemRegistryEvent.getRegistry().register(new BlockItem(ModBlocks.REDSTONE_VEIN, properties).setRegistryName("backtobasics:redstone_vein")); ItemRegistryEvent.getRegistry().register(new UnrefinedIron()); ItemRegistryEvent.getRegistry().register(new UnrefinedIronBits()); ItemRegistryEvent.getRegistry().register(new UnrefinedGold()); ItemRegistryEvent.getRegistry().register(new UnrefinedGoldBits()); ItemRegistryEvent.getRegistry().register(new DiamondBits()); ItemRegistryEvent.getRegistry().register(new RedstoneBits()); //LOGGER.info("banana"); //LOGGER.info(ItemRegistryEvent.getRegistry().getValues()); } @SubscribeEvent public static void onTileEntityRegistry(final RegistryEvent.Register<TileEntityType<?>> event) { event.getRegistry().register(TileEntityType.Builder.create(AutoMinerTileEntity::new, ModBlocks.AUTO_MINER).build(null).setRegistryName("auto_miner")); event.getRegistry().register(TileEntityType.Builder.create(StoreTileEntity::new, ModBlocks.STORE).build(null).setRegistryName("store")); } @SubscribeEvent public static void onContainerRegistry(final RegistryEvent.Register<ContainerType<?>> event) { event.getRegistry().register(IForgeContainerType.create((windowId, inv, data) -> { BlockPos pos = data.readBlockPos(); return new AutoMinerContainer(windowId, BackToBasics.proxy.getClientWorld(), pos, inv, BackToBasics.proxy.getClientPlayer()); }).setRegistryName("auto_miner")); event.getRegistry().register(IForgeContainerType.create((windowId, inv, data) -> { BlockPos pos = data.readBlockPos(); return new StoreContainer(windowId, BackToBasics.proxy.getClientWorld(), pos, inv, BackToBasics.proxy.getClientPlayer()); }).setRegistryName("store")); } } } here is where I attach the capabiltiy: package com.Garrett.backtobasics.capabilities; import com.Garrett.backtobasics.BackToBasics; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; /** * Event handler */ public class BackToBasicsEventHandler { public static final ResourceLocation BLOCK_POPULATION_CAPABILITY = new ResourceLocation(BackToBasics.MODID, "block_population"); @SubscribeEvent public void attachCapability(AttachCapabilitiesEvent<World> event) { if (!(event.getObject() instanceof World)) return; event.addCapability(BLOCK_POPULATION_CAPABILITY, new BlockPopulationProvider()); } }
September 7, 20196 yr Author 8 hours ago, diesieben07 said: Also explain how exactly you determined that the value resets. I put log statements where I was changing the values for the capability: @Override public void onBlockPlacedBy(World worldIn, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { worldIn.getCapability(BlockPopulationProvider.BLOCK_POPULATION_CAPABILITY).ifPresent(cap -> { cap.addStore(); LOGGER.info("onBlockPlacedBY"); LOGGER.info("world.isRemote = " + worldIn.isRemote + ", number of stores: " + cap.getStores()); }); } whenever I place the block that has this in its block class, I get two sets of logs like these: [13:56:19.942] [Client thread/INFO] [minecraft/Block]: onBlockPlacedBY [13:56:19.942] [Client thread/INFO] [minecraft/Block]: world.isRemote = true, number of stores: 1 [13:56:19.944] [Server thread/INFO] [minecraft/Block]: onBlockPlacedBY [13:56:19.944] [Server thread/INFO] [minecraft/Block]: world.isRemote = false, number of stores: 16 now that I look at it though, I see that it's on the client thread that the number is resetting. If it's on the "Client thread" thread, why does it say the world is remote? does that mean that even when I'm playing on my own, the world is always considered remote from the client side? I had assumed that when the world is remote, the code being run was on the server side. should I just ignore the client side information? Edited September 7, 20196 yr by andGarrett
September 7, 20196 yr Remote means the client - I think you are only supposed to set capability values on the server. So use !isRemote when you want to set capability values.
September 7, 20196 yr 39 minutes ago, andGarrett said: should I just ignore the client side information? Don' change the values on the client based on what the client says is true. The client can and will lie. Always and only change the data on the server and then sync it to the client via the use of packets. 40 minutes ago, andGarrett said: I had assumed that when the world is remote, the code being run was on the server side. It's actually the other way around. I can't remember the explanation I've heard for it's name though. But basically always check for !world.isRemote or Entity#isServerWorld; which both do the same thing. 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.
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.