package micdoodle8.mods.galacticraft.core.dimension; import com.google.common.collect.Lists; import micdoodle8.mods.galacticraft.api.vector.BlockVec3; import micdoodle8.mods.galacticraft.core.Constants; import micdoodle8.mods.galacticraft.core.GalacticraftCore; import micdoodle8.mods.galacticraft.core.blocks.BlockSpinThruster; import micdoodle8.mods.galacticraft.core.entities.ITumblable; import micdoodle8.mods.galacticraft.core.entities.player.FreefallHandler; import micdoodle8.mods.galacticraft.core.network.PacketSimple; import micdoodle8.mods.galacticraft.core.util.ConfigManagerCore; import micdoodle8.mods.galacticraft.core.util.GCCoreUtil; import micdoodle8.mods.galacticraft.core.util.GCLog; import micdoodle8.mods.galacticraft.core.util.RedstoneUtil; import net.minecraft.block.Block; import net.minecraft.block.BlockLiquid; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityFallingBlock; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.item.EntityTNTPrimed; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.BlockPos; import net.minecraft.util.MathHelper; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; public class SpinManager { private static final float GFORCE = 9.81F / 400F; //gravity in metres per tick squared public boolean doSpinning = true; public float angularVelocityRadians = 0F; public float skyAngularVelocity = this.angularVelocityRadians * Constants.RADIANS_TO_DEGREES; public float angularVelocityTarget = 0F; public float angularVelocityAccel = 0F; public double spinCentreX; public double spinCentreZ; private float momentOfInertia; private float massCentreX; private float massCentreZ; public int ssBoundsMaxX; public int ssBoundsMinX; public int ssBoundsMaxY; public int ssBoundsMinY; public int ssBoundsMaxZ; public int ssBoundsMinZ; private OrbitSpinSaveData savefile; private LinkedList<BlockPos> thrustersPlus = Lists.newLinkedList(); private LinkedList<BlockPos> thrustersMinus = Lists.newLinkedList(); private BlockPos oneSSBlock; //private HashSet<BlockPos> stationBlocks = new HashSet(); private HashSet<BlockVec3> checked = new HashSet<BlockVec3>(); private float artificialG; //Used to make continuous particles + thrust sounds at the spin thrusters in this dimension //If false, make particles + sounds occasionally in small bursts, just for fun (micro attitude changes) //see: BlockSpinThruster.randomDisplayTick() public boolean thrustersFiring = false; private boolean dataNotLoaded = true; private List<Entity> loadedEntities = Lists.newLinkedList(); private WorldProviderSpaceStation worldProvider; private boolean clientSide = true; public SpinManager(WorldProviderSpaceStation provider) { this.worldProvider = provider; } /** * Called from WorldProviderOrbit when registering the worldObj */ public void registerServerSide() { if (!this.worldProvider.worldObj.isRemote) { this.clientSide = false; } } public float getSpinRate() { return this.skyAngularVelocity; } /** * Sets the spin rate for the dimension in radians per tick For example, * 0.031415 would be 1/200 revolution per tick So that would be 1 revolution * every 10 seconds */ public void setSpinRate(float angle) { this.angularVelocityRadians = angle; this.skyAngularVelocity = angle * 180F / 3.1415927F; if (GCCoreUtil.getEffectiveSide() == Side.CLIENT) { this.updateSkyProviderSpinRate(); } } @SideOnly(Side.CLIENT) private void updateSkyProviderSpinRate() { this.worldProvider.setSpinDeltaPerTick(this.skyAngularVelocity); } public void setSpinRate(float angle, boolean firing) { this.angularVelocityRadians = angle; this.skyAngularVelocity = angle * 180F / 3.1415927F; this.worldProvider.setSpinDeltaPerTick(this.skyAngularVelocity); this.thrustersFiring = firing; } public void setSpinCentre(double x, double z) { this.spinCentreX = x; this.spinCentreZ = z; if (this.clientSide) { if (ConfigManagerCore.enableDebug) { GCLog.info("Clientside update to spin centre: " + x + "," + z); } } } public void setSpinBox(int mx, int xx, int my, int yy, int mz, int zz) { this.ssBoundsMinX = mx; this.ssBoundsMaxX = xx; this.ssBoundsMinY = my; this.ssBoundsMaxY = yy; this.ssBoundsMinZ = mz; this.ssBoundsMaxZ = zz; } public void removeThruster(BlockPos thruster, boolean positive) { if (positive) { this.thrustersPlus.remove(thruster); } else { this.thrustersMinus.remove(thruster); } } /** * This will check all blocks which are in contact with each other to find * the shape of the spacestation. It also finds the centre of mass (to * rotate around) and the moment of inertia (how easy/hard this is to * rotate). * <p/> * If placingThruster is true, it will return false if the thruster (at * baseBlock) is not in contact with the "existing" spacestation - so the * player cannot place thrusters on outlying disconnected blocks and expect * them to have an effect. * <p/> * Note: this check will briefly load, server-side, all chunks which have * spacestation blocks in them or 1 block adjacent to those. * * @param baseBlock * @return */ public boolean refresh(BlockPos baseBlock, boolean placingThruster) { if (this.oneSSBlock == null || this.worldProvider.worldObj.getBlockState(this.oneSSBlock).getBlock().isAir(this.worldProvider.worldObj, this.oneSSBlock)) { if (baseBlock != null) { this.oneSSBlock = baseBlock; } else { this.oneSSBlock = new BlockPos(0, 64, 0); } } // Find contiguous blocks using an algorithm like the oxygen sealer one List<BlockVec3> currentLayer = new LinkedList<BlockVec3>(); List<BlockVec3> nextLayer = new LinkedList<BlockVec3>(); final List<BlockPos> foundThrusters = new LinkedList<BlockPos>(); this.checked.clear(); currentLayer.add(new BlockVec3(this.oneSSBlock)); this.checked.add(new BlockVec3(this.oneSSBlock)); Block bStart = this.worldProvider.worldObj.getBlockState(this.oneSSBlock).getBlock(); if (bStart instanceof BlockSpinThruster) { foundThrusters.add(this.oneSSBlock); } float thismass = 0.1F; //Mass of a thruster float thismassCentreX = 0.1F * this.oneSSBlock.getX(); float thismassCentreY = 0.1F * this.oneSSBlock.getY(); float thismassCentreZ = 0.1F * this.oneSSBlock.getZ(); float thismoment = 0F; int thisssBoundsMaxX = this.oneSSBlock.getX(); int thisssBoundsMinX = this.oneSSBlock.getX(); int thisssBoundsMaxY = this.oneSSBlock.getY(); int thisssBoundsMinY = this.oneSSBlock.getY(); int thisssBoundsMaxZ = this.oneSSBlock.getZ(); int thisssBoundsMinZ = this.oneSSBlock.getZ(); while (currentLayer.size() > 0) { int bits; for (BlockVec3 vec : currentLayer) { bits = vec.sideDoneBits; if (vec.x < thisssBoundsMinX) { thisssBoundsMinX = vec.x; } if (vec.y < thisssBoundsMinY) { thisssBoundsMinY = vec.y; } if (vec.z < thisssBoundsMinZ) { thisssBoundsMinZ = vec.z; } if (vec.x > thisssBoundsMaxX) { thisssBoundsMaxX = vec.x; } if (vec.y > thisssBoundsMaxY) { thisssBoundsMaxY = vec.y; } if (vec.z > thisssBoundsMaxZ) { thisssBoundsMaxZ = vec.z; } for (int side = 0; side < 6; side++) { if ((bits & (1 << side)) == 1) { continue; } BlockVec3 sideVec = vec.newVecSide(side); if (!this.checked.contains(sideVec)) { this.checked.add(sideVec); Block b = sideVec.getBlockID(this.worldProvider.worldObj); if (b != null && !b.isAir(this.worldProvider.worldObj, sideVec.toBlockPos())) { nextLayer.add(sideVec); if (bStart.isAir(this.worldProvider.worldObj, this.oneSSBlock)) { this.oneSSBlock = sideVec.toBlockPos(); bStart = b; } float m = 1.0F; //Liquids have a mass of 1, stone, metal blocks etc will be heavier if (!(b instanceof BlockLiquid)) { //For most blocks, hardness gives a good idea of mass m = b.getBlockHardness(this.worldProvider.worldObj, sideVec.toBlockPos()); if (m < 0.1F) { m = 0.1F; } else if (m > 30F) { m = 30F; } //Wood items have a high hardness compared with their presumed mass if (b.getMaterial() == Material.wood) { m /= 4; } //TODO: higher mass for future Galacticraft hi-density item like neutronium //Maybe also check for things in other mods by name: lead, uranium blocks? } thismassCentreX += m * sideVec.x; thismassCentreY += m * sideVec.y; thismassCentreZ += m * sideVec.z; thismass += m; thismoment += m * (sideVec.x * sideVec.x + sideVec.z * sideVec.z); if (b instanceof BlockSpinThruster && !RedstoneUtil.isBlockReceivingRedstone(this.worldProvider.worldObj, sideVec.toBlockPos())) { foundThrusters.add(sideVec.toBlockPos()); } } } } } currentLayer = nextLayer; nextLayer = new LinkedList<BlockVec3>(); } if (placingThruster && !this.checked.contains(new BlockVec3(baseBlock))) { if (foundThrusters.size() > 0) { //The thruster was not placed on the existing contiguous space station: it must be. if (ConfigManagerCore.enableDebug) { GCLog.info("Thruster placed on wrong part of space station: base at " + this.oneSSBlock + " - baseBlock was " + baseBlock + " - found " + foundThrusters.size()); } return false; } //No thruster on the original space station - so assume the player made new station and start check again //This offers players a reset option: just remove all thrusters from original station then starting adding to new one //(This first check prevents an infinite loop) if (!this.oneSSBlock.equals(baseBlock)) { this.oneSSBlock = baseBlock; if (this.worldProvider.worldObj.getBlockState(this.oneSSBlock).getBlock().getMaterial() != Material.air) { return this.refresh(baseBlock, true); } } return false; } // Update thruster lists based on what was found this.thrustersPlus.clear(); this.thrustersMinus.clear(); for (BlockPos thruster : foundThrusters) { IBlockState state = this.worldProvider.worldObj.getBlockState(thruster); int facing = state.getBlock().getMetaFromState(state) & 8; if (facing == 0) { this.thrustersPlus.add(thruster); } else { this.thrustersMinus.add(thruster); } } // Calculate centre of mass float mass = thismass; this.massCentreX = thismassCentreX / thismass + 0.5F; float massCentreY = thismassCentreY / thismass + 0.5F; this.massCentreZ = thismassCentreZ / thismass + 0.5F; //System.out.println("(X,Z) = "+this.massCentreX+","+this.massCentreZ); this.setSpinCentre(this.massCentreX, this.massCentreZ); //The boundary is at the outer edges of the blocks this.ssBoundsMaxX = thisssBoundsMaxX + 1; this.ssBoundsMinX = thisssBoundsMinX; this.ssBoundsMaxY = thisssBoundsMaxY + 1; this.ssBoundsMinY = thisssBoundsMinY; this.ssBoundsMaxZ = thisssBoundsMaxZ + 1; this.ssBoundsMinZ = thisssBoundsMinZ; // Calculate momentOfInertia thismoment -= this.massCentreX * this.massCentreX * mass; thismoment -= this.massCentreZ * this.massCentreZ * mass; this.momentOfInertia = thismoment; //TODO // TODO defy gravity // TODO break blocks which are outside SS (not in checked) // TODO prevent spin if there is a huge number of blocks outside SS GCLog.debug("MoI = " + this.momentOfInertia + " CoMx = " + this.massCentreX + " CoMz = " + this.massCentreZ); //Send packets to clients in this dimension List<Object> objList = new ArrayList<Object>(); objList.add(Double.valueOf(this.spinCentreX)); objList.add(Double.valueOf(this.spinCentreZ)); GalacticraftCore.packetPipeline.sendToDimension(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_DATA, GCCoreUtil.getDimensionID(this.worldProvider), objList), GCCoreUtil.getDimensionID(this.worldProvider)); objList = new ArrayList<Object>(); objList.add(Integer.valueOf(this.ssBoundsMinX)); objList.add(Integer.valueOf(this.ssBoundsMaxX)); objList.add(Integer.valueOf(this.ssBoundsMinY)); objList.add(Integer.valueOf(this.ssBoundsMaxY)); objList.add(Integer.valueOf(this.ssBoundsMinZ)); objList.add(Integer.valueOf(this.ssBoundsMaxZ)); GalacticraftCore.packetPipeline.sendToDimension(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_BOX, GCCoreUtil.getDimensionID(this.worldProvider), objList), GCCoreUtil.getDimensionID(this.worldProvider)); this.updateSpinSpeed(); return true; } public void updateSpinSpeed() { if (this.momentOfInertia > 0F) { float netTorque = 0F; int countThrusters = 0; int countThrustersReverse = 0; for (BlockPos thruster : this.thrustersPlus) { float xx = thruster.getX() - this.massCentreX; float zz = thruster.getZ() - this.massCentreZ; netTorque += MathHelper.sqrt_float(xx * xx + zz * zz); countThrusters++; } for (BlockPos thruster : this.thrustersMinus) { float xx = thruster.getX() - this.massCentreX; float zz = thruster.getZ() - this.massCentreZ; netTorque -= MathHelper.sqrt_float(xx * xx + zz * zz); countThrustersReverse++; } if (countThrusters == countThrustersReverse) { this.angularVelocityAccel = 0.000004F; this.angularVelocityTarget = 0F; } else { countThrusters += countThrustersReverse; if (countThrusters > 4) { countThrusters = 4; } float maxRx = Math.max(this.ssBoundsMaxX - this.massCentreX, this.massCentreX - this.ssBoundsMinX); float maxRz = Math.max(this.ssBoundsMaxZ - this.massCentreZ, this.massCentreZ - this.ssBoundsMinZ); float maxR = Math.max(maxRx, maxRz); this.angularVelocityTarget = MathHelper.sqrt_float(GFORCE / maxR) / 2; //The divide by 2 is not scientific but is a Minecraft factor as everything happens more quickly float spinCap = 0.00125F * countThrusters; //TODO: increase this above 20F in release versions so everything happens more slowly this.angularVelocityAccel = netTorque / this.momentOfInertia / 20F; if (this.angularVelocityAccel < 0) { this.angularVelocityAccel = -this.angularVelocityAccel; this.angularVelocityTarget = -this.angularVelocityTarget; if (this.angularVelocityTarget < -spinCap) { this.angularVelocityTarget = -spinCap; } } else //Do not make it spin too fast or players might get dizzy //Also make it so players need minimum 4 thrusters for best spin if (this.angularVelocityTarget > spinCap) { this.angularVelocityTarget = spinCap; } if (ConfigManagerCore.enableDebug) { GCLog.info("MaxR = " + maxR + " Angular vel = " + this.angularVelocityTarget + " Angular accel = " + this.angularVelocityAccel); } } } if (!this.clientSide) { //Save the updated data for the world this.save(); } } public void updateSpin() { if (!this.clientSide) { if (this.dataNotLoaded) { this.savefile = OrbitSpinSaveData.initWorldData(this.worldProvider.worldObj); this.readFromNBT(this.savefile.datacompound); if (ConfigManagerCore.enableDebug) { GCLog.info("Loading data from save: " + this.savefile.datacompound.getFloat("omegaSky")); } this.dataNotLoaded = false; } if (this.doSpinning) { boolean updateNeeded = true; if (this.angularVelocityTarget < this.angularVelocityRadians) { float newAngle = this.angularVelocityRadians - this.angularVelocityAccel; if (newAngle < this.angularVelocityTarget) { newAngle = this.angularVelocityTarget; } this.setSpinRate(newAngle); this.thrustersFiring = true; } else if (this.angularVelocityTarget > this.angularVelocityRadians) { float newAngle = this.angularVelocityRadians + this.angularVelocityAccel; if (newAngle > this.angularVelocityTarget) { newAngle = this.angularVelocityTarget; } this.setSpinRate(newAngle); this.thrustersFiring = true; } else if (this.thrustersFiring) { this.thrustersFiring = false; } else { updateNeeded = false; } if (updateNeeded) { this.writeToNBT(this.savefile.datacompound); this.savefile.markDirty(); List<Object> objList = new ArrayList<Object>(); objList.add(Float.valueOf(this.angularVelocityRadians)); objList.add(Boolean.valueOf(this.thrustersFiring)); GalacticraftCore.packetPipeline.sendToDimension(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_SPIN, GCCoreUtil.getDimensionID(this.worldProvider), objList), GCCoreUtil.getDimensionID(this.worldProvider)); } } //Update entity positions if in freefall this.loadedEntities.clear(); this.loadedEntities.addAll(this.worldProvider.worldObj.loadedEntityList); for (Entity e : this.loadedEntities) { //TODO: What about vehicles from GC (buggies) and other mods? if ((e instanceof EntityItem || e instanceof EntityLivingBase && !(e instanceof EntityPlayer) || e instanceof EntityTNTPrimed || e instanceof EntityFallingBlock) && !e.onGround) { AxisAlignedBB entityBoundingBox = e.getEntityBoundingBox(); boolean outsideStation = entityBoundingBox.maxX < this.ssBoundsMinX || entityBoundingBox.minX > this.ssBoundsMaxX || entityBoundingBox.maxY < this.ssBoundsMinY || entityBoundingBox.minY > this.ssBoundsMaxY || entityBoundingBox.maxZ < this.ssBoundsMinZ || entityBoundingBox.minZ > this.ssBoundsMaxZ; if (outsideStation || FreefallHandler.testEntityFreefall(this.worldProvider.worldObj, entityBoundingBox)) { if (this.doSpinning) { this.moveRotatedEntity(e, this.spinCentreX, this.spinCentreZ, this.angularVelocityRadians); } FreefallHandler.tickFreefallEntity(e); if (e instanceof ITumblable) { ((ITumblable) e).setTumbling(3F); } } else { if (e instanceof ITumblable) { ((ITumblable) e).setTumbling(0F); } } } } } } private void moveRotatedEntity(Entity e, double rotationCentreX, double rotationCentreZ, float deltaTheta) { //Do the rotation if (deltaTheta != 0F) { float angle; final double xx = e.posX - rotationCentreX; final double zz = e.posZ - rotationCentreZ; double arc = Math.sqrt(xx * xx + zz * zz); if (xx == 0D) { angle = zz > 0 ? 3.1415926535F / 2 : -3.1415926535F / 2; } else { angle = (float) Math.atan(zz / xx); } if (xx < 0D) { angle += 3.1415926535F; } angle += deltaTheta / 3F; arc = arc * deltaTheta; final double offsetX = -arc * MathHelper.sin(angle); final double offsetZ = arc * MathHelper.cos(angle); e.posX += offsetX; e.posZ += offsetZ; e.lastTickPosX += offsetX; e.lastTickPosZ += offsetZ; //Rotated into an unloaded chunk (probably also drifted out to there): byebye if (!e.worldObj.isBlockLoaded(new BlockPos(MathHelper.floor_double(e.posX), 64, MathHelper.floor_double(e.posZ)))) { e.setDead(); return; } e.setEntityBoundingBox(e.getEntityBoundingBox().offset(offsetX, 0.0D, offsetZ)); //TODO check for block collisions here - if so move the entity appropriately and apply fall damage //Moving the entity = slide along / down e.rotationYaw += this.skyAngularVelocity; while (e.rotationYaw > 360F) { e.rotationYaw -= 360F; } //Rotate the motion vector (motionX, motionZ) by the angular rotation double velocity = Math.sqrt(e.motionX * e.motionX + e.motionZ * e.motionZ); if (e.motionX == 0D) { angle = e.motionZ > 0 ? 3.1415926535F / 2 : -3.1415926535F / 2; } else { angle = (float) Math.atan(e.motionZ / e.motionX); } if (e.motionX < 0D) { angle += 3.1415926535F; } angle += deltaTheta; e.motionX = velocity * MathHelper.cos(angle); e.motionZ = velocity * MathHelper.sin(angle); } } @SideOnly(Side.CLIENT) public boolean updatePlayerForSpin(EntityPlayerSP p, float partial) { float angleDelta = partial * this.angularVelocityRadians; if (this.doSpinning && angleDelta != 0F) { //TODO maybe need to test to make sure xx and zz are not too large (outside sight range of SS) //TODO think about server + network load (loading/unloading chunks) when movement is rapid //Maybe reduce chunkloading radius? boolean doCentrifugal = false; float angle; final double xx = p.posX - this.spinCentreX; final double zz = p.posZ - this.spinCentreZ; double arc = Math.sqrt(xx * xx + zz * zz); if (xx == 0D) { angle = zz > 0 ? 3.1415926535F / 2 : -3.1415926535F / 2; } else { angle = (float) Math.atan(zz / xx); } if (xx < 0D) { angle += 3.1415926535F; } angle += angleDelta / 3F; arc = arc * angleDelta; double offsetX = -arc * Math.sin(angle); double offsetZ = arc * Math.cos(angle); //Check for block collisions here - if so move the player appropriately //First check that there are no existing collisions where the player is now (TODO: bounce the player away) if (p.worldObj.getCollidingBoundingBoxes(p, p.getEntityBoundingBox()).size() == 0) { //Now check for collisions in the new direction and if there are some, try reducing the movement int collisions = 0; do { List<AxisAlignedBB> list = p.worldObj.getCollidingBoundingBoxes(p, p.getEntityBoundingBox().addCoord(offsetX, 0.0D, offsetZ)); collisions = list.size(); if (collisions > 0) { if (!doCentrifugal) { p.motionX += -offsetX; p.motionZ += -offsetZ; } offsetX /= 2D; offsetZ /= 2D; if (offsetX < 0.01D && offsetX > -0.01D) { offsetX = 0D; } if (offsetZ < 0.01D && offsetZ > -0.01D) { offsetZ = 0D; } doCentrifugal = true; } } while (collisions > 0); p.posX += offsetX; p.posZ += offsetZ; p.setEntityBoundingBox(p.getEntityBoundingBox().offset(offsetX, 0.0D, offsetZ)); } p.rotationYaw += this.skyAngularVelocity * partial; p.renderYawOffset += this.skyAngularVelocity * partial; while (p.rotationYaw > 360F) { p.rotationYaw -= 360F; } while (p.rotationYaw < 0F) { p.rotationYaw += 360F; } return doCentrifugal; } return false; } @SideOnly(Side.CLIENT) public void applyCentrifugalForce(EntityPlayerSP p) { int quadrant = 0; double xd = p.posX - this.spinCentreX; double zd = p.posZ - this.spinCentreZ; double accel = Math.sqrt(xd * xd + zd * zd) * this.angularVelocityRadians * this.angularVelocityRadians * 4D; if (xd < 0) { if (xd < -Math.abs(zd)) { quadrant = 2; } else { quadrant = zd < 0 ? 3 : 1; } } else if (xd > Math.abs(zd)) { quadrant = 0; } else { quadrant = zd < 0 ? 3 : 1; } switch (quadrant) { case 0: p.motionX += accel; break; case 1: p.motionZ += accel; break; case 2: p.motionX -= accel; break; case 3: default: p.motionZ -= accel; } } /** * Call this when player first login/transfer to this dimension * <p/> * TODO how can this code be called by other mods / plugins with teleports * (e.g. Bukkit)? See WorldUtil.teleportEntity() * * @param player */ public void sendPackets(EntityPlayerMP player) { List<Object> objList = new ArrayList<Object>(); objList.add(this.angularVelocityRadians); objList.add(this.thrustersFiring); if (player == null) { GalacticraftCore.packetPipeline.sendToDimension(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_SPIN, GCCoreUtil.getDimensionID(this.worldProvider), objList), GCCoreUtil.getDimensionID(this.worldProvider)); } else { GalacticraftCore.packetPipeline.sendTo(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_SPIN, GCCoreUtil.getDimensionID(player.worldObj), objList), player); } objList = new ArrayList<>(); objList.add(this.spinCentreX); objList.add(this.spinCentreZ); if (player == null) { GalacticraftCore.packetPipeline.sendToDimension(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_DATA, GCCoreUtil.getDimensionID(this.worldProvider), objList), GCCoreUtil.getDimensionID(this.worldProvider)); } else { GalacticraftCore.packetPipeline.sendTo(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_DATA, GCCoreUtil.getDimensionID(player.worldObj), objList), player); } objList = new ArrayList<>(); objList.add(this.ssBoundsMinX); objList.add(this.ssBoundsMaxX); objList.add(this.ssBoundsMinY); objList.add(this.ssBoundsMaxY); objList.add(this.ssBoundsMinZ); objList.add(this.ssBoundsMaxZ); if (player == null) { GalacticraftCore.packetPipeline.sendToDimension(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_BOX, GCCoreUtil.getDimensionID(this.worldProvider), objList), GCCoreUtil.getDimensionID(this.worldProvider)); } else { GalacticraftCore.packetPipeline.sendTo(new PacketSimple(PacketSimple.EnumSimplePacket.C_UPDATE_STATION_BOX, GCCoreUtil.getDimensionID(player.worldObj), objList), player); } } private void save() { if (this.savefile == null) { this.savefile = OrbitSpinSaveData.initWorldData(this.worldProvider.worldObj); this.dataNotLoaded = false; } else { this.writeToNBT(this.savefile.datacompound); this.savefile.markDirty(); } } public void readFromNBT(NBTTagCompound nbt) { this.doSpinning = true;//nbt.getBoolean("doSpinning"); this.angularVelocityRadians = nbt.getFloat("omegaRad"); this.skyAngularVelocity = nbt.getFloat("omegaSky"); this.angularVelocityTarget = nbt.getFloat("omegaTarget"); this.angularVelocityAccel = nbt.getFloat("omegaAcc"); NBTTagCompound oneBlock = (NBTTagCompound) nbt.getTag("oneBlock"); if (oneBlock != null) { // this.oneSSBlock = BlockVec3.readFromNBT(oneBlock); this.oneSSBlock = new BlockPos(oneBlock.getInteger("x"), oneBlock.getInteger("y"), oneBlock.getInteger("z")); } else { this.oneSSBlock = null; } //A lot of the data can be refreshed by refresh this.refresh(this.oneSSBlock, false); this.sendPackets(null); } public void writeToNBT(NBTTagCompound nbt) { nbt.setBoolean("doSpinning", this.doSpinning); nbt.setFloat("omegaRad", this.angularVelocityRadians); nbt.setFloat("omegaSky", this.skyAngularVelocity); nbt.setFloat("omegaTarget", this.angularVelocityTarget); nbt.setFloat("omegaAcc", this.angularVelocityAccel); if (this.oneSSBlock != null) { NBTTagCompound oneBlock = new NBTTagCompound(); oneBlock.setInteger("x", this.oneSSBlock.getX()); oneBlock.setInteger("y", this.oneSSBlock.getY()); oneBlock.setInteger("z", this.oneSSBlock.getZ()); nbt.setTag("oneBlock", oneBlock); } } }