Jump to content

[1.8] Custom p-plate does not see POWERED state on client


jeffryfisher

Recommended Posts

My block extends a vanilla block (pressure plate), adding a property. When the server (!remote) updates the state, setting both properties, only my own property changes client-side. The vanilla property is always stuck at its default/base value on the client.

 

I have server side print statements telling me that the state being set for the update method is what I want. Where do I look next to see where the vanilla property is being clobbered on its way to the client?

 

My Block class:

public abstract class classSmartPPlate extends BlockPressurePlate {

  public static final PropertyInteger SIGNAL = PropertyInteger.create ("signal", 0, 15);

  private Block keyIngredient = null;                   // Each child class must set keyIngredient
  protected classSelector selector = null;              // Child class should allocate implementation of this interface

  public classSmartPPlate(String plateName) {
    super (Material.circuits, Sensitivity.MOBS);
    this.setUnlocalizedName (plateName);                // Used in regBlock
    this.setStepSound (soundTypeMetal);
    classSensorMod.regBlock (this);
    children.add (Block.getIdFromBlock (this));         // Same function used to make automatic ItemBlock in item registry
    keyIngredient = this.defineKeyIngredient ();
  }

  /**
   * SIGNAL is the metadata value that becomes redstone strength via
   *    abstract methods that insist on using block states instead of raw metadata.
   *    
   * POWERED is the derived boolean that controls rendering. 
   */
  protected BlockState createBlockState() {
    return new BlockState (this, new IProperty[] { POWERED, SIGNAL });
  }

  /**
   * Convert the given metadata into a BlockState for this Block.
   * The super's simple boolean and our own finer signal are each derived independently from the same meta.
   */
  @Override
  public IBlockState getStateFromMeta(int meta) {
    return this.getDefaultState ()                          // We can't use super because of its "== 1"
        .withProperty (POWERED, Boolean.valueOf (meta > 0)) // So we write the inequality.
        .withProperty (SIGNAL, Integer.valueOf (meta));
  }

  /**
   * Convert the BlockState into the correct metadata value.
   * We ignore super.POWER and simply return our own SIGNAL from which it is derived.
   */
  @Override
  public int getMetaFromState(IBlockState state) {
    return ((Integer) state.getValue (SIGNAL)).intValue ();
  }

  /**
   * Convert internal detection signal to external redstone signal. Our parent tries to rectify all
   * detections into +15 redstone. We don' want that, so we must override both func_150060_c &
   * func_150066_d to pass signal unchanged.
   */
  @Override
  protected int getRedstoneStrength(IBlockState state) {
    return ((Integer) state.getValue (SIGNAL)).intValue ();
  }

  /**
   * This should be named setBlockState.
   * In this method, super is smart enough to use "> 0", so
   * Let super do its POWERED before we append our SIGNAL.
   */
  @Override
  protected IBlockState setRedstoneStrength(IBlockState state, int signal) {
    IBlockState intermediate = super.setRedstoneStrength (state, signal);
    System.out.println (String.format ("Input signal is %d", signal));      // TODO: Remove debug print
    System.out.println (String.format ("    Intermediate POWERED = %s", intermediate.getValue (POWERED).toString ()));

    IBlockState result = intermediate.withProperty (SIGNAL, Integer.valueOf (signal));
    System.out.println (String.format ("  Result POWERED = %s\n", result.getValue (POWERED).toString ()));

    return result;
  }

  protected abstract Block defineKeyIngredient();       // Each child class must define its keyIngredient

   @Override
  public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn) {
    if (!worldIn.isRemote) {
      int old = this.getRedstoneStrength (state);
      this.updateState (worldIn, pos, state, old);  // Always update, because diff entities have diff strengths
    }
  }

  protected abstract ArrayList<Entity> getEntityList(World w, AxisAlignedBB aabb) ;

  /**
  * Returns the signal level of one entity. If multiple applicable entities share a plate,
  * the strongest signal among them will supersede the rest, so sort values accordingly.
  */
  protected abstract int getSignal(Entity e);

  @Override
  protected int computeRedstoneStrength(World w, BlockPos pos) {
    int signal = 0;
    ArrayList<Entity> list = this.getEntityList (w, this.getSensitiveAABB (pos)); // Detect our entities

    for (Entity e : list) {
      if (!e.doesEntityNotTriggerPressurePlate ()) {               // Look at each mob we can detect (we're not counting)
        signal = Math.max (signal, this.getSignal (e));            // Set signal to strongest one mob on our plate
      }
    }
    return signal;
  }

}

 

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

Without overthinking - you have too many states.

I mean. 0-15 for your signal, and where goes the boolean POWERED?

 

Lookup BlockPressurePlateWeighted which uses POWER to measure power strengh from 0-15. It uses 0 value as replacement for POWERED=false.

 

This is probably why client can't decode it. Idk why server says otherwise.

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

I mean. 0-15 for your signal, and where goes the boolean POWERED?

 

  @Override
  public IBlockState getStateFromMeta(int meta) {
    return this.getDefaultState ()                          // We can't use super because of its "== 1"
        .withProperty (POWERED, Boolean.valueOf (meta > 0)) // So we write the inequality.
        .withProperty (SIGNAL, Integer.valueOf (meta));
  }

 

He's already doing what you said.

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

Right: Signal is meta, and meta is signal.

 

"Powered" is purely derivitive from signal. If the metadata value made it to the client side, then my own meta-to-blockstate method should assign both properties.

 

However, there must be something else going on... The client thread never even hits my breakpoint in that method. I don't know much about client-server comm, but I suspect that the properties are moving via something other than metadata. Might I need NBT coding and decoding? Argh, what I think I know about blocks, tile entities, items etc is getting all mixed up.

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

To clarify: On the client, the POWERED property remains false while it is true server-side. Is my mod responsible for some message call to sync the sides?

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

To clarify: On the client, the POWERED property remains false while it is true server-side. Is my mod responsible for some message call to sync the sides?

 

When the server sends a block change to the client, the

IBlockState

is serialised to its ID in the packet. The ID of a state is derived from the

Block

's ID and the metadata for that state.

 

If a property doesn't affect metadata, its value won't be synced to clients.

 

If your block requires a property that's not stored in the metadata (e.g. the property is stored in a

TileEntity

, derived from the other properties or derived from surrounding blocks) to render properly, override

Block#getActualState

to return an

IBlockState

with that property set.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

Interesting... So if one property is all that matters on the server, and another is derived to control rendering, then I need this "actual" method in order to force the render-control property to transmit?

 

I'll give it go as soon as I get a break from tax season...

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

Well I'll be hog-tied! Here I thought the meta value would be passed from server to client where getStateFromMeta would be called. I programmed that method to handle this exact situation.

 

Adding an override of getActualState does the trick. My p-plate was already able to produce its redstone power on the server. Now it visually sinks 1/16 of a block when activated.

 

Here's my seemingly paradoxical code for getActualState:

 

  @Override
  public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos) {
    return getStateFromMeta (getMetaFromState (state));     // Believe it or not, this is NOT a no-op!
  }

 

:o

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

What's horrible is that getActualState is somehow being called with a state that's NOT the product of my own class's getStateFromMeta. State arrives having only one of its properties set (Choonster mentioned "serialization", which I did not fully understand). My getActual method converts back to meta so it can use my own meta-to-state method to make the complete state that I had expected in the first place.

 

Here's my state def and the conversion methods:

 

  public static final PropertyInteger SIGNAL = PropertyInteger.create ("signal", 0, 15);

  /**
   * SIGNAL is the metadata value that becomes redstone strength via
   *    abstract methods that insist on using block states instead of raw metadata.
   *    
   * POWERED is super-class's derived boolean that controls rendering. 
   */
  @Override
  protected BlockState createBlockState() {
    return new BlockState (this, new IProperty[] { POWERED, SIGNAL });
  }

  /**
   * Convert the given metadata into a BlockState for this Block.
   * The super's simple boolean and our own finer signal are each derived independently from the same meta.
   */
  @Override
  public IBlockState getStateFromMeta(int meta) {
    return this.getDefaultState ()                          // We can't use super because of its "== 1"
        .withProperty (POWERED, Boolean.valueOf (meta > 0)) // So we write the more intelligent inequality.
        .withProperty (SIGNAL, Integer.valueOf (meta));
  }

  /**
   * Convert the BlockState into the correct metadata value.
   * We don't use super.POWERED, simply storing SIGNAL from which POWERED can be derived.
   */
  @Override
  public int getMetaFromState(IBlockState state) {
    return ((Integer) state.getValue (SIGNAL)).intValue ();
  }

 

As you can see, the extended class's SIGNAL property is an integer expansion of the super-class's boolean POWERED property. SIGNAL equates to metadata, and POWERED has become a derived property (is the SIGNAL non-zero?). As described by the comments, the SIGNAL becomes the redstone power on the server, while POWERED controls rendering (p-plate up versus depressed) on the client.

 

The one other factor one might care about is that (in client proxy) SIGNAL is ignored for rendering:

    ...StateMap.Builder ().addPropertiesToIgnore (classSmartPPlate.SIGNAL...

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

(Choonster mentioned "serialization", which I did not fully understand)

 

Minecraft uses Netty to send data between the client and server. Netty doesn't know how to send complex objects like

IBlockState

s over the network and recreate them on the other side, so they need to be broken down into simpler types that Netty does know about (this is a similar concept to storing data in NBT, both are forms of serialisation).

 

In this case Minecraft sends the ID of the

IBlockState

, which is just an

int

derived from the

Block

's ID and the state's metadata.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

The part I don't understand is what happens when Netty's serial arrives on the client. If Netty has a block ID and meta value, then what is it doing instead of calling the block's getStateFromMeta?

 

@D7: My conversions are symmetrical for all valid block states. Because one property depends on the other, POWERED should never be zero when SIGNAL is non-zero (and vice-versa). Maybe I was supposed to take a different approach when I wanted to expand a vanilla boolean into an integer? Should I have junked the vanilla boolean entirely and then written all 16 states in my blockstates.json file, choosing one model for zero and another model for each of the 15 levels of positive signal?

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

You should not be using POWERED then, as in memory you're taking up twice as much room as you need to because you have 'invalid' states in the IBlockState table.

In your renderer you should just have a >0 check. Or, as others have suggested use IUnlistedProperty and fill it in in getActualState.

Beyond that, depending on how you register your class things could get odd with the metadata.

What state does that pass in that is 'invalid'?

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

Link to comment
Share on other sites

The part I don't understand is what happens when Netty's serial arrives on the client. If Netty has a block ID and meta value, then what is it doing instead of calling the block's getStateFromMeta?

 

IBlockState

s are converted to and from their IDs using

Block.BLOCK_STATE_IDS

. This is populated each time a

Block

is registered: The registry iterates through all of the possible states and adds each one to the map using

blockId << 4 | block.getMetaFromState(state)

as the ID. If multiple states share an ID, whichever one was added last will be returned from

ObjectIntIdentityMap#getByValue

.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Link to comment
Share on other sites

You should not be using POWERED then, as in memory you're taking up twice as much room as you need to because you have 'invalid' states in the IBlockState table.

 

Maybe... If my 32 permutations map to only 16 metadata values, would they occupy 16 slots or 32?

 

I'll try to create my blockstate with only SIGNAL, but some of the vanilla code might then break (POWERED runs like a rash through the vanilla p-plate class). Maybe I'll end up extending the p-plate's base class instead. I'll also look at weighted p-plates to see how they manage both redstone strength and on/off rendering. Come to think of it, perhaps that's where I should have started.

 

Beyond that, depending on how you register your class things could get odd with the metadata.

 

So far it's working like a charm, even after closing and reopening the world. There are only 16 possible meta values, so what's the worst that could happen?

 

What state does that pass in that is 'invalid'?

 

On the client side, the getActualState method was being called with a state that had non-zero SIGNAL but false POWERED. Based on Choonster's explanation, the POWERED=false permutation of that signal strength must have been added to the map after its true permutation, clobbering it.

 

I'd ask why Netty uses the mapping instead of calling the state-to-meta and meta-to-state methods, but I hope I never need to know. I vaguely recall something about Netty being a separate thread, so I'll guess that it has something to do with thread-safe programming. In any case, it is what it is, and now I can see how it tripped up the rendering on the client.

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

Maybe... If my 32 permutations map to only 16 metadata values, would they occupy 16 slots or 32?

 

You are creating 32 block states, half of wich are invalid for your game logic (powered=true,signal=0 / powered=false,signal!=0)

 

 

So far it's working like a charm, even after closing and reopening the world. There are only 16 possible meta values, so what's the worst that could happen?

 

Desync :)

 

I'd ask why Netty uses the mapping instead of calling the state-to-meta and meta-to-state methods, but I hope I never need to know. I vaguely recall something about Netty being a separate thread, so I'll guess that it has something to do with thread-safe programming. In any case, it is what it is, and now I can see how it tripped up the rendering on the client.

 

Becasuse what is hitting the wire is the raw chunk data (a block id / metadata hash for each block in the chunk) not the single block IBlockState

 

Link to comment
Share on other sites

Yes, every permutation of every property gets a blockstate created for it thus all your 'invalid' states STILL exist and take up memory.

Even if they are not put into the ID mapping.

Admittedly as BlockState implementation is a small class, that's not much memory for you, wasting maybe ~500 bytes. But waste is waste.

As for why they don't use mTs/sTm in networking. state = MAP[(ID<<16) | meta] is thousands of times faster then state = MAP[iD].mTs(meta)

I do Forge for free, however the servers to run it arn't free, so anything is appreciated.
Consider supporting the team on Patreon

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.