Posted August 28, 201411 yr My latest project has been to make an Elevator for Minecraft. Have learned a lot doing so with respect to Tile entities, packet handling, and gui's and I know that I can learn more. Everything works well but one thing. The floor movement up and down moves too fast. I would like to slow it down, so it feels like a real elevator moving up and down. Attached is my TE that has all the movement code in it. I am looking for some suggestions on how to place a few second delay between each step of the Y axis movement as the floor and players move up and down. The method startFloorCall is at the bottom of the code. The method is called from within the updateEntity() method when floorCall = true. 1. Tried just a simple counter loop. No change. 2. Tried a tick handler server side. Seemed to work for the floor, but not the players. Are player movements client side, server side, or both. If client side, would explain why this did not work. 3. Tried to add a timer to the updateEntity() in the TE, but the timer would never update when I was within my method. If I could get this to work, would be best for cpu time. package com.eractnod.elevator.tileentity; import java.util.List; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; public class TileEntityCreateElevator extends TileEntity implements IInventory{ private ItemStack[] inv; private String customName; public String numberOfFloors; public String numberBetweenFloors; public String direction; private boolean floors = false; public int[] floorX = new int[16]; public int[] floorY = new int[16]; public int[] floorZ = new int[16]; public int[] floorNum = new int[16]; public int currentFloor; public int calledFloor; public boolean performCall = false; public boolean floorCall = false; public TileEntityCreateElevator(){ inv = new ItemStack[2]; } @Override public int getSizeInventory() { return inv.length; } @Override public ItemStack getStackInSlot(int slot) { return inv[slot]; } @Override public ItemStack decrStackSize(int slot, int amount) { ItemStack stack = getStackInSlot(slot); if (stack != null) { if (stack.stackSize <= amount) { setInventorySlotContents(slot, null); } else { stack = stack.splitStack(amount); if (stack.stackSize == 0) { setInventorySlotContents(slot, null); } } } return stack; } @Override public ItemStack getStackInSlotOnClosing(int slot) { ItemStack stack = getStackInSlot(slot); if (stack != null) { setInventorySlotContents(slot, null); } return stack; } @Override public void setInventorySlotContents(int slot, ItemStack stack) { inv[slot] = stack; if (stack != null && stack.stackSize > getInventoryStackLimit()) { stack.stackSize = getInventoryStackLimit(); } } @Override public String getInventoryName() { return null; } @Override public boolean hasCustomInventoryName() { return false; } @Override public int getInventoryStackLimit() { return 1; } @Override public boolean isUseableByPlayer(EntityPlayer player) { return worldObj.getTileEntity(xCoord, yCoord, zCoord) == this && player.getDistanceSq(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) < 64; } @Override public void openInventory() { } @Override public void closeInventory() { } @Override public boolean isItemValidForSlot(int slot, ItemStack stack) { return true; } public void updateEntity(){ if (performCall){ System.out.println("yobo"); startCall(calledFloor, worldObj); this.setCurrent(calledFloor); this.currentFloor = calledFloor; performCall = false; worldObj.markBlockForUpdate(floorX[0], floorY[0], floorZ[0]); } if(!worldObj.isRemote){ if (floorCall){ System.out.println("yobo2"); startFloorCall(calledFloor, worldObj); this.setCurrent(calledFloor); floorCall = false; worldObj.markBlockForUpdate(floorX[0], floorY[0], floorZ[0]); } } } public void setFloors(String a, String b, String c){ numberOfFloors = a; numberBetweenFloors = b; direction = c; } public ItemStack getFloorItemStack(){ return inv[0]; } public String getFloors(){ return numberOfFloors; } public String getBetween(){ return numberBetweenFloors; } public String getDirection(){ return direction; } public void setFloorLocations(int x, int y, int z, int metaCall) { System.out.println("x " + x + " y " + y + " z " + z + " meta " + metaCall); this.floorX[metaCall] = x; this.floorY[metaCall] = y; this.floorZ[metaCall] = z; } public void setCurrent(int meta){ this.currentFloor = meta; } public int getCurrent(){ return currentFloor; } public void setPerformCall(int calledFloor, boolean performCall, boolean floorCall) { this.calledFloor = calledFloor; this.performCall = performCall; this.floorCall = floorCall; } public int getFloorCalled(){ return this.calledFloor; } public boolean getPerformCall(){ return this.performCall; } public boolean getNextFloor(){ return this.floorCall; } /** * Reads a tile entity from NBT. */ public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); NBTTagList nbttaglist = nbt.getTagList("Items", 10); this.inv = new ItemStack[this.getSizeInventory()]; if (nbt.hasKey("NumberFloors")){ numberOfFloors = nbt.getString("NumberFloors"); } if (nbt.hasKey("NumberBetween")){ numberBetweenFloors = nbt.getString("NumberBetween"); } if (nbt.hasKey("Direction")){ direction = nbt.getString("Direction"); } if (nbt.hasKey("name")) { this.customName = nbt.getString("name"); } for (int i = 0; i < nbttaglist.tagCount(); ++i) { NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.getCompoundTagAt(i); int j = nbttagcompound1.getByte("Slot") & 255; if (j >= 0 && j < this.inv.length) { this.inv[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1); } } for (int i = 0; i < floorX.length; i++){ this.floorX[i] = nbt.getInteger("floorX" + i); this.floorY[i] = nbt.getInteger("floorY" + i); this.floorZ[i] = nbt.getInteger("floorZ" + i); } this.currentFloor = nbt.getInteger("current"); this.calledFloor = nbt.getInteger("calledfloor"); this.performCall = nbt.getBoolean("performcall"); this.floorCall = nbt.getBoolean("floorcall"); } /** * Writes a tile entity to NBT. */ public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < this.inv.length; ++i) { if (this.inv[i] != null) { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound1.setByte("Slot", (byte)i); this.inv[i].writeToNBT(nbttagcompound1); nbttaglist.appendTag(nbttagcompound1); } } nbt.setTag("Items", nbttaglist); if (this.hasCustomInventoryName()) { nbt.setString("name", this.customName); } if (numberOfFloors != null){ nbt.setString("NumberFloors", numberOfFloors); } if (numberBetweenFloors != null){ nbt.setString("NumberBetween", numberBetweenFloors); } if (direction != null){ nbt.setString("Direction", direction); } for (int i = 0; i < floorX.length; i++){ nbt.setInteger("floorX" + i, this.floorX[i]); nbt.setInteger("floorY" + i, this.floorY[i]); nbt.setInteger("floorZ" + i, this.floorZ[i]); } nbt.setInteger("current", currentFloor); nbt.setInteger("calledfloor", calledFloor); nbt.setBoolean("performcall", performCall); nbt.setBoolean("floorcall", floorCall); } /** * Called when you receive a TileEntityData packet for the location this * TileEntity is currently in. On the client, the NetworkManager will always * be the remote server. On the server, it will be whomever is responsible for * sending the packet. * * @param net The NetworkManager the packet originated from * @param pkt The data packet */ // Client Server Sync @Override public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { NBTTagCompound nbt = pkt.func_148857_g(); numberOfFloors = nbt.getString("NumberFloors"); numberBetweenFloors = nbt.getString("NumberBetween"); direction = nbt.getString("Direction"); for (int i = 0; i < floorX.length; i++){ floorX[i] = nbt.getInteger("floorX" + i); floorY[i] = nbt.getInteger("floorY" + i); floorZ[i] = nbt.getInteger("floorZ" + i); } this.currentFloor = nbt.getInteger("current"); this.calledFloor = nbt.getInteger("calledfloor"); this.performCall = nbt.getBoolean("performcall"); this.floorCall = nbt.getBoolean("floorcall"); } @Override public Packet getDescriptionPacket() { NBTTagCompound nbt = new NBTTagCompound(); if (numberOfFloors != null){ nbt.setString("NumberFloors", numberOfFloors); } if (numberBetweenFloors != null){ nbt.setString("NumberBetween", numberBetweenFloors); } if (direction != null){ nbt.setString("Direction", direction); } for (int i = 0; i < floorX.length; i++){ nbt.setInteger("floorX" + i, floorX[i]); nbt.setInteger("floorY" + i, floorY[i]); nbt.setInteger("floorZ" + i, floorZ[i]); } nbt.setInteger("current", currentFloor); nbt.setInteger("calledfloor", calledFloor); nbt.setBoolean("performcall", performCall); nbt.setBoolean("floorcall", floorCall); return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, this.blockMetadata, nbt); } public Block getFloorBlock(World world){ Block floorBlock = Blocks.stone; TileEntityCreateElevator te = (TileEntityCreateElevator) world.getTileEntity(floorX[0], floorY[0], floorZ[0]); ItemStack stack = te.getStackInSlot(0); if (stack != null){ if (stack.getItem() instanceof ItemBlock){ Item floorItem = stack.getItem(); int meta0 = stack.getItemDamage(); floorBlock = Block.getBlockFromItem(floorItem); return floorBlock; } } return floorBlock; } public int getFloorBlockMeta(World world){ Block floorBlock = Blocks.stone; TileEntityCreateElevator te = (TileEntityCreateElevator) world.getTileEntity(floorX[0], floorY[0], floorZ[0]); ItemStack stack = te.getStackInSlot(0); if (stack != null){ if (stack.getItem() instanceof ItemBlock){ Item floorItem = stack.getItem(); int meta0 = stack.getItemDamage(); floorBlock = Block.getBlockFromItem(floorItem); return meta0; } } return 0; } public void startCall(int meta, World world) { Block floorBlock = this.getFloorBlock(world); int floorMeta = this.getFloorBlockMeta(world); int currentFloor = this.getCurrent(); int x = this.floorX[currentFloor]; int y = this.floorY[currentFloor]; int z = this.floorZ[currentFloor]; //Current floor below called floor Move UP if (currentFloor < meta){ for (int y2 = y; y2 != floorY[meta]; y2++){ for (int x11 = -1; x11 <=1; x11++){ for (int z11 = -1; z11 <=1; z11++){ world.setBlock(x + x11, y2, z + z11, Blocks.air); world.setBlock(x + x11, y2 + 1, z + z11, floorBlock, floorMeta, 2); } } } } //Current floor above called floor Move DOWN if (currentFloor > meta){ for (int y2 = y; y2 != floorY[meta]; y2--){ for (int x11 = -1; x11 <=1; x11++){ for (int z11 = -1; z11 <=1; z11++){ world.setBlock(x + x11, y2, z + z11, Blocks.air); world.setBlock(x + x11, y2 - 1, z + z11, floorBlock, floorMeta, 2); } } } } } private void startFloorCall(int calledFloor2, World world) { int numberFloors = Integer.parseInt(numberOfFloors); //EntityPlayer player = this.worldObj.getClosestPlayer(this.floorX[currentFloor], this.floorY[currentFloor], this.floorZ[currentFloor], 10); List listPlayer = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox(this.floorX[0]-2,this.floorY[0],this.floorZ[0]-2,this.floorX[numberFloors]+2,this.floorY[numberFloors]+2,this.floorZ[numberFloors]+2)); System.out.println("List " + listPlayer); if (!listPlayer.isEmpty()){ Block floorBlock = this.getFloorBlock(world); int floorMeta = this.getFloorBlockMeta(world); int currentFloor = this.getCurrent(); int x = this.floorX[currentFloor]; int y = this.floorY[currentFloor]; int z = this.floorZ[currentFloor]; //Current floor below called floor Move UP if (currentFloor < calledFloor2){ for (int y2 = y; y2 != floorY[calledFloor2]; y2++){ for (int x11 = -1; x11 <=1; x11++){ for (int z11 = -1; z11 <=1; z11++){ /** Need delay here * * * */ world.setBlock(x + x11, y2, z + z11, Blocks.air); world.setBlock(x + x11, y2 + 1, z + z11, floorBlock, floorMeta, 2); for (int i = 0; i < listPlayer.size(); i++){ EntityPlayer p = (EntityPlayer) listPlayer.get(i); p.setPositionAndUpdate(x + x11, y2 + 2, z + z11); } //player.setPositionAndUpdate(x + x11, y2 + 2, z + z11); } } } } //Current floor above called floor Move DOWN if (currentFloor > calledFloor2){ for (int y2 = y; y2 != floorY[calledFloor2]; y2--){ for (int x11 = -1; x11 <=1; x11++){ for (int z11 = -1; z11 <=1; z11++){ /** Need delay here * * * */ world.setBlock(x + x11, y2, z + z11, Blocks.air); world.setBlock(x + x11, y2 - 1, z + z11, floorBlock, floorMeta, 2); for (int i = 0; i < listPlayer.size(); i++){ EntityPlayer p = (EntityPlayer) listPlayer.get(i); p.setPositionAndUpdate(x + x11, y2, z + z11); } //player.setPositionAndUpdate(x + x11, y2, z + z11); } } } } } } }
August 28, 201411 yr have you tried a CD int? you tried a counter loop, I think you had the right idea? or did exactly what I'm going to tell you: int CD_TIME = 45; int coolDown = CD_TIME; @Override public void updateEntity() { if (coolDown == 0) { coolDown = CD_TIME; //put delayed commands in here } else coolDown --; } I think someone asked me why I did that and it's because some of my tile Entities have some large CPU hungry commands that they don't need to run every tick, just every now and then (to make sure things are going ok) one day I'll re-factor to something nicer and more intelligent but I just use my manual counters, since a tile Entity and other entities update every tick (that's what 100 times a second?) so the delay would be every 45 ticks. This is just my really dumb workaround. Maybe someone will come along with a better more efficient solution Currently updating my Mod to 1.10.2 https://bitbucket.org/hugo_the_dwarf/riseoftristram2016/src?at=master
August 28, 201411 yr You can use World#getCurrentWorldTime() % 20 == 0 to make it run 1 a second (since there's twenty ticks in a second) BEFORE ASKING FOR HELP READ THE EAQ! I'll help if I can. Apologies if I do something obviously stupid. If you don't know basic Java yet, go and follow these tutorials.
August 29, 201411 yr Author Thank you for the replies. Between the two answers I was able to get my ServerTickHandler to work to add the delay. However I have come to the conclusion that the jerky movement would cause too many headaches. So will do without until I can figure out 1/4 or 1/8 block steps. Would make the movement less choppy. Now to add Elevator music while you are in the elevator. Tickhandler code public class ServerTickHandler { public int timer; public ServerTickHandler(){ this.timer = 12000; } @SubscribeEvent public int onServerTick(TickEvent.ServerTickEvent event){ timer--; if (timer == 0){ timer = 12000; } return timer; } } And what I added to the TE. ServerTickHandler handler = new ServerTickHandler(); int tick; do{ tick = handler.onServerTick(null); }while (tick != 1);
August 29, 201411 yr Hi Your tile Entity can render anywhere, so you can render it in 1/8 block steps if you want. I imagine your lift shaft is rendered using blocks, and your elevator using the TileEntitySpecialRenderer, which gives you lots of flexibility? The server doesn't need to do much - just to communicate to the client what the elevator state is (moving, to which floor, etc); the smooth movement needs to happen on the client. On the client, every tick, update the elevator position based on the speed. For example float currentYpos = lastTickYPos + currentSpeedBlocksPerTick; Then render the elevator at the fractional y position (and update the player position if they are inside the elevator - you will need to figure out how to temporarily ignore server updates of the player position to avoid rubber banding - I have done that before, don't remember off hand since I don't have my code to hand - let us know if you need help with that). -TGG
August 29, 201411 yr Author I was thinking about going that route. Make the floor into a 9 block entity that mimic's the boat or minecart, then set ridable to true. That would mean a 3rd re-write of this mod. (and of coarse I am most likely going to do it as it would mean learning something new), but not at this time. The first writing was with 2 tile entitys. One for the elevator shaft and one placed upto 15 times for making the call and floor buttons. Started to be a nightmare syncing all the data between the two tile entities. The second writing removed the second tile entity and now just places a block with meta data, and this block then calls the gui for call and floor buttons. Everything is then stored in the one tile entity. Made it easier to set and save each floor location during the build of the shaft.
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.