
Lycanus Darkbinder
Members-
Posts
118 -
Joined
-
Last visited
Everything posted by Lycanus Darkbinder
-
It seems like you are doing or understanding something very wrong here. To save a TE put the data you need into the NBTTagCompound passed to writeToNBT and read the data back in readFromNBT. Done. If you need the data on the client, too, you need to sync it with packets. Let me explain what's happening so you can better understand my issue: 1. Click "Single Player" 2. Choose a world and click "Play Selected World" 3. createAndLoadEntity() gets called (TileEntity.java) 4. This calls the default constructor of my TileEntitySmartLight (only here for breakpoint purposes) 5. Then my readFromNBT() gets called which indicates the server is requesting this TileEntity 6. Then WorldRenderer.updateRenderer() checks the chunkcache for a client side TileEntity 7. ChunkCache.getBlockTileEntity() calls chunk.getTileEntity() 8. This leads to BlockSmartLight.createTileEntity() indicating we're creating a client TileEntity 9. BlockSmartLight.getLightValue() gets called which has a parameter of IBlockAccess 10. IBlockAccess.getBlockTileEntity() returns the client TileEntity from #8, not the server TileEntity from #5 So, how do I synchronize the client TileEntity that getLightValue() is asking for when this is happening at world load and there is no function override between steps #5 and steps #6 when the game requests the two TileEntities? It wouldn't be a problem if getLightValue() didn't use IBlockAccess but that always gets a client version of the TileEntity, it never loads a server version. Any advice would be appreciated. I don't see how packets can help the situation but since I've not seen any good documentation on packets I can't really comment. PS: As for the constructor, I kept getting console messages "Ignoring entity with TileEntitySmartLight" until I added the constructor. Once I put it in (even an empty one) the messages stopped.
-
Why that? TileEntity has a field worldObj . Use that. Well for one, worldObj is not available while in the constructor. All properties from the TileEntity super class are NULL while in the constructor. My constructor accepts a boolean. Of course. Why should the client save any NBT data? The server is the side that manages the game, saves the world, etc. The client only has dummy values for everything. Think of a dedicated server (connect via IP): Where should the client save the world to if it would? On a singleplayer world, the chunkcache attempts to load the TileEntity for the client but fails so it builds a new one. After the server TileEntity is loaded with readFromNBT, the call stack looks like this: The bolded part is where it falls down because the TileEntity is not saved so it builds a brand new one. Here's the piece from Chunk.getChunkBlockTileEntity if (tileentity == null) { tileentity = Block.blocksList[l].createTileEntity(this.worldObj, meta); this.worldObj.setBlockTileEntity(this.xPosition * 16 + par1, par2, this.zPosition * 16 + par3, tileentity); } In the above code, this.worldObj is a WorldClient. Why is the client expecting chunkcache to have a TileEntity if, as you say, they're not supposed to be saved? More importantly, could you please point me in the direction of some up-to-date documentation on how people save and load TileEntities? My TileEntity isn't even that fancy but it seems like it's a lot more work than advertised in the wiki.
-
Well to be fair the package doesn't necessarily mean it's not forge. Take a look at net.minecraft.block.block and you'll see a giant comment called "FORGE START". Thanks for this tip. I was implementing ITileEntityProvider based on some research on the wiki and other sources. I guess things are a bit outdated now.
-
I have TileEntity with a field called isRemote that gets set in onBlockPlacedBy() based on world.isRemote. When a block is placed, two TileEntity are created and the fields set to true and false. When I press ESC, writeToNBT() gets called only once and isRemote is false indicating this is the server TileEntity. It seems that the TileEntity where isRemote is true never gets a call to writeToNBT(). I print to the console the value of isRemote every time the function is called and it never prints "true".
-
After following this through the debugger, the bottome line is when IBlockAccess calls World.getBlockTileEntity() the "world" is always a WorldClient according to the "variables" window in Eclipse. I guess the only workaround would be to make sure you're always interacting with a client TileEntity. The problem here is some methods such as updateTick are server only so the World parameter that is passed in the function definition is never a WorldClient.
-
Well while researching more on my issue from my thread, I realized that when you load a world with blocks already placed, updateTick gets called right away which means the cached World objects are NULL causing a crash. Perhaps that's what you've encountered? I'm still working on a better resolution.
-
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Well unfortunately caching the World mirrors doesn't work. When you reload the server with blocks already placed then updateTick attempts to access the cached World while it is still NULL, causing a crash. -
I don't know if ITileEntityProvider is a Forge implementation or not but just in case it is.... Suggestion: Implement createTileEntity() in ITileEntityProvider to use the metadata value as provided by the call to createTileEntity() in Block.java Details: In Block.java, under the heading "Forge Start" we find an implementation of createTileEntity: public TileEntity createTileEntity(World world, int metadata) { if (isTileProvider) { return ((ITileEntityProvider)this).createNewTileEntity(world); } return null; } which in turn calls to ITileEntityProvider, the actual implementation that gets overridden by modders. Unfortunately it neglects to pass the metadata value through to ITileEntityProvider thereby making this value unavailable. The usefulness in having it is that the value can be passed into the TileEntity through its constructor (none of the TileEntity inherited properties: worldObj, xCoord, blockMetadata, etc. are available for use in the constructor) which would in turn allow modders some flexibility to make rudamentary decisions on how the TileEntity would be initialized.
-
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Are you using a custom packet handler? I haven't done that yet, just started reading about it. Anyway, there is another thread recently posted where someone realized that an IBlockAccess TileEntity is different than a World TileEntity. The main problem is that functions which use a World object instead of an IBlockAccess object simply don't act on the same TileEntity. I traced through the code in Chunk.java that actually returns the TileEntity and interestingly they both use the same function but it does answer a few IF...THEN questions differently depending on how it was called. -
Indeed, I just puzzled this out in my getLightValue() thread. Short version: the TileEntity from World is a server-side one while IBlockAccess is client-side. You can look at my explanation of what's happening as well as a possible workaround. http://www.minecraftforge.net/forum/index.php/topic,12125.msg63300.html#msg63300
-
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Though this isn't very elegant, it fixes the problem: public class BlockSmartLight extends Block implements ITileEntityProvider { // World mirrors for TileEntity retrieval functions to ensure we're // always getting the same TileEntity. IBlockAccess usually only works // with CLIENT versions while "World" typically works with server versions World worldCli; World worldSrv; @Override public void updateTick(World world, int x, int y, int z, Random random) { // Update both TileEntities so the one retrieved by IBlockAccess // in getLightValue() will be current TileEntitySmartLight teCli = (TileEntitySmartLight)worldCli.getBlockTileEntity(x, y, z); TileEntitySmartLight teSrv = (TileEntitySmartLight)worldSrv.getBlockTileEntity(x, y, z); boolean newIsLit = isOnTime(world, x, y, z); if (teCli != null) teCli.setIsLit(newIsLit); if (teSrv != null) teSrv.setIsLit(newIsLit); world.markBlockForUpdate(x, y, z); world.scheduleBlockUpdate(x, y, z, this.blockID, SmartLights.getConfig().getTickRate()); } @Override public TileEntity createNewTileEntity(World world) { // Initialize the WORLD mirrors if necessary. if (world.isRemote) { if (worldCli == null) worldCli = world; } else { if (worldSrv == null) worldSrv = world; } return new TileEntitySmartLight(); } } By using World mirrors in updateTick() to force update both TileEntities, it allows the TileEntity retrieved by the IBlockAccess in getLightValue() to behave properly. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
I think I figured out the problem: IBlockAccess in getLightValue() always gets a client version of the TileEntity, it never gets a server version and World in updateTick() always gets a server version so the two TileEntities are not the same. Illustration: @Override public void onBlockPlacedBy(World world, int x, int y, int z, EntityLiving entityLiving, ItemStack itemStack) { // Initialize the TileEntity TileEntitySmartLight te = (TileEntitySmartLight)world.getBlockTileEntity(x, y, z); if (te != null) { te.Init(); if (world.isRemote) te.setIsServer(false); else te.setIsServer(true); } } @Override public void updateTick(World world, int x, int y, int z, Random random) { TileEntitySmartLight te = (TileEntitySmartLight)world.getBlockTileEntity(x, y, z); if (te != null) { // Always TRUE (server) SmartLights.getDebugger().PrintToConsole("updateTick(" + x + ", " + y + ", " + z + ") TileEntity.isServer = " + te.isServer()); } } @Override public int getLightValue(IBlockAccess iba, int x, int y, int z) { TileEntitySmartLight te = (TileEntitySmartLight)iba.getBlockTileEntity(x, y, z); if (te != null) { // Always FALSE (client) SmartLights.getDebugger().PrintToConsole("getLightValue(" + x + ", " + y + ", " + z + ") TileEntity.isServer = " + te.isServer()); } } so this indicates that when updateTick() modifies the TileEntity, it is not modifying the one that getLightValue() is going to use. -
If you're overriding the base function then it shouldn't get called unless you do something like super.keyDown(). Minecraft must have some other listener active to get the keypress.
-
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
@Mazetar The problem with that is setBlock() causes a call to getLightValue() but at that time the TileEntity has been reset because of a call to removeTileEntity() somewhere in the chain. That means that copying the tileEntity back after setBlock() doesn't work unless you can trigger a call to getLightValue() which is my problem. @Moritz If that's from RedPower, it's from Minecraft 1.4.6 and does not apply to 1.5.2. There is no updateBlock() to override in TileEntity. @GotoLink No, getLightValueInt() is returning the same for both sides. What I did notice however is that sometimes in getLightValue() the getBlockTileEntity occasionally returns a NULL TileEntity. The big roadblock here is how to tell Minecraft that the block has changed to trigger a call to getLightValue() without actually changing it by destroying the block and tileentity @Anyone who's interested If you follow the call stack after calling setBlock() there are a bunch of private methods that trigger light recalculations. I wonder, if I create a class to extend World and override setBlock() if that may help. I could copy those private methods and just tell it to ignore the call to breakBlock() and removeTileEntity(). -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Ok, so I tested getLightValue() using this: public int getLightValue(IBlockAccess iba, int x, int y, int z) { Random rand = new Random(); int Low = 1; int High = 15; int val = rand.nextInt(High-Low) + Low; return val; } which makes the individual sides of the block strobe on and off at varying intensity which in itself is kinda neat. The problem is the only way I could trigger it was setBlock() which as mentioned above destroys and recreates the TileEntity thereby losing all the stored info. I've ripped all sorts of functions out of setBlock() and tried to override them but to no avail. I even tried overriding shouldRefresh() in TileEntity which can return FALSE to prevent destroying the TileEntity but it never got called. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Still no luck. I took a look at what setBlock() did because it always refreshes the light and pulled these functions out and stuck them in updateTick(): updateAllLightTypes() markBlockForUpdate() notifyBlockChange() The last call actually ends up calling notifyBlocksOfNeighborChange() but for whatever reason this still had no effect. Unfortunately setBlock() destroys the TileEntity and copying it and calling setBlockTileEntity() after doesn't work because the TileEntity starts off with no light value and it also doesn't trigger getLightValue(). -
None of the "onBlock..." functions are called during chunk generation, to do that you need to find and override the chunk generation code to insert your block. This is no easy task. For reference, this is the order in which the functions are called when you place a block: onBlockPlaced() client onBlockPlacedBy() client onBlockPlaced() server onBlockAdded() server onBlockPlacedBy() server The "client" labels indicate that world.isRemote returned TRUE. Again, none of these functions are called during chunk generation.
-
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
That's the thing, it does recalculate the lighting. The function getLightValue() is called from world.computeLightValue(): int blockLight = (block == null ? 0 : block.getLightValue(this, par1, par2, par3)); the debugger shows blockLight is 14 regardless of how I return from getLightValue(). Everything is identical except for the fact that it isn't visually updating for the player. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
@Mazetar Thanks for that but unfortunately it doesn't remedy my problem. I had already used Eclipse to step through what's happening and watched Minecraft calculate the light value: When I return 14 from my tile entity the calculated light value is not updated in the world. When I return 14 from super.getLightValue() the calculated light value is updated in the world. It doesn't make sense to me. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Unfortunately neither change made any difference. Since markBlockForUpdate() is not available from IBlockAccess, I tried putting it in updateTick() but there was no effect. The only way to get any light at all was to return the result from calling super.getLightValue(). That's the part I don't understand. My TileEntity returns 14 which is the same as the result from calling super.getLightValue() but it does nothing. I even took out all checks such as isLit() and simply had it return the value from TileEntity. Note: I also tried world.markBlockForRenderUpdate() because the comments say it is used for lighting changes but it also had no effect. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
@MineMaarten, @GotoLink Thanks for your suggestions. I'll make some changes and post back the results. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Hmm, no. I always use the isRemote check before interacting with the TileEntity. I was under the impression that only GUI related stuff needed to be done client side because the server would handle sending block information and lighting to all players in the chunk. So, for example, when onBlockActivated() gets called, I only set the isLit property after checking !world.isRemote. Admittedly I watched several tutorials on YouTube and checked the Forge wiki but most information seems outdated. I pieced together what I thought would work. -
[Solved] Overriding getLightValue() has no effect
Lycanus Darkbinder replied to Lycanus Darkbinder's topic in Modder Support
Crap, I made a mistake in my OP. I was thinking of getIcon() for some reason when I wrote the post. I am in fact using the getLightValue() as you suggested. The tile entity returns 4,5,6, etc. depending on the internal value it has but when getLightValue passes that back to the super class, nothing happens. It goes through a bunch of calculations but the light value in the world never changes. I edited the OP accordingly. @Override public int getLightValue(IBlockAccess iba, int x, int y, int z) { // TODO // Figure out why the return result has no effect on lighting // for some reason the light value stays at 10 where it started TileEntitySmartLight te = getTileEntity(iba, x, y, z); if (te != null) { if (te.getIsLit()) return te.getCurrentLightValInt(); // no matter the value, world light never changes else return 0; } else { // For some reason we can't get a valid TileEntity, // let the super class deal with the light value return super.getLightValue(iba, x, y, z); } } // Support functions to reduce typing private TileEntitySmartLight getTileEntity(IBlockAccess iba, int x, int y, int z) { // Returns a TileEntitySmartLight retrieved by a IBlockAccess object // Grab a generic tileentity TileEntity te = iba.getBlockTileEntity(x, y, z); // Check if it is the proper type and cast it for the return // otherwise return NULL if (te instanceof TileEntitySmartLight) return (TileEntitySmartLight)te; else return null; } private TileEntitySmartLight getTileEntity(World world, int x, int y, int z) { // Returns a TileEntitySmartLight retrieved by a WORLD object // Grab a generic tileentity TileEntity te = world.getBlockTileEntity(x, y, z); // Check if it is the proper type and cast it for the return // otherwise return NULL if (te instanceof TileEntitySmartLight) return (TileEntitySmartLight)te; else return null; } -
Greetings, I have a TileEntity with two methods: int getCurrentLightVal() void setCurrentLightVal(int val); the setter is called in onBlockActivated and sets the new light value, rolling over to 4 if the value is greater than 15 (ie: the light value will always be between 4 and 15). My problem is that in my block's override of getLightValue(), returning getCurrentLightValue() from the TileEntity does not actually cause the light in the world to change. In fact the block does not light up at all. If I remove the TileEntity and create a separate "lit" block and use setBlock() to swap between the two, the lighting changes properly but naturally does not save state between sessions. Is there something I'm missing in regards to getLightValue()? It seems pretty straight forward to me. I stepped through the code in the debugger and fail to see why this is not working even though the function is returning the proper value.