Jump to content

IceMetalPunk

Members
  • Posts

    402
  • Joined

  • Last visited

Posts posted by IceMetalPunk

  1. The ResourceLocation is wrong. You use

    "chaotica:fluids/corrosive_chaos_still.png"
    which points to
    assets/chaotica/fluids/corrosive_chaos_still.png
    The texture is located at
    assets/textures/chaotica/fluids/corrosive_chaos_still.png

    The correct ResourceLocation would be

    "chaotica:textures/fluids/corrosive_chaos_still.png"

     

    *Facepalm* I added "textures", but forgot the extension, then I added the extension and removed "textures"... Thank you! I don't know why I had it in my head that Minecraft would assume parts of the path, such as "textures" and ".png", for ResourceLocations being bound to textures.

     

    Thanks again!

  2. Nope; adding the .png extension didn't help. It's still giving me a FileNotFoundException.

     

    If it helps, this whole project has a GitHub repo, where you can look at both the code and the resource paths: https://github.com/IceMetalPunk/Chaotica (This particular piece of code is in the FluidTankChaos() class's render() method; the resource locations are initialized in the individual fluid classes).

     

    Any ideas? I don't understand how I can be getting a File Not Found exception when the file is right there...unless I'm misunderstanding something about the way Minecraft parses resource locations?

  3. Yes, that color. The thought crossed my mind, but since it never rendered the usual black checkerboard either, even after growing pretty tall, I wasn't sure if that was the case.

     

    But as I said, I've confirmed the ResourceLocation is correct (or at least its string form is correct), and I have the .png texture file in that same location. I'm wondering: does it *have* to be in a standard directory, such as "blocks" or "items"? I was hoping to keep my workspace more organized by creating my own "fluids" directory, figuring that shouldn't be a problem as long as I coded the proper directory, right?

     

    If that's okay, then what else would cause the image not to be found? (Actually, this is an image I'm not assigning as a texture to any block or anything yet; it's just an image file. Could that be the problem? And if so, will I need to create a dummy class to assign the texture to for it to work? That feels very hacky...)

     

    *EDIT* I just realized I'd overlooked an error in my log. It does indeed say the corrosive chaos still texture file is not found. Why would it be unable to find it? Or do I need to specify the "textures" part of the path myself? (I'm about to try that, just brainstorming here.)

     

    *Update: Nope, specifying the "textures" portion of the path did not, in fact, get rid of the File Not Found error...

  4. I have a GUI, and part of it is supposed to render a fluid tank. I'm handling the rendering in the custom fluid tank class itself, and passing the gui instance to it. Everything worked fine when I was rendering the fluid as a solid color, but now I'm trying to make it render the actual fluid's still texture and...it's pink. The fluid texture itself is green and animated (literally a re-color of the vanilla water texture), but the rendered fluid is just solid pink.

     

    This is the relevant code:

     

    			GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
    		GlStateManager.disableBlend();
    		Minecraft.getMinecraft().getTextureManager().bindTexture(tex);
    		GuiUtils.drawTexturedModalRect(left, top, 0, 0, width, renderHeight, zLevel);
    		GlStateManager.enableBlend();

     

    I've drawn some debug text and verified that "tex" is holding the correct ResourceLocation (specifically, "chaotica:fluids/corrosive_chaos_still", with "chaotica" being the domain; the texture is located in "assets/chaotica/textures/fluids/corrosive_chaos_still.png"). The left, top, width, height and zLevel are all correct as well, which makes sense since the rectangle is being drawn in the correct place and the correct size. It's just...pink.

     

    Why is it pink? xD

  5. There are no unsigned types in Java, except maybe char, which doesn't really count.

    Guava has some handy methods in it's UnsignedBytes resp. UnsignedInts classes though.

    Darn. Just when I was starting to really like Java, I learn about its downfalls :P Oh, well, using shorts has fixed the problem and everything works as it should now, even if it takes 16 bits just to get past 127 >_<

     

    Thank you everyone for your help!

  6. I've narrowed down the color problem. I realized my getColor() method was returning -1, so after a lot of breakpoint/stepping debugging, I've determined that my setColor() method is storing negative values in the local color array instead of the positive ones passed in. Is it unsafe in Java to cast an int directly to a byte? (If so, I'm assuming that would be because all ints are signed while, I take it, bytes are unsigned?) If so, what's the accepted way to make that conversion, since Java seems to assume all constant numerical values are ints without a cast? (I'm about to try Byte.valueOf() to see if that helps the implicit cast go more smoothly, but meanwhile, if there's a better way, someone please let me know.)

     

    And if it's not obvious, I'm relatively new to Java programming, having spent most of my time with PHP, GML, JavaScript, and C++, so sometimes these little quirks of Java catch me.

     

    *EDIT* Okay, turns out you can't pass an int to Byte.valueOf(). More importantly, I did a bit of reading, and apparently it's not possible to have an unsigned byte--or any unsigned values--in Java? That's...annoying. So I'm about to (grudgingly) change the array over to shorts to give enough bits to hold the full 0-255 range...hopefully that will work.

     

    Is the article I read wrong, or is there really no way to have an unsigned byte in Java?

  7. try

     

    this.drawRect((this.sizex / 2) - (18/2), top, (this.sizex / 2) + (18/2), top + 60, color);

     

    I had to subtract left from right to get 18, and top from bottom for 60. For small objects like bars and that make sure you store their dims (height width)

     

    Doing that gave me this result:

     

    FluidTankBug2.png

     

    It's still about twice as wide and tall as the pixel values suggest it should be, too low down, and the wrong color. More importantly, other UI's won't have the fluid tank in the center, so I need to know how to make this work with coordinates that aren't calculated from the dimensions of the UI as well.

     

    *EDIT* Well, there you go. I started to wonder if it wasn't just "about" double the size, but if the coordinates were *exactly* double. So I divided all my coordinates by 2, and...the position and size are now perfect! Can anyone explain why I had to divide the pixel values by 2?

     

    So now the only big problem is that the color is still white even though the fluid's getColor should be returning a yellow color--can anyone help m figure that one out?

     

    *EDIT 2* I also forgot setColor isn't a method of Fluid, but of my extension of Fluid, so here's that class so you can see how I'm setting and getting the color:

     

    package com.icemetalpunk.chaotica.fluids;
    
    import net.minecraft.util.ResourceLocation;
    import net.minecraftforge.fluids.Fluid;
    import net.minecraftforge.fluids.FluidRegistry;
    
    public class FluidChaosBase extends Fluid {
    
    protected byte[] color = new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255 };
    
    public FluidChaosBase(String fluidName, ResourceLocation still, ResourceLocation flowing) {
    	super(fluidName, still, flowing);
    	this.setUnlocalizedName(fluidName).setGaseous(false);
    	FluidRegistry.registerFluid(this);
    }
    
    @Override
    public int getColor() {
    	return this.color[0] << 24 | this.color[1] << 16 | this.color[2] << 8 | this.color[3];
    }
    
    public void setColor(byte r, byte g, byte b, byte a) {
    	this.color[0] = r;
    	this.color[1] = g;
    	this.color[2] = b;
    	this.color[3] = a;
    }
    
    public void setColor(int r, int g, int b, int a) {
    	this.color[0] = (byte) r;
    	this.color[1] = (byte) g;
    	this.color[2] = (byte) b;
    	this.color[3] = (byte) a;
    }
    
    }

     

    Have I done something wrong here, or is the problem elsewhere?

  8. I have a class that extends GuiContainer for a custom tile entity of mine. I'm drawing the UI, drawing the container name, etc. and all that works fine. However, I'm now trying to render the fluid in the tank of the tile entity, and the rectangle is being drawn completely wrong, in the wrong location, with the wrong width, and in the wrong color. I've even simplified it to ignore the amount of fluid and just draw a static rectangle, and it's still drawing in the wrong place.

     

    I've done so much research and tried many things, but I can't figure this one out on my own.

     

    Here's the current code I'm using:

     

    	@Override
    protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
    
    	int left = 168, top = 30, right = 186, bottom = 90;
    	FluidTank tank = (FluidTank) (((ICapabilityProvider) this.tileEntity).getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null));
    	int color = 0xFFFFFFFF, amount = 0;
    	if (tank.getFluid().getFluid() != null) {
    		color = tank.getFluid().getFluid().getColor();
    		amount = tank.getFluidAmount();
    	}
    
    	// Draw container title
    	FontRenderer fontRender = Minecraft.getMinecraft().fontRendererObj;
    	String title = new TextComponentTranslation("container.chaotic_condenser").getUnformattedText();
    	fontRender.drawString(title, this.xSize / 2 - fontRender.getStringWidth(title) / 2, 5, 0, false);
    	// Render fluid amount in text
    	fontRender.drawString(amount + " mb", 5, 25, 0, false);
    
    	/* FIXME: Implement proper fluid tank rendering. */
    	this.drawRect(left, top, right, bottom, color);
    
    }

     

    Those coordinates are relative to the GUI's background image, taken directly from Photoshop measurements. The title draws perfectly fine, as does the text showing the amount of fluid (which is also how I know the tank is non-null and properly being read). But the bar...is too far to the right, too wide, and completely white (even though the fluid color is a yellow-ish).

     

    Does drawRect use some weird transformations before drawing or something? I couldn't find anything, but the coordinates are clearly not correct...

  9. ...snip...

    Thank you for your clear explanation! I've switched over to the Capability system for my tile entity now, and everything still works, which I'll take as a good sign. I can certainly see the advantage of better mod interoperability. Now if only I could get the fluid tank rendering to work properly...but that's a different bug for a different day, and one for me to try and figure out first before asking! :D Thanks again.

  10. Also don't implement ITileEntityProvider.  You want the hasTileEntity and getTileEntity methods that exist in the Block class (hint: the one you're using is wrong).

     

    Okay, I've switched to using the ones from the Block class; but it seems like the default implementations of those methods check for ITileEntityProvider anyway and ultimately end up doing the same thing; what's the benefit to switching over?

     

    It's not directly related to your question, but you're using

    IFluidHandler

    incorrectly.

     

    The whole point of the Capability system is that you don't implement interfaces on your

    TileEntity

    , instead you store the objects in the

    TileEntity

    and override the

    ICapabilityProvider

    methods to return them. Forge's documentation explains this in more detail here.

     

    I'll admit, after reading the documentation for Capabilities, as well as some example code elsewhere...I'm totally confused by the system. So basically, I would attach an instance of the IFluidHandler Capability to the tile entity in the AttachCapabilities event, one instance per tank in the tile entity, then override the tile entity's getCapability/hasCapability methods to return those fluid handler instances? And then whenever it needs to fill/drain anything, it would...uh, pick one of the capabilities and call the fill/drain methods on them? I really don't understand what's going on there, nor do I understand what the system is supposed to improve over simply implementing the handler interface on the tile entity itself...

  11. I have a block and an associated ITickable tile entity. Everything works fine, until I quit the world and re-load it. After that, the server-side tile entity loads up fine, but the client-side one seems to disappear. (Tested by having the tile entity's update() method output this.worldObj.isRemote to the system log. It outputs for both client and server after placing the block, but only for the server after reloading the world.)

     

    I've organized my code in such a way as to try and make adding new blocks, items, etc. easier on myself later, so there's lots of inheritance and classes which register themselves on instantiation. I'm wondering if, while setting up that structure, I've missed something somewhere? Here's the relevant code:

     

    ChaoticaBlockBase.java:

     

    package com.icemetalpunk.chaotica.blocks;
    
    import com.icemetalpunk.chaotica.Chaotica;
    
    import net.minecraft.block.Block;
    import net.minecraft.block.material.Material;
    import net.minecraft.util.ResourceLocation;
    import net.minecraftforge.fml.common.registry.GameRegistry;
    import net.minecraftforge.oredict.OreDictionary;
    
    public class ChaoticaBlockBase extends Block {
    
    public ChaoticaBlockBase(String name, Material materialIn) {
    	super(materialIn);
    	this.setUnlocalizedName(name).setRegistryName(new ResourceLocation(Chaotica.MODID, name))
    			.setCreativeTab(Chaotica.tab);
    }
    
    protected void register() {
    	GameRegistry.register(this);
    	if (this instanceof ChaoticaTEBlock) {
    		GameRegistry.registerTileEntity(((ChaoticaTEBlock) this).getTileEntityClass(),
    				((ChaoticaTEBlock) this).getTileEntityName());
    	}
    
    	String[] oreDict = this.getOreDict();
    	if (oreDict != null) {
    		for (String entry : oreDict) {
    			OreDictionary.registerOre(entry, this);
    		}
    	}
    }
    
    // Override this if this block has an oredict entry.
    public String[] getOreDict() {
    	return null;
    }
    
    }

     

     

    ChaoticaTEBlockBase.java (For tile entity providing blocks.)

     

    package com.icemetalpunk.chaotica.blocks;
    
    import net.minecraft.block.ITileEntityProvider;
    import net.minecraft.block.material.Material;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.world.World;
    
    public abstract class ChaoticaTEBlock extends ChaoticaBlockBase implements ITileEntityProvider {
    
    public ChaoticaTEBlock(String name, Material materialIn) {
    	super(name, materialIn);
    }
    
    // Tile entity providers should provide the class and name of their tile
    // entity here for registration.
    public abstract Class<? extends TileEntity> getTileEntityClass();
    
    public abstract String getTileEntityName();
    
    // Generic createNewTileEntity so only the getTileEntityClass needs to be
    // specified.
    @Override
    public TileEntity createNewTileEntity(World world, int meta) {
    	try {
    		return this.getTileEntityClass().newInstance();
    	}
    	catch (InstantiationException e) {
    		e.printStackTrace();
    		return null;
    	}
    	catch (IllegalAccessException e) {
    		e.printStackTrace();
    		return null;
    	}
    }
    }

     

     

    Here's the specific block in question:

     

    BlockChaoticCondenser.java:

     

    package com.icemetalpunk.chaotica.blocks;
    
    import javax.annotation.Nullable;
    
    import com.icemetalpunk.chaotica.Chaotica;
    import com.icemetalpunk.chaotica.gui.ChaoticaGuiHandler;
    import com.icemetalpunk.chaotica.tileentities.TileEntityChaoticCondenser;
    
    import net.minecraft.block.SoundType;
    import net.minecraft.block.material.Material;
    import net.minecraft.block.state.IBlockState;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.item.ItemStack;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.EnumFacing;
    import net.minecraft.util.EnumHand;
    import net.minecraft.util.math.BlockPos;
    import net.minecraft.world.World;
    
    public class BlockChaoticCondenser extends ChaoticaTEBlock {
    
    public BlockChaoticCondenser() {
    	super("chaotic_condenser", Material.ROCK);
    	this.setHardness(3.5F);
    	this.setSoundType(SoundType.STONE);
    	this.register();
    }
    
    @Override
    public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand,
    		@Nullable ItemStack item, EnumFacing side, float hitX, float hitY, float hitZ) {
    	if (!world.isRemote) {
    		player.openGui(Chaotica.instance, ChaoticaGuiHandler.Guis.CONDENSER.ordinal(), world, pos.getX(),
    				pos.getY(), pos.getZ());
    	}
    	return true;
    }
    
    @Override
    public Class<? extends TileEntity> getTileEntityClass() {
    	return TileEntityChaoticCondenser.class;
    }
    
    @Override
    public String getTileEntityName() {
    	return "ChaoticCondenser";
    }
    
    }

     

     

    And the tile entity, or at least the relevant code:

     

    TileEntityChaoticCondenser.java:

     

    package com.icemetalpunk.chaotica.tileentities;
    
    import java.util.Iterator;
    import java.util.Map;
    
    import com.icemetalpunk.chaotica.Chaotica;
    import com.icemetalpunk.chaotica.ChaoticaUtils;
    import com.icemetalpunk.chaotica.fluids.FluidTankChaos;
    import com.icemetalpunk.chaotica.sounds.ChaoticaSoundRegistry;
    
    import net.minecraft.block.Block;
    import net.minecraft.block.state.IBlockState;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.inventory.IInventory;
    import net.minecraft.item.ItemStack;
    import net.minecraft.nbt.NBTTagCompound;
    import net.minecraft.network.NetworkManager;
    import net.minecraft.network.play.server.SPacketUpdateTileEntity;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.ITickable;
    import net.minecraft.util.SoundCategory;
    import net.minecraftforge.fluids.Fluid;
    import net.minecraftforge.fluids.FluidStack;
    import net.minecraftforge.fluids.capability.FluidTankPropertiesWrapper;
    import net.minecraftforge.fluids.capability.IFluidHandler;
    import net.minecraftforge.fluids.capability.IFluidTankProperties;
    
    public class TileEntityChaoticCondenser extends TileEntity implements IFluidHandler, ITickable {
    
    protected Fluid fluid = Chaotica.fluids.CORROSIVE_CHAOS;
    protected int capacity = 5 * Fluid.BUCKET_VOLUME;
    protected FluidStack fluidStack = new FluidStack(this.fluid, 0);
    protected FluidTankChaos tank = new FluidTankChaos(this.capacity);
    protected int countdown = 40;
    protected int maxCountdown = 40; // Max amount it resets to
    
    public TileEntityChaoticCondenser() {
    	this.tank.setCanFill(false);
    }
    
    // Ticks until the next check for blocks to convert to chaos
    public int getCountdown() {
    	return this.countdown;
    }
    
    public int getMaxCountdown() {
    	return this.maxCountdown;
    }
    
    @Override
    public IFluidTankProperties[] getTankProperties() {
    	return new IFluidTankProperties[] { new FluidTankPropertiesWrapper(tank) };
    };
    
    public Fluid getFluid() {
    	return this.fluidStack.getFluid();
    }
    
    @Override
    public void readFromNBT(NBTTagCompound tag) {
    	super.readFromNBT(tag);
    	NBTTagCompound tankTag = tag.getCompoundTag("Tank");
    	this.tank.readFromNBT(tankTag);
    	this.countdown = tag.getInteger("Countdown");
    }
    
    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound tag) {
    	super.writeToNBT(tag);
    	NBTTagCompound tankTag = new NBTTagCompound();
    	tank.writeToNBT(tankTag);
    	tag.setTag("Tank", tankTag);
    	tag.setInteger("Countdown", this.countdown);
    	return tag;
    }
    
    public int fill(int amount, boolean doFill) {
    	return this.tank.fill(new FluidStack(this.fluid, amount), doFill);
    }
    
    @Override
    public int fill(FluidStack resource, boolean doFill) {
    	return this.tank.fill(resource, doFill);
    }
    
    @Override
    public FluidStack drain(FluidStack resource, boolean doDrain) {
    	return this.tank.drain(resource, doDrain);
    }
    
    @Override
    public FluidStack drain(int maxDrain, boolean doDrain) {
    	return this.tank.drain(maxDrain, doDrain);
    }
    
    @Override
    public void update() {
    	String[] debug = new String[] { "server", "client" };
    	if (--this.countdown == 0) {
    		this.countdown = this.maxCountdown;
    		System.out.println("Countdown on " + debug[this.worldObj.isRemote ? 1 : 0] + " to " + this.countdown);
    		// ...snipped out irrelevant code here...
    	}
    }
    
    @Override
    public SPacketUpdateTileEntity getUpdatePacket() {
    	NBTTagCompound tag = new NBTTagCompound();
    	this.writeToNBT(tag);
    
    	IBlockState state = this.worldObj.getBlockState(this.pos);
    	Block block = state.getBlock();
    	int metadata = block.getMetaFromState(state);
    
    	return new SPacketUpdateTileEntity(this.pos, metadata, tag);
    }
    
    @Override
    public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity packet) {
    	this.readFromNBT(packet.getNbtCompound());
    }
    
    }

     

     

    If you look in the tile entity's update() method, you'll see the simple debug output I'm using. So why does it all work fine when I place the block, but after reloading the world, all the messages from the client-side tile entity stop, as though it's just removed and not re-created when the world loads again?

  12. Look at

    CommandTeleport

    on how to do teleportation properly. It most certainly should not be happening client-side at all.

     

    It seems like all that command does is call EntityPlayerMP's connection.setPlayerLocation() for the teleport. So I tried using that, and I still get the "Player Moved Wrongly" warning and rubber-banding...

     

    Here's the current code:

     

    	protected void teleport(EntityPlayerMP player, BlockPos destination) {
    	teleport(player, destination.getX(), destination.getY(), destination.getZ());
    }
    
    protected void teleport(EntityPlayerMP player, int x, int y, int z) {
    	player.connection.setPlayerLocation(x, y, z, player.rotationYaw, player.rotationPitch);
    }
    
    // Teleport the player when they walk through the flames with a linked
    // portkey
    @Override
    public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
    	if (entity instanceof EntityPlayerMP && !entity.worldObj.isRemote) {
    		EntityPlayerMP player = (EntityPlayerMP) entity;
    		ItemStack mainItem = player.getHeldItemMainhand();
    		ItemStack offItem = player.getHeldItemOffhand();
    
    		NBTTagCompound tag = null;
    		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
    			tag = mainItem.getTagCompound();
    			mainItem.damageItem(1, player);
    		}
    		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {
    			tag = offItem.getTagCompound();
    			offItem.damageItem(1, player);
    		}
    
    		if (tag != null) {
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    			player.velocityChanged = true;
    			teleport(player, x, y, z);
    			player.setFire(1);
    		}
    	}
    }

     

    Next I'll try using the latest version of Forge and see if that helps, but for now, I'm doing exactly what the teleport command does but it's still complaining.

  13. Ok I figured it out. You need to call it in both the client and server. That means remove the if(!world.isRemove) {} then that error will stop.

     

    Here is my example.

    @Override
    public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
    	super.onEntityCollidedWithBlock(world, pos, state, entity);
    	if(!world.isRemote) {
    		if(entity instanceof EntityPlayer) {
    			EntityPlayer player = (EntityPlayer) entity;
    
    			BlockPos npos = pos.add(25, 1.0, 0);
    
    			player.velocityChanged = true;
    
    			player.setPositionAndUpdate(npos.getX(), npos.getY(), npos.getZ());
    		}
    	}
    }
    
    @Override
    public void onEntityWalk(World world, BlockPos pos, Entity entity) {
    	// TODO Auto-generated method stub
    	super.onEntityWalk(world, pos, entity);
    
    
    	//if(!world.isRemote) {
    		if(entity instanceof EntityPlayer) {
    			EntityPlayer player = (EntityPlayer) entity;
    
    			BlockPos npos = pos.add(25, 1.0, 0);
    
    			player.velocityChanged = true;
    
    			player.setPositionAndUpdate(npos.getX(), npos.getY(), npos.getZ());
    		}
    	//}
    
    }
    

     

    However, I ran into a weird problem with the onEntityCollidedWithBlock event. It was firing so I used the onEntityWalk even and it worked.

    If I use onEntityWalk, the method is never called; keep in mind this is a fire-based block with no collision box.

     

    Using onEntityCollidedWithBlock, but moving the isRemote check around just the damageItem lines (which is required or else there's weird damage desync problems with the item) and not around the teleportation line, still gives the warning/rubber banding...

  14. The entire method is wrapped in a conditional that checks if

    !entity.worldObj.isRemote

    ....the whole thing runs only on the server.

     

    A bit of file searching shows this warning occurs under the following conditions:

     

    1) The player is not in creative or spectator mode.

    2) The player is not sleeping.

    3) The player has not just switched dimensions.

    4) The server update packet shows the player is more than 0.25 blocks away from the player's last known location.

     

    Which, of course, is the current situation when teleporting. But I don't understand why, then, it doesn't trigger the warning when using an Ender Pearl, since the EntityEnderPearl uses the same method to teleport the player on the server just as I'm doing...

  15. I have a block which, when collided with, checks for a certain item in the player's hand. If found, it uses the item's data tags to get a coordinate, and then teleports the player there.

     

    This *almost* works. Except when the teleportation occurs, I get a server warning in the console saying "[Playername] moved wrongly!" and end up getting teleported right back next to the block instead of the proper coordinates. I tried looking into the EntityEnderPearl code to see how that handles teleportation, and it seems to just use EntityPlayerMP#setPositionAndUpdate...but that's what I'm using and the server is complaining about it.

     

    I've found online other people who've had this trouble, but the solutions were for older versions of Forge which no longer apply; how do I teleport the player properly without it complaining and rubber-banding them?

     

    Here's the code in question:

     

    ]@Override
    public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
    	if (entity instanceof EntityPlayerMP && !entity.worldObj.isRemote) {
    		EntityPlayer player = (EntityPlayer) entity;
    		ItemStack mainItem = player.getHeldItemMainhand();
    		ItemStack offItem = player.getHeldItemOffhand();
    		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
    			NBTTagCompound tag = mainItem.getTagCompound();
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    			mainItem.damageItem(1, player);
    			player.setPositionAndUpdate(x, y, z);
    			player.setFire(1);
    		}
    		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {
    
    			NBTTagCompound tag = offItem.getTagCompound();
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    			offItem.damageItem(1, player);
    			player.setPositionAndUpdate(x, y, z);
    			EntityEnderPearl p;
    			player.setFire(1);
    		}
    	}
    }

  16. I didn't even know Forge had its own logger O_o . I learn something new every day xD But yeah, System.out.println does actually show whether the message is coming from the client thread or the server thread.

     

    As I was testing with the new logger, I think I've stumbled (accidentally) upon a key component of this problem. As you can see from the code, it teleports the player away if they're holding a linked item. During testing, I accidentally walked into the block without holding the proper item...and suddenly, I started getting console output from both the client and the server.

     

    So I'm going to assume that before, the client was teleporting the player away before the server registered a collision, causing the code to only run on the client side. Now that I've simply altered the code so it will only run on the server, everything is fixed.

     

    I guess I should take this as a lesson: just because something strange is happening doesn't mean your own code isn't to blame xD

     

    I'm now getting another bug related to the teleportation, but it's not related to the item data, so I'll make a new topic for it.

     

    Thanks, everyone who helped! :D

  17. Block#onEntityCollidedWithBlock

    is called on both the client and the server. Are you sure your override isn't being called on the server?

    100%. Below is the full code of the block class, and the only output I'm getting in the console is "Player collided! On server?: false" (and only once per collision, not twice like you'd expect from a server and client execution).

     

    package com.IceMetalPunk.amethystic.AmethysticBlocks;
    
    import java.util.Random;
    
    import com.IceMetalPunk.amethystic.Amethystic;
    
    import net.minecraft.block.Block;
    import net.minecraft.block.BlockFire;
    import net.minecraft.block.material.MapColor;
    import net.minecraft.block.state.IBlockState;
    import net.minecraft.entity.Entity;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.item.ItemStack;
    import net.minecraft.nbt.NBTTagCompound;
    import net.minecraft.util.EnumFacing;
    import net.minecraft.util.math.BlockPos;
    import net.minecraft.world.World;
    
    public class BlockEnderFlame extends BlockFire {
    public BlockEnderFlame() {
    	super();
    	this.setUnlocalizedName("ender_flame").setRegistryName(Amethystic.MODID, "ender_flame");
    }
    
    @Override
    public int tickRate(World worldIn) {
    	return 10;
    }
    
    @Override
    public MapColor getMapColor(IBlockState state) {
    	return MapColor.CYAN;
    }
    
    @Override // So the Ender Flame doesn't spread
    public void updateTick(World world, BlockPos pos, IBlockState state, Random rand) {
    	Block block = world.getBlockState(pos.down()).getBlock();
    	int i = ((Integer) state.getValue(AGE)).intValue();
    	boolean flag = block.isFireSource(world, pos.down(), EnumFacing.UP);
    
    	if (!flag && world.isRaining() && this.canDie(world, pos) && rand.nextFloat() < 0.2F + (float) i * 0.03F) {
    		world.setBlockToAir(pos);
    	}
    	else if (i < 15) {
    		state = state.withProperty(AGE, Integer.valueOf(i + 1));
    		world.setBlockState(pos, state, 4);
    		world.scheduleUpdate(pos, this, this.tickRate(world) + rand.nextInt(10));
    	}
    	else {
    		world.setBlockToAir(pos);
    	}
    }
    
    // Teleport the player when they walk through the flames with a linked
    // portkey
    @Override
    public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
    	if (entity instanceof EntityPlayer) {
    		EntityPlayer player = (EntityPlayer) entity;
    
    		ItemStack mainItem = player.getHeldItemMainhand();
    		ItemStack offItem = player.getHeldItemOffhand();
    		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
    			NBTTagCompound tag = mainItem.getTagCompound();
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    
    			System.out.println("Player collided! On server?: " + !player.worldObj.isRemote);
    
    			mainItem.damageItem(1, player); // FIXME: Damage only occurs on
    											// client, not server?
    			player.setPositionAndUpdate(x, y, z);
    			player.setFire(1);
    		}
    		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {
    
    			NBTTagCompound tag = offItem.getTagCompound();
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    			offItem.damageItem(1, player);
    			player.setPositionAndUpdate(x, y, z);
    			player.setFire(1);
    		}
    	}
    }
    }

  18. Entity#isServerWorld

    returns the inverse of

    World#isRemote

    by default, but

    EntityPlayerSP

    overrides it to return

    true

    .

    EntityLiving

    also overrides it to only return

    true

    if the entity's AI is enabled.

     

    If you want to check whether you're on the server, use

    World#isRemote

    directly.

     

    ...d'oh! >_< Of course there would be overrides that I didn't see in my examination of things... I bet I'd have seen that if I'd added a breakpoint and stepped through the method... thanks.

     

    So now that brings me to the true nature of this problem: the collision method is, in fact, only being called on the client after all, not on the server. So how would I detect a collision on the server? There doesn't even seem to be an event to hook into for that (presumably because this method is the preferred way to handle collisions), so how would I damage the item on the server when collisions seem to only be handled on the client?

  19. "Damage not being saved" and "stays 0 on the server" means you're only damaging the item on the client side.

    I know...which is what I don't understand. As I said, the method that calls the item damaging has a debug output that prints player.isServerWorld(). Every output from that is true; i.e. it's only being called on the server (or at least it *is* being called on the server). And yet everything else regarding the damage suggests it's only being called on the client. How is it possible for isServerWorld (which just wraps

    !world.isRemote

    anyway) to return true but the next line of code to run only on the client?

    Just to clear it up !world.isRemote is server side and world.isRemote is client side. In the code above you return if !world.isRemote without doing anything in Item#onItemRightClick. And for the player.isServerWorld() println how are we supposed to know with out seeing updated code.

     

    Yes, I know; originally, I had it return if world.isRemote was true, but that was giving me the initial problem of removing all the damage from the item; after changing it to return is world.isRemote was false, that problem stopped happening, although now the damage isn't saving; since it didn't actually fix the problem completely, I've since changed it back.

     

    But the code that actually damages the item isn't doing any checks for server/client side at all; it should be running whenever the block collision method is called. The "updated code" is exactly the same as above with an added console log, but here it is again:

     

    	@Override
    public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) {
    	if (entity instanceof EntityPlayer) {
    		EntityPlayer player = (EntityPlayer) entity;
    
    		ItemStack mainItem = player.getHeldItemMainhand();
    		ItemStack offItem = player.getHeldItemOffhand();
    		if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) {
    			NBTTagCompound tag = mainItem.getTagCompound();
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    
    			System.out.println("Player collided! On server?: " + player.isServerWorld());
    
    			mainItem.damageItem(1, player); // FIXME: Damage only occurs on
    											// client, not server?
    			player.setPositionAndUpdate(x, y, z);
    			player.setFire(1);
    		}
    		else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) {
    
    			NBTTagCompound tag = offItem.getTagCompound();
    			int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ");
    			offItem.damageItem(1, player);
    			player.setPositionAndUpdate(x, y, z);
    			player.setFire(1);
    		}
    	}
    }

     

    That println() with the player.isServerWorld() call is always printing true, meaning it's only running on the server, correct? And yet, as I mentioned, when right-clicking the item, other debug outputs show the server version of the item has 0 damage at all times, while the client version has the proper amount of damage.

×
×
  • Create New...

Important Information

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