private boolean assignInstance(UnityInstance instance) {
    Player player = PlayerService.getInstance().assignUnityAgent();
    if (player == null) {
      logger.debug("Unable to assign player for unity instance " + instance.status.name);
      return false;
    }

    UnityInstanceStatus status = instance.status;

    status.playerId = player.id;
    status.requestedState = UnityInstanceStatus.State.Running;
    status.state = UnityInstanceStatus.State.None;
    status.zone = ZoneService.getZone(status.name);
    PlayerService.getInstance().setZone(status.playerId, status.zone);
    GridService.getInstance().createForZone(status.zone.number);

    zoneToName.put(status.zone.number, status.name);
    instance.assigned = true;
    return true;
  }
  public static void releaseInstance(UnityInstance instance) {
    instance.status.requestedState = UnityInstanceStatus.State.None;
    instance.ask(instance.status);

    PlayerService.getInstance().releastUnityAgent(instance.status.playerId);
    GridService.getInstance().removeForZone(instance.status.zone.number);
    ZoneService.releaseZone(instance.status.name);
    instance.running = false;
    instance.assigned = false;
    zoneToName.remove(instance.status.zone.number);
    instances.remove(instance.status.name);
    logger.warn("Unity instance " + instance.status.name + " released");
  }
  private void doAttack(Attack attack) {
    boolean sendToObjectGrid = false;
    boolean sendToDefaultGrid = false;

    Zone zone = PlayerService.getInstance().getZone(playerId);
    if (attack.playerSkill == null) {
      logger.warning("Attack without player skill, ignoring");
      return;
    }

    if (Strings.isNullOrEmpty(attack.attackerCharacterId)) {
      logger.warning("Attack without attackerCharacterId, ignoring");
      return;
    }

    logger.warning(
        "Attack "
            + attack.attackerCharacterId
            + " "
            + attack.targetId
            + " skill "
            + attack.playerSkill.id);

    StatusEffectTarget statusEffectTarget = new StatusEffectTarget();
    VitalsHandler.ensure(playerId, zone.name);

    statusEffectTarget.attack = attack;

    statusEffectTarget.originCharacterId = attack.attackerCharacterId;
    statusEffectTarget.originEntityId = playerId;

    if (attack.playerSkill.category == PlayerSkill.Category.SingleTarget) {
      if (Strings.isNullOrEmpty(attack.targetId)) {
        logger.warning("SingleTarget with no targetId");
        return;
      } else {
        Character character = CharacterService.instance().find(attack.targetId);

        // No character = Object/vehicle/etc..
        if (character == null) {
          if (BuildObjectHandler.exists(attack.targetId)) {
            attack.targetType = Attack.TargetType.BuildObject;
          } else {
            attack.targetType = Attack.TargetType.Object;
          }
          statusEffectTarget.targetEntityId = attack.targetId;
          sendToObjectGrid = true;
        } else {
          attack.targetType = Attack.TargetType.Character;
          statusEffectTarget.targetEntityId = character.playerId;
          sendToDefaultGrid = true;
        }

        ensureTargetVitals(attack.targetType, statusEffectTarget.targetEntityId, zone.name);
      }

    } else if (attack.playerSkill.category == PlayerSkill.Category.Self) {
      attack.targetType = Attack.TargetType.Character;
      statusEffectTarget.targetEntityId = playerId;
      ensureTargetVitals(attack.targetType, statusEffectTarget.targetEntityId, zone.name);
      sendToDefaultGrid = true;

    } else if (attack.playerSkill.category == PlayerSkill.Category.Aoe
        || attack.playerSkill.category == PlayerSkill.Category.AoeDot) {
      if (attack.targetLocation == null) {
        logger.warning("Aoe without targetLocation");
        return;
      }

      statusEffectTarget.location = attack.targetLocation;
      sendToObjectGrid = true;
      sendToDefaultGrid = true;

    } else if (attack.playerSkill.category == PlayerSkill.Category.Pbaoe) {

      Grid grid = GridService.getInstance().getGrid(zone.name, "default");

      TrackData td = grid.get(playerId);
      if (td == null) {
        logger.warning("TrackData not found for " + playerId);
        return;
      }

      statusEffectTarget.location = new GmVector3();
      statusEffectTarget.location.xi = td.x;
      statusEffectTarget.location.yi = td.y;
      statusEffectTarget.location.zi = td.z;

      sendToObjectGrid = true;
      sendToDefaultGrid = true;
    } else {
      logger.warning("Invalid damage type");
      return;
    }

    if (sendToDefaultGrid) {
      StatusEffectManager.tell("default", zone.name, statusEffectTarget.clone(), getSelf());
    }

    if (sendToObjectGrid) {
      StatusEffectManager.tell("build_objects", zone.name, statusEffectTarget.clone(), getSelf());
    }

    sendAttack(attack, zone.name);
  }