@Override
  protected void setupCustomSkin() {
    super.setupCustomSkin();

    if (ClientProxyCore.capeMap.containsKey(this.getGameProfile().getName())) {
      this.galacticraftCape =
          GCEntityClientPlayerMP.getLocationCape2(this.getGameProfile().getName());
      this.galacticraftCapeImageData =
          GCEntityClientPlayerMP.getDownloadImage(
              this.galacticraftCape,
              GCEntityClientPlayerMP.getCapeURL(this.getGameProfile().getName()),
              null,
              null);
    }
  }
  public GuiJoinSpaceRace(GCEntityClientPlayerMP player) {
    this.thePlayer = player;

    SpaceRace race = SpaceRaceManager.getSpaceRaceFromID(player.spaceRaceInviteTeamID);

    if (race != null) {
      this.spaceRaceData = race;
    } else {
      List<String> playerList = new ArrayList<String>();
      playerList.add(player.getGameProfile().getName());
      this.spaceRaceData = new SpaceRace(playerList, "Unnamed Team", new FlagData(48, 32));
    }
  }
  @SideOnly(Side.CLIENT)
  @Override
  public void handleClientSide(EntityPlayer player) {
    GCEntityClientPlayerMP playerBaseClient = null;

    if (player instanceof GCEntityClientPlayerMP) {
      playerBaseClient = (GCEntityClientPlayerMP) player;
    } else {
      return;
    }

    switch (this.type) {
      case C_AIR_REMAINING:
        if (String.valueOf(this.data.get(2))
            .equals(
                String.valueOf(
                    FMLClientHandler.instance()
                        .getClient()
                        .thePlayer
                        .getGameProfile()
                        .getName()))) {
          TickHandlerClient.airRemaining = (Integer) this.data.get(0);
          TickHandlerClient.airRemaining2 = (Integer) this.data.get(1);
        }
        break;
      case C_UPDATE_DIMENSION_LIST:
        if (String.valueOf(this.data.get(0))
            .equals(FMLClientHandler.instance().getClient().thePlayer.getGameProfile().getName())) {
          final String[] destinations = ((String) this.data.get(1)).split("\\.");

          if (FMLClientHandler.instance().getClient().theWorld != null
              && !(FMLClientHandler.instance().getClient().currentScreen
                      instanceof GuiCelestialSelection
                  || FMLClientHandler.instance().getClient().currentScreen
                      instanceof GuiGalaxyMap)) {
            FMLClientHandler.instance().getClient().displayGuiScreen(new GuiCelestialSelection());
          }
        }
        break;
      case C_SPAWN_SPARK_PARTICLES:
        int x, y, z;
        x = (Integer) this.data.get(0);
        y = (Integer) this.data.get(1);
        z = (Integer) this.data.get(2);
        Minecraft mc = Minecraft.getMinecraft();

        for (int i = 0; i < 4; i++) {
          if (mc != null
              && mc.renderViewEntity != null
              && mc.effectRenderer != null
              && mc.theWorld != null) {
            final EntityFX fx =
                new EntityFXSparks(
                    mc.theWorld,
                    x - 0.15 + 0.5,
                    y + 1.2,
                    z + 0.15 + 0.5,
                    mc.theWorld.rand.nextDouble() / 20 - mc.theWorld.rand.nextDouble() / 20,
                    mc.theWorld.rand.nextDouble() / 20 - mc.theWorld.rand.nextDouble() / 20);

            if (fx != null) {
              mc.effectRenderer.addEffect(fx);
            }
          }
        }
        break;
      case C_UPDATE_GEAR_SLOT:
        int subtype = (Integer) this.data.get(2);
        EntityPlayer gearDataPlayer = null;
        MinecraftServer server = MinecraftServer.getServer();

        if (server != null) {
          gearDataPlayer =
              server.getConfigurationManager().getPlayerForUsername((String) this.data.get(0));
        } else {
          gearDataPlayer = player.worldObj.getPlayerEntityByName((String) this.data.get(0));
        }

        if (gearDataPlayer != null) {
          PlayerGearData gearData =
              ClientProxyCore.playerItemData.get(gearDataPlayer.getPersistentID());

          if (gearData == null) {
            gearData = new PlayerGearData(player);
          }

          EnumModelPacket type = EnumModelPacket.values()[(Integer) this.data.get(1)];

          switch (type) {
            case ADDMASK:
              gearData.setMask(0);
              break;
            case REMOVEMASK:
              gearData.setMask(-1);
              break;
            case ADDGEAR:
              gearData.setGear(0);
              break;
            case REMOVEGEAR:
              gearData.setGear(-1);
              break;
            case ADDLEFTGREENTANK:
              gearData.setLeftTank(0);
              break;
            case ADDLEFTORANGETANK:
              gearData.setLeftTank(1);
              break;
            case ADDLEFTREDTANK:
              gearData.setLeftTank(2);
              break;
            case ADDRIGHTGREENTANK:
              gearData.setRightTank(0);
              break;
            case ADDRIGHTORANGETANK:
              gearData.setRightTank(1);
              break;
            case ADDRIGHTREDTANK:
              gearData.setRightTank(2);
              break;
            case REMOVE_LEFT_TANK:
              gearData.setLeftTank(-1);
              break;
            case REMOVE_RIGHT_TANK:
              gearData.setRightTank(-1);
              break;
            case ADD_PARACHUTE:
              String name = "";

              if (subtype != -1) {
                name = ItemParaChute.names[subtype];
                gearData.setParachute(
                    new ResourceLocation(
                        GalacticraftCore.ASSET_DOMAIN,
                        "textures/model/parachute/" + name + ".png"));
              }
              break;
            case REMOVE_PARACHUTE:
              gearData.setParachute(null);
              break;
            case ADD_FREQUENCY_MODULE:
              gearData.setFrequencyModule(0);
              break;
            case REMOVE_FREQUENCY_MODULE:
              gearData.setFrequencyModule(-1);
              break;
            case ADD_THERMAL_HELMET:
              gearData.setThermalPadding(0, 0);
              break;
            case ADD_THERMAL_CHESTPLATE:
              gearData.setThermalPadding(1, 0);
              break;
            case ADD_THERMAL_LEGGINGS:
              gearData.setThermalPadding(2, 0);
              break;
            case ADD_THERMAL_BOOTS:
              gearData.setThermalPadding(3, 0);
              break;
            case REMOVE_THERMAL_HELMET:
              gearData.setThermalPadding(0, -1);
              break;
            case REMOVE_THERMAL_CHESTPLATE:
              gearData.setThermalPadding(1, -1);
              break;
            case REMOVE_THERMAL_LEGGINGS:
              gearData.setThermalPadding(2, -1);
              break;
            case REMOVE_THERMAL_BOOTS:
              gearData.setThermalPadding(3, -1);
              break;
            default:
              break;
          }

          ClientProxyCore.playerItemData.put(playerBaseClient.getPersistentID(), gearData);
        }

        break;
      case C_CLOSE_GUI:
        FMLClientHandler.instance().getClient().displayGuiScreen(null);
        break;
      case C_RESET_THIRD_PERSON:
        FMLClientHandler.instance().getClient().gameSettings.thirdPersonView =
            playerBaseClient.getThirdPersonView();
        break;
      case C_UPDATE_SPACESTATION_LIST:
        try {
          if (WorldUtil.registeredSpaceStations != null) {
            for (Integer registeredID : WorldUtil.registeredSpaceStations) {
              DimensionManager.unregisterDimension(registeredID);
            }
          }
          WorldUtil.registeredSpaceStations = new ArrayList<Integer>();

          if (this.data.size() > 0) {
            if (this.data.get(0) instanceof Integer) {
              for (Object o : this.data) {
                Integer dimID = (Integer) o;

                if (!WorldUtil.registeredSpaceStations.contains(dimID)) {
                  WorldUtil.registeredSpaceStations.add(dimID);
                  if (!DimensionManager.isDimensionRegistered(dimID)) {
                    DimensionManager.registerDimension(
                        dimID, ConfigManagerCore.idDimensionOverworldOrbit);
                  } else {
                    GCLog.severe(
                        "Dimension already registered on client: unable to register space station dimension "
                            + dimID);
                  }
                }
              }
            } else if (this.data.get(0) instanceof Integer[]) {
              for (Object o : (Integer[]) this.data.get(0)) {
                Integer dimID = (Integer) o;

                if (!WorldUtil.registeredSpaceStations.contains(dimID)) {
                  WorldUtil.registeredSpaceStations.add(dimID);
                  if (!DimensionManager.isDimensionRegistered(dimID)) {
                    DimensionManager.registerDimension(
                        dimID, ConfigManagerCore.idDimensionOverworldOrbit);
                  } else {
                    GCLog.severe(
                        "Dimension already registered on client: unable to register space station dimension "
                            + dimID);
                  }
                }
              }
            }
          }
          break;
        } catch (final Exception e) {
          e.printStackTrace();
        }
      case C_UPDATE_SPACESTATION_DATA:
        SpaceStationWorldData var4 =
            SpaceStationWorldData.getMPSpaceStationData(
                player.worldObj, (Integer) this.data.get(0), player);
        var4.readFromNBT((NBTTagCompound) this.data.get(1));
        break;
      case C_UPDATE_SPACESTATION_CLIENT_ID:
        ClientProxyCore.clientSpaceStationID = (Integer) this.data.get(0);
        break;
      case C_UPDATE_PLANETS_LIST:
        try {
          if (WorldUtil.registeredPlanets != null) {
            for (Integer registeredID : WorldUtil.registeredPlanets) {
              DimensionManager.unregisterDimension(registeredID);
            }
          }
          WorldUtil.registeredPlanets = new ArrayList<Integer>();

          if (this.data.size() > 0) {
            if (this.data.get(0) instanceof Integer) {
              for (Object o : this.data) {
                Integer dimID = (Integer) o;

                if (!WorldUtil.registeredPlanets.contains(dimID)) {
                  WorldUtil.registeredPlanets.add(dimID);
                  DimensionManager.registerDimension(dimID, dimID);
                }
              }
            } else if (this.data.get(0) instanceof Integer[]) {
              for (Object o : (Integer[]) this.data.get(0)) {
                Integer dimID = (Integer) o;

                if (!WorldUtil.registeredPlanets.contains(dimID)) {
                  WorldUtil.registeredPlanets.add(dimID);
                  DimensionManager.registerDimension(dimID, dimID);
                }
              }
            }
          }
          break;
        } catch (final Exception e) {
          e.printStackTrace();
        }
      case C_ADD_NEW_SCHEMATIC:
        final ISchematicPage page =
            SchematicRegistry.getMatchingRecipeForID((Integer) this.data.get(0));
        if (!playerBaseClient.unlockedSchematics.contains(page)) {
          playerBaseClient.unlockedSchematics.add(page);
        }
        break;
      case C_UPDATE_SCHEMATIC_LIST:
        for (Object o : this.data) {
          Integer schematicID = (Integer) o;

          if (schematicID != -2) {
            Collections.sort(playerBaseClient.unlockedSchematics);

            if (!playerBaseClient.unlockedSchematics.contains(
                SchematicRegistry.getMatchingRecipeForID(Integer.valueOf(schematicID)))) {
              playerBaseClient.unlockedSchematics.add(
                  SchematicRegistry.getMatchingRecipeForID(Integer.valueOf(schematicID)));
            }
          }
        }
        break;
      case C_PLAY_SOUND_BOSS_DEATH:
        player.playSound(GalacticraftCore.ASSET_PREFIX + "entity.bossdeath", 10.0F, 0.8F);
        break;
      case C_PLAY_SOUND_EXPLODE:
        player.playSound("random.explode", 10.0F, 0.7F);
        break;
      case C_PLAY_SOUND_BOSS_LAUGH:
        player.playSound(GalacticraftCore.ASSET_PREFIX + "entity.bosslaugh", 10.0F, 0.2F);
        break;
      case C_PLAY_SOUND_BOW:
        player.playSound("random.bow", 10.0F, 0.2F);
        break;
      case C_UPDATE_OXYGEN_VALIDITY:
        playerBaseClient.oxygenSetupValid = (Boolean) this.data.get(0);
        break;
      case C_OPEN_PARACHEST_GUI:
        switch ((Integer) this.data.get(1)) {
          case 0:
            if (player.ridingEntity instanceof EntityBuggy) {
              FMLClientHandler.instance()
                  .getClient()
                  .displayGuiScreen(
                      new GuiBuggy(
                          player.inventory,
                          (EntityBuggy) player.ridingEntity,
                          ((EntityBuggy) player.ridingEntity).getType()));
              player.openContainer.windowId = (Integer) this.data.get(0);
            }
            break;
          case 1:
            int entityID = (Integer) this.data.get(2);
            Entity entity = player.worldObj.getEntityByID(entityID);

            if (entity != null && entity instanceof IInventorySettable) {
              FMLClientHandler.instance()
                  .getClient()
                  .displayGuiScreen(
                      new GuiParaChest(player.inventory, (IInventorySettable) entity));
            }

            player.openContainer.windowId = (Integer) this.data.get(0);
            break;
        }
        break;
      case C_UPDATE_WIRE_BOUNDS:
        TileEntity tile =
            player.worldObj.getTileEntity(
                (Integer) this.data.get(0), (Integer) this.data.get(1), (Integer) this.data.get(2));

        if (tile instanceof TileEntityConductor) {
          ((TileEntityConductor) tile).adjacentConnections = null;
          player
              .worldObj
              .getBlock(tile.xCoord, tile.yCoord, tile.zCoord)
              .setBlockBoundsBasedOnState(player.worldObj, tile.xCoord, tile.yCoord, tile.zCoord);
        }
        break;
      case C_OPEN_SPACE_RACE_GUI:
        if (Minecraft.getMinecraft().currentScreen == null) {
          TickHandlerClient.spaceRaceGuiScheduled = false;
          player.openGui(
              GalacticraftCore.instance,
              GuiIdsCore.SPACE_RACE_START,
              player.worldObj,
              (int) player.posX,
              (int) player.posY,
              (int) player.posZ);
        } else {
          TickHandlerClient.spaceRaceGuiScheduled = true;
        }
        break;
      case C_UPDATE_SPACE_RACE_DATA:
        Integer teamID = (Integer) this.data.get(0);
        String teamName = (String) this.data.get(1);
        FlagData flagData = (FlagData) this.data.get(2);
        Vector3 teamColor = (Vector3) this.data.get(3);
        List<String> playerList = new ArrayList<String>();

        for (int i = 4; i < this.data.size(); i++) {
          String playerName = (String) this.data.get(i);
          ClientProxyCore.flagRequestsSent.remove(playerName);
          playerList.add(playerName);
        }

        SpaceRace race = new SpaceRace(playerList, teamName, flagData, teamColor);
        race.setSpaceRaceID(teamID);
        SpaceRaceManager.addSpaceRace(race);
        break;
      case C_OPEN_JOIN_RACE_GUI:
        playerBaseClient.spaceRaceInviteTeamID = (Integer) this.data.get(0);
        player.openGui(
            GalacticraftCore.instance,
            GuiIdsCore.SPACE_RACE_JOIN,
            player.worldObj,
            (int) player.posX,
            (int) player.posY,
            (int) player.posZ);
        break;
      case C_UPDATE_FOOTPRINT_LIST:
        ClientProxyCore.footprintRenderer.footprints.clear();
        for (int i = 0; i < this.data.size(); i++) {
          Footprint print = (Footprint) this.data.get(i);
          ClientProxyCore.footprintRenderer.addFootprint(print);
        }
        break;
      case C_UPDATE_STATION_SPIN:
        if (playerBaseClient.worldObj.provider instanceof WorldProviderOrbit) {
          ((WorldProviderOrbit) playerBaseClient.worldObj.provider)
              .setSpinRate((Float) this.data.get(0), (Boolean) this.data.get(1));
        }
        break;
      case C_UPDATE_STATION_DATA:
        if (playerBaseClient.worldObj.provider instanceof WorldProviderOrbit) {
          ((WorldProviderOrbit) playerBaseClient.worldObj.provider)
              .setSpinCentre((Double) this.data.get(0), (Double) this.data.get(1));
        }
        break;
      case C_UPDATE_STATION_BOX:
        if (playerBaseClient.worldObj.provider instanceof WorldProviderOrbit) {
          ((WorldProviderOrbit) playerBaseClient.worldObj.provider)
              .setSpinBox(
                  (Integer) this.data.get(0),
                  (Integer) this.data.get(1),
                  (Integer) this.data.get(2),
                  (Integer) this.data.get(3),
                  (Integer) this.data.get(4),
                  (Integer) this.data.get(5));
        }
        break;
      case C_UPDATE_THERMAL_LEVEL:
        playerBaseClient.thermalLevel = (Integer) this.data.get(0);
        break;
      case C_DISPLAY_ROCKET_CONTROLS:
        player.addChatMessage(
            new ChatComponentText(
                Keyboard.getKeyName(KeyHandlerClient.spaceKey.getKeyCode())
                    + "  - "
                    + GCCoreUtil.translate("gui.rocket.launch.name")));
        player.addChatMessage(
            new ChatComponentText(
                Keyboard.getKeyName(KeyHandlerClient.leftKey.getKeyCode())
                    + " / "
                    + Keyboard.getKeyName(KeyHandlerClient.rightKey.getKeyCode())
                    + "  - "
                    + GCCoreUtil.translate("gui.rocket.turn.name")));
        player.addChatMessage(
            new ChatComponentText(
                Keyboard.getKeyName(KeyHandlerClient.accelerateKey.getKeyCode())
                    + " / "
                    + Keyboard.getKeyName(KeyHandlerClient.decelerateKey.getKeyCode())
                    + "  - "
                    + GCCoreUtil.translate("gui.rocket.updown.name")));
        player.addChatMessage(
            new ChatComponentText(
                Keyboard.getKeyName(KeyHandlerClient.openFuelGui.getKeyCode())
                    + "       - "
                    + GCCoreUtil.translate("gui.rocket.inv.name")));
        break;
      default:
        break;
    }
  }
  @SideOnly(Side.CLIENT)
  public void spinUpdate(GCEntityClientPlayerMP p) {
    boolean freefall = true;
    if (p.boundingBox.maxX >= this.ssBoundsMinX
        && p.boundingBox.minX <= this.ssBoundsMaxX
        && p.boundingBox.maxY >= this.ssBoundsMinY
        && p.boundingBox.minY <= this.ssBoundsMaxY
        && p.boundingBox.maxZ >= this.ssBoundsMinZ
        && p.boundingBox.minZ <= this.ssBoundsMaxZ) {
      // Player is somewhere within the space station boundaries

      // This is an "on the ground" check
      int playerFeetOnY = (int) (p.boundingBox.minY - 0.001D);
      Block b =
          this.worldObj.getBlock(
              MathHelper.floor_double(p.posX), playerFeetOnY, MathHelper.floor_double(p.posX));
      double blockYmax = b.getBlockBoundsMaxY() + playerFeetOnY;
      if (b != Blocks.air && p.boundingBox.minY - blockYmax < 0.001D) {
        freefall = false;
      } else {
        // Check if the player's bounding box is in the same block coordinates as any non-vacuum
        // block (including torches etc)
        // If so, it's assumed the player has something close enough to grab onto, so is not in
        // freefall
        // Note: breatheable air here means the player is definitely not in freefall
        int xmx = MathHelper.floor_double(p.boundingBox.maxX);
        int ym = MathHelper.floor_double(p.boundingBox.minY);
        int yy = MathHelper.floor_double(p.boundingBox.maxY);
        int zm = MathHelper.floor_double(p.boundingBox.minZ);
        int zz = MathHelper.floor_double(p.boundingBox.maxZ);
        BLOCKCHECK:
        for (int x = MathHelper.floor_double(p.boundingBox.minX); x <= xmx; x++) {
          for (int y = ym; y <= yy; y++) {
            for (int z = zm; z <= zz; z++) {
              if (this.worldObj.getBlock(x, y, z) != Blocks.air) {
                freefall = false;
                break BLOCKCHECK;
              }
            }
          }
        }
      }

      /*
      if (freefall)
      {
      	//If that check didn't produce a result, see if the player is inside the walls
      	//TODO: could apply special weightless movement here like Coriolis force - the player is inside the walls,  not touching them, and in a vacuum
      	int quadrant = 0;
      	double xd = p.posX - this.spinCentreX;
      	double zd = p.posZ - this.spinCentreZ;
      	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;

      	int ymin = MathHelper.floor_double(p.boundingBox.minY)-1;
      	int ymax = MathHelper.floor_double(p.boundingBox.maxY);
      	int xmin, xmax, zmin, zmax;

      	switch (quadrant)
      	{
      	case 0:
      		xmin = MathHelper.floor_double(p.boundingBox.maxX);
      		xmax = this.ssBoundsMaxX - 1;
      		zmin = MathHelper.floor_double(p.boundingBox.minZ)-1;
      		zmax = MathHelper.floor_double(p.boundingBox.maxZ)+1;
      		break;
      	case 1:
      		xmin = MathHelper.floor_double(p.boundingBox.minX)-1;
      		xmax = MathHelper.floor_double(p.boundingBox.maxX)+1;
      		zmin = MathHelper.floor_double(p.boundingBox.maxZ);
      		zmax = this.ssBoundsMaxZ - 1;
      		break;
      	case 2:
      		zmin = MathHelper.floor_double(p.boundingBox.minZ)-1;
      		zmax = MathHelper.floor_double(p.boundingBox.maxZ)+1;
      		xmin = this.ssBoundsMinX;
      		xmax = MathHelper.floor_double(p.boundingBox.minX);
      		break;
      	case 3:
      	default:
      		xmin = MathHelper.floor_double(p.boundingBox.minX)-1;
      		xmax = MathHelper.floor_double(p.boundingBox.maxX)+1;
      		zmin = this.ssBoundsMinZ;
      		zmax = MathHelper.floor_double(p.boundingBox.minZ);
      		break;
      	}

      	//This block search could cost a lot of CPU (but client side) - maybe optimise later
      	BLOCKCHECK0:
      	for(int x = xmin; x <= xmax; x++)
      		for (int z = zmin; z <= zmax; z++)
      			for (int y = ymin; y <= ymax; y++)
      				if (this.worldObj.getBlock(x, y, z) != Blocks.air)
      				{
      					freefall = false;
      					break BLOCKCHECK0;
      				}
      }*/
    }

    boolean doGravity = true;
    if (freefall) {
      doGravity = false;
      // Do spinning
      if (this.doSpinning && this.angularVelocityRadians != 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?
        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.141592536F / 2 : -3.141592536F / 2;
        } else {
          angle = (float) Math.atan(zz / xx);
        }
        if (xx < 0D) {
          angle += 3.141592536F;
        }
        angle += this.angularVelocityRadians / 3F;
        arc = arc * this.angularVelocityRadians;
        double offsetX = -arc * MathHelper.sin(angle);
        double offsetZ = arc * MathHelper.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 (this.worldObj.getCollidingBoundingBoxes(p, p.boundingBox).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 =
                this.worldObj.getCollidingBoundingBoxes(
                    p, p.boundingBox.addCoord(offsetX, 0.0D, offsetZ));
            collisions = list.size();
            if (collisions > 0) {
              if (!doGravity) {
                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;
              }
              doGravity = true;
            }
          } while (collisions > 0);

          p.posX += offsetX;
          p.posZ += offsetZ;
          p.boundingBox.offset(offsetX, 0.0D, offsetZ);
        }

        p.rotationYaw += this.skyAngularVelocity;
        while (p.rotationYaw > 360F) {
          p.rotationYaw -= 360F;
        }

        /*				//Just started freefall - give some impulse
        if (!p.inFreefall && p.inFreefallFirstCheck)
        {
        	p.motionX += offsetX * 0.91F;
        	p.motionZ += offsetZ * 0.91F;
        }*/
      }

      // Reverse effects of deceleration
      p.motionX /= 0.91F;
      p.motionZ /= 0.91F;
      p.motionY /= 0.9800000190734863D;

      // Do freefall motion
      if (!p.capabilities.isCreativeMode) {
        double dx = p.motionX - this.pPrevMotionX;
        double dy = p.motionY - this.pPrevMotionY;
        double dz = p.motionZ - this.pPrevMotionZ;
        double dyaw = p.rotationYaw - p.prevRotationYaw;
        p.rotationYaw -= dyaw * 0.8D;

        // if (p.capabilities.isFlying)
        /// Undo whatever vanilla tried to do to our y motion
        p.motionY -= dy;
        p.motionX -= dx;
        p.motionZ -= dz;

        if (p.movementInput.moveForward != 0) {
          p.motionX -=
              p.movementInput.moveForward * MathHelper.sin(p.rotationYaw / 57.29578F) / 400F;
          p.motionZ +=
              p.movementInput.moveForward * MathHelper.cos(p.rotationYaw / 57.29578F) / 400F;
        }

        if (p.movementInput.sneak) {
          p.motionY -= 0.0015D;
        }

        if (p.movementInput.jump) {
          p.motionY += 0.0015D;
        }

        if (p.motionX > 0.7F) {
          p.motionX = 0.7F;
        }
        if (p.motionX < -0.7F) {
          p.motionX = -0.7F;
        }
        if (p.motionY > 0.7F) {
          p.motionY = 0.7F;
        }
        if (p.motionY < -0.7F) {
          p.motionY = -0.7F;
        }
        if (p.motionZ > 0.7F) {
          p.motionZ = 0.7F;
        }
        if (p.motionZ < -0.7F) {
          p.motionZ = -0.7F;
        }
      } else {
        if (p.motionX > 1.2F) {
          p.motionX = 1.2F;
        }
        if (p.motionX < -1.2F) {
          p.motionX = -1.2F;
        }
        if (p.motionZ > 1.2F) {
          p.motionZ = 1.2F;
        }
        if (p.motionZ < -1.2F) {
          p.motionZ = -1.2F;
        }
      }
      // TODO: Think about endless drift?
      // Player may run out of oxygen - that will kill the player eventually if can't get back to SS
      // Maybe player needs a 'suicide' button if floating helplessly in space and with no tether
      // Could auto-kill + respawn the player if floats too far away (config option whether to lose
      // items or not)
      // But we want players to be able to enjoy the view of the spinning space station from the
      // outside
      // Arm and leg movements could start tumbling the player?
    } else {
      if (p.movementInput.jump) {
        if (p.onGround) {
          p.motionY += 0.15D;
        } else {
          p.motionY += 0.01D;
        }
      } else if (p.movementInput.sneak) {
        if (!p.onGround) {
          p.motionY -= 0.01D;
        }
      }
    }

    // Artificial gravity
    if (doGravity) {
      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;
      }
    }

    p.inFreefall = freefall;
    p.inFreefallFirstCheck = true;
    this.pPrevMotionX = p.motionX;
    this.pPrevMotionY = p.motionY;
    this.pPrevMotionZ = p.motionZ;
  }