Jump to content

[1.8][SOLVED] TE Registration not Sticking


jeffryfisher

Recommended Posts

Even after I register my tile entity, classToNameMap comes up dry. What obvious thing am I missing?

 

Console Output:

[17:30:28] [server thread/INFO]: Saving and pausing game...
[17:30:28] [server thread/INFO]: Saving chunks for level 'Blower Test'/Overworld
[17:30:29] [server thread/ERROR] [FML]: A TileEntity type jrfinventions.classBlowerTEClient has throw an exception trying to write state. It will not persist. Report this to the mod author
java.lang.RuntimeException: class jrfinventions.classBlowerTEClient is missing a mapping! This is a bug!
at net.minecraft.tileentity.TileEntity.writeToNBT(TileEntity.java:94) ~[TileEntity.class:?]
at jrfinventions.classBlowerTE.writeToNBT(classBlowerTE.java:52) ~[classBlowerTE.class:?]
at net.minecraft.world.chunk.storage.AnvilChunkLoader.writeChunkToNBT(AnvilChunkLoader.java:410) [AnvilChunkLoader.class:?]
at net.minecraft.world.chunk.storage.AnvilChunkLoader.saveChunk(AnvilChunkLoader.java:193) [AnvilChunkLoader.class:?]
at net.minecraft.world.gen.ChunkProviderServer.saveChunkData(ChunkProviderServer.java:266) [ChunkProviderServer.class:?]
at net.minecraft.world.gen.ChunkProviderServer.saveChunks(ChunkProviderServer.java:332) [ChunkProviderServer.class:?]
at net.minecraft.world.WorldServer.saveAllChunks(WorldServer.java:976) [WorldServer.class:?]
at net.minecraft.server.MinecraftServer.saveAllWorlds(MinecraftServer.java:419) [MinecraftServer.class:?]
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:147) [integratedServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:540) [MinecraftServer.class:?]
at java.lang.Thread.run(Unknown Source) [?:1.7.0_55]
[17:30:29] [server thread/INFO]: Saving chunks for level 'Blower Test'/Nether
[17:30:29] [server thread/INFO]: Saving chunks for level 'Blower Test'/The End
[17:30:31] [server thread/INFO]: Stopping server
[17:30:31] [server thread/INFO]: Saving players
[17:30:31] [server thread/INFO]: Saving worlds
[17:30:31] [server thread/INFO]: Saving chunks for level 'Blower Test'/Overworld
[17:30:31] [server thread/INFO]: Saving chunks for level 'Blower Test'/Nether
[17:30:31] [server thread/INFO]: Saving chunks for level 'Blower Test'/The End

 

The throw at line 94:

    public void writeToNBT(NBTTagCompound compound)
    {
        String s = (String)classToNameMap.get(this.getClass());

        if (s == null)
        {
            throw new RuntimeException(this.getClass() + " is missing a mapping! This is a bug!");

 

The mapping:

public class classBlockBlower extends BlockDirectional implements ITileEntityProvider {

  private boolean firstTime = true;                                // TODO: Remove debug var

// public static final PropertyDirection FACING = PropertyDirection.create ("facing");
  public static final PropertyInteger FANSPEED = PropertyInteger.create ("fanspeed", 0, 3);

  public static final int tickPeriod = 10;                  // Update block once every x ticks (at 20 ticks per second)
  protected final String mod;
  protected boolean clean;                                  // Debug: Send one tick warning

  public classBlockBlower(String n) {
    super (Material.iron);

    this.setDefaultState (this.blockState.getBaseState ().withProperty (FACING, EnumFacing.NORTH));
    this.setUnlocalizedName (n);                            // Used in regBlock
    this.setStepSound (soundTypeMetal);
    this.setHardness (0.5F);
    this.setCreativeTab (CreativeTabs.tabRedstone);         // May be superseded by regBlock
    classInventionsMod.regBlock (this);                     // Not ticking until speed fixed when placed.
    mod = classInventionsMod.MODID;
    GameRegistry.registerTileEntity (classBlowerTE.class, "classBlowerTE");
  }
...
}

 

I've stepped through in the debugger, and the registerTileEntity call (which adds to the map) is made before the classToNameMap.get. I am bamboozeled. What am I missing?

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

Thanks, the debugger must have shown me the server doing its registration and then the client blowing up. I'm still wrapping my head around running client-server in the debugger, not always aware of which is which when I hit a break. I'll make sure that the client-side TE class is what gets registered in the client.

 

Yes, my naming convention is archaic. I like to keep classes very distinct from instances and variables so I instantly know what I am seeing wherever any are used to refer to static methods and members.

 

My tile entity is following the pattern of proxies, with the client-only TE making a continuous custom sound using classes that can't even be imported server-side. I couldn't exactly follow the minecart's sound example because it depends on World/WorldClient which doesn't even know I exist. I needed to create my own class that has a client version to do that. It starts the sound at itself during setPosition. I programmed the sound to terminate itself (donePlaying=true) when its TE becomes invalid during removal.

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

It sounds like you are making things way more complicated than they need to be. Classes such as TileEntities that are included in both server and client jars can and do use @SideOnly(Side.CLIENT) methods, they just need to segregate them smartly.

 

In your case, you can simply check if the world is remote before doing any of your client-side sound stuff, or if you're really worried about it, funnel the call through your proxy via a NON-static method. Static methods and variables are typically design flaws, so if you find yourself using them a lot (which it sounds like you are), you may want to rethink how you're doing things.

 

Anyways, you most definitely should NOT have different classes for the same TileEntity depending on which side you expect it to be on. Not only is there no reason to ever do that, but Minecraft isn't set up to handle it. It's just asking for trouble.

Link to comment
Share on other sites

It sounds like you are making things way more complicated than they need to be.

 

If I were developing inside minecraft, I might imitate minecart programming by having World class start my continuous sound for me. Alas, I don't have that option, so I must work around it and create something else that has World's server and client versions. Right now that's my TE class, but it may end up being a new proxy for my TE to call.

Classes such as TileEntities that are included in both server and client jars can and do use @SideOnly(Side.CLIENT) methods, they just need to segregate them smartly.

 

I am up against more than methods. I am facing a client-only class (PositionedSound), and that prevents me from even having its import statement on the server side.

 

In your case, you can simply check if the world is remote before doing any of your client-side sound stuff

 

That would be fine for execution, but it won't save my program when the import statement on the server throws a class-not-found exception.

 

or... funnel the call through your proxy

 

I thought about that, but then this mod would no longer be able to re-use the proxy-setup code I developed for my earlier mods. Rather than monkeying with that setup, I opted to add what is in effect a 2nd proxy pair.

 

Static methods and variables are typically design flaws, so if you find yourself using them a lot (which it sounds like you are), you may want to rethink how you're doing things.

 

Don't worry, I only use them where they make sense (or where Minecraft uses them). I mentioned static references as part of the reason for my idiosyncratic class naming -- I like to remind myself that a class is a class, not an instance. And if static references are uncommon, then I need the reminder even more.

 

Anyways, you most definitely should NOT have different classes for the same TileEntity depending on which side you expect it to be on. Not only is there no reason to ever do that, but Minecraft isn't set up to handle it. It's just asking for trouble.

 

When I find that trouble, I'll create a special proxy just for my TE to call. The TE class will again be unified, and a new pair of classes will handle server/client divergence. I'll also add a note here to tell future adventurers how much trouble I found. Thanks for the advance warning.

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

Imports are a compiler fiction. The JVM does not deal with imports and neither does anyone else but javac. Everything past one of the very early compilation steps always deals with fully qualified names.

OK, Eclipse's fiction (and error message) must have tricked me. Some time ago I tried to simply bracket my sided method calls so they'd only execute on the correct side, but their mere presence caused problems when loading. That means that the "world is remote" suggestion (someone else's) was not sufficient.

 

I thought about that, but then this mod would no longer be able to re-use the proxy-setup code I developed for my earlier mods.

That... sounds like a horrible "proxy setup" whatever that may be, if you cannot easily add to it...

It's actually quite useful for giving me a great head start on each mod. The things needed in every mod are already written, so I can jump ahead to the "meat" of each mod. It's also not the source of my problem, so I won't belabor it.

 

You actually hit the nail on the head by noticing that it was the client-side TE that wasn't registered. When I registered that class on the client using the same string used with the server class on the server side, the tile entities worked as intended. Thanks.

 

This is what my TE handling ended up being:

 

BlockBlower.createNewTileEntity:

public class BlockBlower extends BlockDirectional implements ITileEntityProvider {

<snip>

  @Override
  public TileEntity createNewTileEntity(World worldIn, int meta) {
    BlowerTE te;                           // TE needed to hold continuous (whirring) positioned sound
    // Consequently, TE's purpose is to emit *all* block sounds
    if (JRFmod.side.isServer ()) {         // TE follows pattern of proxy common/client
      te = new BlowerTE ();
    } else {                                    // client-side
      ClassLoader mcl = Loader.instance ().getModClassLoader ();
      try {                                     // Reflection
        Class c = Class.forName (InventionsMod.MODID + ".BlowerTEClient", true, mcl);
        te = (BlowerTE) c.newInstance ();
      } catch (Exception e) {
        System.out.println ("BlockBlower failed to instantiate its Client TE because " + e.toString ());
        throw new LoaderException (e);
      }
    }
    return te;
  }
}

 

Server (common) tile entity:

public class BlowerTE extends TileEntity {

  protected static final String mod = InventionsMod.MODID;
  protected float speed = 0.0f;             // float used as pitch

//  public BlowerTE() {}

  public void setPos(BlockPos pos) {        // Vanilla calls immediately after construction
    super.setPos (pos);                     // Set TE pos
    this.pos.add (0.5f, 0.5f, 0.5f);        // Our purpose is sound, so center the floats
  }

  protected void playSFX(World w, String sndName) {
    w.playSoundEffect (pos.getX (), pos.getY (), pos.getZ (), mod + ':' + sndName, 1.0f, this.speed);
  }

  public void setSpeed(int speed) {
    this.speed = speed;                     // Integer converts to float
  }

  public void pwrUp(World w, int speed) {
    this.setSpeed (speed);                  // First set speed
    playSFX (w, "fanPwrUp");                // Then play extra sound effect
  }

  public void pwrDn(World w) {
    playSFX (w, "fanPwrDn");                // Sound
    this.setSpeed (0);                   // then zero
  }

  public void choke(World w) {
    playSFX (w, "fanChoke");                // First play sound
    this.pwrDn (w);                         // Then power down
  }

  @Override
  public void writeToNBT(NBTTagCompound compound) {
    super.writeToNBT (compound);
    compound.setFloat ("speed", speed);
  }

  @Override
  public void readFromNBT(NBTTagCompound compound) {
    super.readFromNBT (compound);
    this.speed = compound.getFloat ("speed");
  }
}

 

Client tile entity:

@SideOnly(Side.CLIENT)
public class BlowerTEClient extends BlowerTE {

// protected WorldClient w;
  static final SoundHandler sh = Minecraft.getMinecraft ().getSoundHandler ();
  private final PositionedSoundFan snd;
  private boolean playing = false;

  public BlowerTEClient() {
    snd = new PositionedSoundFan (this);       // Snd holds "this" to follow speed changes and eventual removal
  }

  public void setPos(BlockPos pos) {                // Vanilla calls immediately after construction
    super.setPos (pos);                             // Set TE pos
    snd.setPos ();                                  // Sound copies TE position
    if (!playing) {
      sh.playSound (snd);                           // Activate our tickable sound (at pos), which will update itself
      snd.update ();                                // Immediately adjust to fan speed
      playing = true;
    }
  }
}

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

@jeffryfisher I play lots of sounds in my mod and I've NEVER had to do anything nearly as complicated as you are making it to get them working. As diesiben and I have both said: use your proxy.

 

Checking if the world is remote is usually sufficient, even when calling @SideOnly(Side.CLIENT) methods from within your class. If you try to create a reference to a client-only class, however, that's usually where you run into problems, and in your case it's the reference to the SoundHandler* that is ruining your day. Put it in your proxy, and then combine your TileEntity classes into one.

 

* SideOnly classes cannot be inline-instantiated unless as members of a class that is only present on that side, i.e. also has the @SideOnly annotation. By instantiating it inline, you force the code to run on both sides, causing it to fail on the one where the class isn't present.

 

This is the same issue people had when declaring multiple IIcons for their items and blocks, e.g. IIcon[] icons = new IIcon[4]; causes crashes, but just declaring it WITH the annotation, and then initializing it in the client-side only registerIcons method, works fine, and no one needed to create separate client- and server- versions of their Blocks and Items.

Link to comment
Share on other sites

The JVM will try to load all classes it needs, and if a class is referenced at all inside a method and that method is executed, that class will be loaded. Hence @SidedProxy exists...

And that reflective class loading crap is horrible. Please, for the love of god, just make a call into your proxy for things like this.

 

That reflective class loading "crap" is only what @SidedProxy does in the background for common/client proxies. It was even crappier in an earlier version that had a BlockPos parameter in the constructor.

 

I'll look into the distinction between "side" and "isRemote". I've seen it mentioned in many threads, so it must be a common cause of confusion.

 

I'm already planning an upgrade to my abstract mod classes, so I'll see about adding a generalized method to start playing a custom PositionedSound.

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

If you try to create a reference to a client-only class, however, that's usually where you run into problems, and in your case it's the reference to the SoundHandler* that is ruining your day.

 

That, and one of my sounds extends the side-only PositionedSound class in order to create a repeatable, continuous sound (I was told to imitate the minecart). PositionedSound is essentially a renderer for sound. I'm glad I only needed to set a couple predefined fields. After taking a peek lower down, I hope I never feel a need to dive in.

 

Someday, in a perfect future, the World class will have a method for starting a continuous sound by passing in its resource name and the TileEntity (containing position) to which it is anchored. Vanilla code and its messaging will take care of the rest.

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

I'll look into the distinction between "side" and "isRemote". I've seen it mentioned in many threads, so it must be a common cause of confusion.

 

isRemote (a property) is used when you're performing logic updates, where the server has precedence and sends updates to the client.

 

side (an annotation) is used when you want to remove a method, class, or field entirely when it is loaded on the other side.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I am creating a mod that adds new armor sets and changes how armor sets are crafted. When the player interacts with the block it fails to open the gui, but I get no error or stacktrace. All of my registration classes are being called correctly. LightArmorBenchScreen's constructor, render, and renderbg methods are not being called at all. Here is the code: package net.leeveygames.celestial.blocks.custom; import net.leeveygames.celestial.Celestial; import net.leeveygames.celestial.blocks.entity.CelestialBlockEntities; import net.leeveygames.celestial.blocks.entity.LightArmorBenchBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraftforge.network.NetworkHooks; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class LightArmorBenchBlock extends BaseEntityBlock { public static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 12, 16); public LightArmorBenchBlock(Properties pProperties) { super(pProperties); } @Override public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { return SHAPE; } @Override public RenderShape getRenderShape(BlockState pState) { return RenderShape.MODEL; } @Override public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) { if (pState.getBlock() != pNewState.getBlock()) { BlockEntity blockEntity = pLevel.getBlockEntity(pPos); if (blockEntity instanceof LightArmorBenchBlockEntity) { ((LightArmorBenchBlockEntity) blockEntity).drops(); } } super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving); } @NotNull @Override public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHit) { if(pLevel.isClientSide()) return InteractionResult.SUCCESS; BlockEntity be = pLevel.getBlockEntity(pPos); if (be instanceof LightArmorBenchBlockEntity blockEntity) { ServerPlayer player = (ServerPlayer)pPlayer; Celestial.LOGGER.info("Opening Screen."); NetworkHooks.openScreen(player, blockEntity, pPos); } return InteractionResult.CONSUME; } @Nullable @Override public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { return new LightArmorBenchBlockEntity(pPos, pState); } @Nullable @Override public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) { if(pLevel.isClientSide()) { return null; } return createTickerHelper(pBlockEntityType, CelestialBlockEntities.LIGHT_ARMOR_BENCH_BLOCK_ENTITY.get(), (pLevel1, pPos, pState1, pBlockEntity) -> pBlockEntity.tick(pLevel1, pPos, pState1)); } }   package net.leeveygames.celestial.screen; import net.leeveygames.celestial.Celestial; import net.leeveygames.celestial.blocks.CelestialBlocks; import net.leeveygames.celestial.blocks.entity.LightArmorBenchBlockEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.*; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.items.SlotItemHandler; public class LightArmorBenchMenu extends AbstractContainerMenu { public final LightArmorBenchBlockEntity blockEntity; private final Level level; public LightArmorBenchMenu(int pContainerId, Inventory inv, FriendlyByteBuf extraData) { this(pContainerId, inv, inv.player.level().getBlockEntity(extraData.readBlockPos())); Celestial.LOGGER.info("Creating Light Armor Bench Menu. Client"); } public LightArmorBenchMenu(int pContainerId, Inventory inv, BlockEntity entity) { super(CelestialMenuTypes.LIGHT_ARMOR_BENCH_MENU.get(), pContainerId); Celestial.LOGGER.info("Creating Light Armor Bench Menu. Server"); checkContainerSize(inv, 2); blockEntity = ((LightArmorBenchBlockEntity) entity); this.level = inv.player.level(); addPlayerInventory(inv); addPlayerHotbar(inv); createLightArmorBenchSlots(blockEntity); } private void createLightArmorBenchSlots(LightArmorBenchBlockEntity blockEntity) { Celestial.LOGGER.info("Creating Light Armor Bench Slots."); this.blockEntity.getCapability(ForgeCapabilities.ITEM_HANDLER).ifPresent(iItemHandler -> { this.addSlot(new SlotItemHandler(iItemHandler, 0, 11, 15)); this.addSlot(new SlotItemHandler(iItemHandler, 1, 82, 33)); this.addSlot(new SlotItemHandler(iItemHandler, 2, 133, 33)); }); } // CREDIT GOES TO: diesieben07 | https://github.com/diesieben07/SevenCommons // must assign a slot number to each of the slots used by the GUI. // For this container, we can see both the tile inventory's slots as well as the player inventory slots and the hotbar. // Each time we add a Slot to the container, it automatically increases the slotIndex, which means // 0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 - 8) // 9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35) // 36 - 44 = TileInventory slots, which map to our TileEntity slot numbers 0 - 8) private static final int HOTBAR_SLOT_COUNT = 9; private static final int PLAYER_INVENTORY_ROW_COUNT = 3; private static final int PLAYER_INVENTORY_COLUMN_COUNT = 9; private static final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT; private static final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT; private static final int VANILLA_FIRST_SLOT_INDEX = 0; private static final int TE_INVENTORY_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT; // THIS YOU HAVE TO DEFINE! private static final int TE_INVENTORY_SLOT_COUNT = 2; // must be the number of slots you have! @Override public ItemStack quickMoveStack(Player playerIn, int pIndex) { Slot sourceSlot = slots.get(pIndex); if (sourceSlot == null || !sourceSlot.hasItem()) return ItemStack.EMPTY; //EMPTY_ITEM ItemStack sourceStack = sourceSlot.getItem(); ItemStack copyOfSourceStack = sourceStack.copy(); // Check if the slot clicked is one of the vanilla container slots if (pIndex < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) { // This is a vanilla container slot so merge the stack into the tile inventory if (!moveItemStackTo(sourceStack, TE_INVENTORY_FIRST_SLOT_INDEX, TE_INVENTORY_FIRST_SLOT_INDEX + TE_INVENTORY_SLOT_COUNT, false)) { return ItemStack.EMPTY; // EMPTY_ITEM } } else if (pIndex < TE_INVENTORY_FIRST_SLOT_INDEX + TE_INVENTORY_SLOT_COUNT) { // This is a TE slot so merge the stack into the players inventory if (!moveItemStackTo(sourceStack, VANILLA_FIRST_SLOT_INDEX, VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT, false)) { return ItemStack.EMPTY; } } else { System.out.println("Invalid slotIndex:" + pIndex); return ItemStack.EMPTY; } // If stack size == 0 (the entire stack was moved) set slot contents to null if (sourceStack.getCount() == 0) { sourceSlot.set(ItemStack.EMPTY); } else { sourceSlot.setChanged(); } sourceSlot.onTake(playerIn, sourceStack); return copyOfSourceStack; } @Override public boolean stillValid(Player pPlayer) { return stillValid(ContainerLevelAccess.create(level, blockEntity.getBlockPos()), pPlayer, CelestialBlocks.LIGHT_ARMOR_BENCH.get()); } private void addPlayerInventory(Inventory playerInventory) { Celestial.LOGGER.info("Creating Player Inventory."); for (int i = 0; i < 3; ++i) { for (int l = 0; l < 9; ++l) { this.addSlot(new Slot(playerInventory, l + i * 9 + 9, 8 + l * 18, 84 + i * 18)); } } } private void addPlayerHotbar(Inventory playerInventory) { Celestial.LOGGER.info("Creating Player Hotbar."); for (int i = 0; i < 9; ++i) { this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142)); } } }   package net.leeveygames.celestial.screen; import com.mojang.blaze3d.systems.RenderSystem; import net.leeveygames.celestial.Celestial; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; public class LightArmorBenchScreen extends AbstractContainerScreen<LightArmorBenchMenu> { private static final ResourceLocation TEXTURE = new ResourceLocation(Celestial.MOD_ID, "textures/gui/LightArmorBenchGUI.png"); public LightArmorBenchScreen(LightArmorBenchMenu pMenu, Inventory pPlayerInventory, Component pTitle) { super(pMenu, pPlayerInventory, pTitle); Celestial.LOGGER.info("Creating Light Armor Bench Screen."); this.imageWidth = 176; this.imageHeight = 166; } @Override protected void init() { Celestial.LOGGER.info("Screen init."); super.init(); } @Override protected void renderBg(GuiGraphics guiGraphics, float pPartialTick, int pMouseX, int pMouseY) { Celestial.LOGGER.info("Render Background Method."); RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); RenderSystem.setShaderTexture(0, TEXTURE); guiGraphics.blit(TEXTURE, this.leftPos, this.topPos, 0, 0, this.imageWidth, this.imageHeight); } @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) { Celestial.LOGGER.info("Render Method."); renderBackground(guiGraphics); super.render(guiGraphics, mouseX, mouseY, delta); renderTooltip(guiGraphics, mouseX, mouseY); } }   package net.leeveygames.celestial.blocks.entity; import net.leeveygames.celestial.screen.LightArmorBenchMenu; import net.minecraft.client.gui.screens.inventory.FurnaceScreen; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.Container; import net.minecraft.world.Containers; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.inventory.FurnaceMenu; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemStackHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class LightArmorBenchBlockEntity extends BlockEntity implements MenuProvider { private final ItemStackHandler itemHandler = new ItemStackHandler(3) { @Override protected void onContentsChanged(int slot) { super.onContentsChanged(slot); LightArmorBenchBlockEntity.this.setChanged(); } }; private static final int INPUT_SLOT = 0; private static final int OUTPUT_SLOT = 1; private LazyOptional<IItemHandler> lazyItemHandler = LazyOptional.empty(); public LightArmorBenchBlockEntity(BlockPos pPos, BlockState pBlockState) { super(CelestialBlockEntities.LIGHT_ARMOR_BENCH_BLOCK_ENTITY.get(), pPos, pBlockState); } @Override public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) { if(cap == ForgeCapabilities.ITEM_HANDLER) { return lazyItemHandler.cast(); } return super.getCapability(cap, side); } @Override public void onLoad() { super.onLoad(); lazyItemHandler = LazyOptional.of(() -> itemHandler); } @Override public void invalidateCaps() { super.invalidateCaps(); lazyItemHandler.invalidate(); } public void drops() { SimpleContainer inventory = new SimpleContainer(itemHandler.getSlots()); for(int i = 0; i < itemHandler.getSlots(); i++) { inventory.setItem(i, itemHandler.getStackInSlot(i)); } Containers.dropContents(this.level, this.worldPosition, inventory); } @Override public Component getDisplayName() { return Component.translatable("block.celestial.light_armor_bench"); } @Nullable @Override public AbstractContainerMenu createMenu(int pContainerId, Inventory pPlayerInventory, Player pPlayer) { return new LightArmorBenchMenu(pContainerId, pPlayerInventory, this); } @Override protected void saveAdditional(CompoundTag pTag) { pTag.put("inventory", itemHandler.serializeNBT()); super.saveAdditional(pTag); } @Override public void load(CompoundTag pTag) { super.load(pTag); itemHandler.deserializeNBT(pTag.getCompound("inventory")); } public void tick(Level pLevel, BlockPos pPos, BlockState pState) { } }   Here is the console output: https://pastebin.com/krgExnYT
    • I couldn't find much usage, I found this and attempted to implement it. It doesn't seem to do anything though. I have this linked to a server to client packet... I have been at this since September seventh and have not made any progress. Blockpos and Blockstate are all pre-assigned variables, as is pLevel. I took the vertex consumer directly from LevelRenderer where I got this code from anyways. I didn't know what number to put into the ModelBakery.DESTROY_TYPES.get() so I just put the breakTime I used in the above code. Am I missing something?
    • Problematic part: [net.minecraft.server.network.ServerGamePacketListenerImpl/]: Gu57 lost connection: Internal Exception: io.netty.handler.codec.EncoderException: java.lang.NullPointerException: Cannot invoke "net.minecraft.resources.ResourceLocation.toString()" because "p_130086_" is null There is no crash, single player works, sadly it's not Hexerei Debug log: (can't use pastebin, cause 10 mb) https://drive.google.com/file/d/16ikQIx7nYjJtF4aJLy5KqFQHh6-jBNrd/view?usp=sharing
    • Something in the Alex's Caves mod is preventing Distant Horizons from rendering. However, I don't want to get rid of Alex's Caves, as it is a part of my personal modded experience. Can someone please find the issue for me?
  • Topics

  • Who's Online (See full list)

    • There are no registered users currently online
×
×
  • Create New...

Important Information

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