Jump to content

[1.14.4][SOLVED] capability resets server side


andGarrett

Recommended Posts

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 by andGarrett
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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());
    }

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 by andGarrett
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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());
    }
}

 

Link to comment
Share on other sites

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 by andGarrett
Link to comment
Share on other sites

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.

  • Thanks 1

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.

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.